diff --git a/@file_array/private/file2mat.c b/@file_array/private/file2mat.c index f964859d..bf3c7826 100644 --- a/@file_array/private/file2mat.c +++ b/@file_array/private/file2mat.c @@ -1,5 +1,5 @@ /* - * $Id: file2mat.c 6988 2017-01-16 12:38:29Z guillaume $ + * $Id: file2mat.c 7353 2018-06-19 10:39:55Z guillaume $ * John Ashburner */ @@ -26,8 +26,10 @@ Memory mapping is used by this module. For more information on this, see: HANDLE hFile, hMapping; typedef char *caddr_t; #if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64 +#ifdef _MSC_VER_ #define stat _stati64 #define fstat _fstati64 +#endif #define open _open #define close _close #if defined _MSC_VER @@ -625,13 +627,13 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) if (map.dtype->channels == 1) { plhs[0] = mxCreateNumericArray(ndim,odim,map.dtype->clss,mxREAL); -#ifdef SPM_WIN32 +#ifdef _MSC_VER_ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa366801.aspx */ __try { #endif map.dtype->func(ndim-1, idim, iptr, idat, odim, mxGetData(plhs[0])); -#ifdef SPM_WIN32 +#ifdef _MSC_VER_ } __except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) diff --git a/@file_array/private/file2mat.mexw32 b/@file_array/private/file2mat.mexw32 index 1ea74fbc..a56fcc60 100755 Binary files a/@file_array/private/file2mat.mexw32 and b/@file_array/private/file2mat.mexw32 differ diff --git a/@file_array/private/file2mat.mexw64 b/@file_array/private/file2mat.mexw64 index de4f6fbf..1130fc8e 100755 Binary files a/@file_array/private/file2mat.mexw64 and b/@file_array/private/file2mat.mexw64 differ diff --git a/@file_array/private/init.mexw32 b/@file_array/private/init.mexw32 index abde5e91..8ab70d7e 100755 Binary files a/@file_array/private/init.mexw32 and b/@file_array/private/init.mexw32 differ diff --git a/@file_array/private/init.mexw64 b/@file_array/private/init.mexw64 index 17301452..66b5ee1b 100755 Binary files a/@file_array/private/init.mexw64 and b/@file_array/private/init.mexw64 differ diff --git a/@file_array/private/mat2file.mexw32 b/@file_array/private/mat2file.mexw32 index 48b129f6..81a15ee9 100755 Binary files a/@file_array/private/mat2file.mexw32 and b/@file_array/private/mat2file.mexw32 differ diff --git a/@file_array/private/mat2file.mexw64 b/@file_array/private/mat2file.mexw64 index 787f6557..486c12e5 100755 Binary files a/@file_array/private/mat2file.mexw64 and b/@file_array/private/mat2file.mexw64 differ diff --git a/@file_array/size.m b/@file_array/size.m index 5b1c7473..660a4c1c 100644 --- a/@file_array/size.m +++ b/@file_array/size.m @@ -4,7 +4,7 @@ % Copyright (C) 2005-2017-2012 Wellcome Trust Centre for Neuroimaging % -% $Id: size.m 7147 2017-08-03 14:07:01Z spm $ +% $Id: size.m 7440 2018-10-10 17:28:26Z john $ sa = struct(a); @@ -35,9 +35,8 @@ d = d(1:lim); if nargin > 1 - if varargin{1} <= length(d) - d = d(varargin{1}); - else - d = 1; - end + d_tmp = d; + d = ones(size(varargin{1})); + msk = varargin{1}<=length(d_tmp); + d(msk) = d_tmp(varargin{1}(msk)); end diff --git a/@file_array/subsref.m b/@file_array/subsref.m index 46a39245..277107a2 100644 --- a/@file_array/subsref.m +++ b/@file_array/subsref.m @@ -5,7 +5,7 @@ % Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging % -% $Id: subsref.m 7209 2017-11-10 15:33:10Z guillaume $ +% $Id: subsref.m 7439 2018-10-10 17:19:31Z john $ if isempty(subs), return; end diff --git a/@gifti/Contents.m b/@gifti/Contents.m index d13b3f30..62edd7e2 100644 --- a/@gifti/Contents.m +++ b/@gifti/Contents.m @@ -6,23 +6,27 @@ % http://nifti.nimh.nih.gov/ % % This MATLAB class is part of SPM: -% http://www.fil.ion.ucl.ac.uk/spm/ +% https://www.fil.ion.ucl.ac.uk/spm/ % % It relies on external libraries: % Base64, by Peter J. Acklam: % http://home.online.no/~pjacklam/ % miniz, by Rich Geldreich: -% http://code.google.com/p/miniz/ -% XMLTree, by Guillaume Flandin: -% http://www.artefact.tk/software/matlab/xml/ +% https://github.com/richgel999/miniz +% dzip, by Michael Kleder: +% https://www.mathworks.com/matlabcentral/fileexchange/8899 +% XMLTree, mVTK and JSONio, by Guillaume Flandin: +% https://www.artefact.tk/software/matlab/xml/ +% https://www.artefact.tk/software/matlab/mvtk/ +% https://www.artefact.tk/software/matlab/jsonio/ %__________________________________________________________________________ -% Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2008-2018 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: Contents.m 6404 2015-04-13 14:29:53Z guillaume $ +% $Id: Contents.m 7379 2018-07-25 09:11:24Z guillaume $ -% GIfTI file format for MATLAB (The Mathworks, Inc.). -% Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging +% GIfTI library for MATLAB +% Copyright (C) 2008-2018 Wellcome Trust Centre for Neuroimaging % % This program is free software; you can redistribute it and/or % modify it under the terms of the GNU General Public License @@ -36,4 +40,4 @@ % % You should have received a copy of the GNU General Public License % along with this program; if not, write to the Free Software -% Foundation Inc, 59 Temple Pl. - Suite 330, Boston, MA 02111-1307, USA. \ No newline at end of file +% Foundation Inc, 59 Temple Pl. - Suite 330, Boston, MA 02111-1307, USA. diff --git a/@gifti/export.m b/@gifti/export.m index 961e7b77..9d7ebf74 100644 --- a/@gifti/export.m +++ b/@gifti/export.m @@ -8,12 +8,17 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: export.m 6401 2015-04-09 17:21:33Z guillaume $ - -if numel(this) > 1, warning('Only handle scalar objects yet.'); end +% $Id: export.m 7383 2018-07-31 10:53:37Z guillaume $ if nargin <= 1, target = 'MATLAB'; end +if numel(this) > 1 + for i=1:numel(this) + s(i) = export(this(i),target); + end + return; +end + switch lower(target) case 'matlab' s = struct(this); diff --git a/@gifti/gifti.m b/@gifti/gifti.m index 2dcf6135..8cd37ebe 100644 --- a/@gifti/gifti.m +++ b/@gifti/gifti.m @@ -8,7 +8,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: gifti.m 7037 2017-03-15 11:45:13Z guillaume $ +% $Id: gifti.m 7471 2018-11-02 11:14:39Z guillaume $ switch nargin @@ -41,7 +41,7 @@ elseif ishandle(varargin{1}) this = struct('vertices',get(varargin{1},'Vertices'), ... 'faces', get(varargin{1},'Faces')); - if ~isempty(get(varargin{1},'FaceVertexCData')); + if ~isempty(get(varargin{1},'FaceVertexCData')) this.cdata = get(varargin{1},'FaceVertexCData'); end this = gifti(this); @@ -76,7 +76,9 @@ catch error('[GIFTI] Loading of file %s failed.', varargin{1}); end - elseif strcmpi(e,'.asc') || strcmpi(e,'.srf') + elseif ismember(lower(e),{'.asc','.srf','.mgh','.mgz','.pial',... + '.white','.inflated','.nofix','.orig','.smoothwm',... + '.sphere','.reg','.surf','.curv','.area','.sulc'}) this = read_freesurfer_file(varargin{1}); this = gifti(this); elseif strcmpi(e,'.vtk') @@ -85,6 +87,15 @@ elseif strcmpi(e,'.obj') this = obj_read(varargin{1}); this = gifti(this); + elseif strcmpi(e,'.ply') + this = ply_read(varargin{1}); + this = gifti(this); + elseif strcmpi(e,'.stl') + this = stl_read(varargin{1}); + this = gifti(this); + elseif strcmpi(e,'.mz3') + this = mz3_read(varargin{1}); + this = gifti(this); else this = read_gifti_file(varargin{1},giftistruct); this = class(this,'gifti'); diff --git a/@gifti/plot.m b/@gifti/plot.m index 76ae4e5e..fe7ef7c0 100644 --- a/@gifti/plot.m +++ b/@gifti/plot.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: plot.m 5888 2014-02-19 19:54:12Z guillaume $ +% $Id: plot.m 7381 2018-07-25 10:27:54Z guillaume $ % if ishandle(varargin{1}) % h = figure(varargin{1}); @@ -14,9 +14,9 @@ % %axis off; % %camlight; % %camlight(-80,-10); -% %lighting phong; +% %lighting gouraud; % end -% cameramenu; +% cameratoolbar; cdata = []; @@ -55,12 +55,10 @@ set(hp,'FaceVertexCData',cdata(:,indc), 'FaceColor','interp') end -axes(ax); -camlight; -camlight(-80,-10); -lighting phong; -axes(ax); -cameramenu; +camlight(ax); +camlight(ax,-80,-10); +lighting(ax,'gouraud'); +cameratoolbar(h); if nargout varargout{1} = hp; diff --git a/@gifti/private/getdict.m b/@gifti/private/getdict.m index 3d457b13..45d90afc 100644 --- a/@gifti/private/getdict.m +++ b/@gifti/private/getdict.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: getdict.m 4505 2011-09-30 11:45:58Z guillaume $ +% $Id: getdict.m 7379 2018-07-25 09:11:24Z guillaume $ persistent dict; if ~isempty(dict) @@ -13,8 +13,12 @@ end table = {... - 'NIFTI_TYPE_UINT8', 'uint8', '%d', @uint8, 'uint8' - 'NIFTI_TYPE_INT32', 'int32', '%d', @int32, 'int32' + 'NIFTI_TYPE_UINT8', 'uint8', '%d', @uint8, 'uint8' + 'NIFTI_TYPE_INT8', 'int8', '%d', @int8, 'int8' + 'NIFTI_TYPE_UINT16', 'uint16', '%d', @uint16, 'uint16' + 'NIFTI_TYPE_INT16', 'int16', '%d', @int16, 'int16' + 'NIFTI_TYPE_UINT32', 'uint32', '%d', @uint32, 'uint32' + 'NIFTI_TYPE_INT32', 'int32', '%d', @int32, 'int32' 'NIFTI_TYPE_FLOAT32', 'float32', '%f', @single, 'single' 'NIFTI_TYPE_FLOAT64', 'float64', '%f', @double, 'double'}; diff --git a/@gifti/private/miniz.c b/@gifti/private/miniz.c index ae4058cc..ff30323a 100644 --- a/@gifti/private/miniz.c +++ b/@gifti/private/miniz.c @@ -1,4 +1,30 @@ -/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +/* miniz.c 2.0.7 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt @@ -6,51 +32,6 @@ Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). - * Change History - 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug - would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() - (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. - Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - - Merged MZ_FORCEINLINE fix from hdeanclark - - Fix include before config #ifdef, thanks emil.brink - - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can - set it to 1 for real-time compression). - - Merged in some compiler fixes from paulharris's github repro. - - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch - 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). - 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly - "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). - 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. - level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. - 5/28/11 v1.11 - Added statement from unlicense.org - 5/27/11 v1.10 - Substantial compressor optimizations: - - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - - Refactored the compression code for better readability and maintainability. - - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large - drop in throughput on some files). - 5/15/11 v1.09 - Initial stable release. - * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or @@ -155,317 +136,369 @@ uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ +#pragma once + + -#ifndef MINIZ_HEADER_INCLUDED -#define MINIZ_HEADER_INCLUDED -#include -// Defines to completely disable specific portions of miniz.c: -// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ -// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. -//#define MINIZ_NO_STDIO +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ -// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or -// get/set file times, and the C run-time funcs that get/set times won't be called. -// The current downside is the times written to your archives will be from 1979. -//#define MINIZ_NO_TIME +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ -// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_APIS +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ -// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_WRITING_APIS +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ -// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. -//#define MINIZ_NO_ZLIB_APIS +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. -//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ -// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. -// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc -// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user -// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. -//#define MINIZ_NO_MALLOC +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) - // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux - #define MINIZ_NO_TIME +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME #endif +#include + #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) - #include +#include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) -// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ #define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 #endif -#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU -// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 #endif #if MINIZ_X86_OR_X64_CPU -// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) -// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ #define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 #endif #ifdef __cplusplus extern "C" { #endif -// ------------------- zlib-style API Definitions. +/* ------------------- zlib-style API Definitions. */ -// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ typedef unsigned long mz_ulong; -// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ void mz_free(void *p); #define MZ_ADLER32_INIT (1) -// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) -// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); -// Compression strategies. -enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; -// Method +/* Method */ #define MZ_DEFLATED 8 -#ifndef MINIZ_NO_ZLIB_APIS - -// Heap allocation callbacks. -// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); -#define MZ_VERSION "9.1.15" -#define MZ_VERNUM 0x91F0 -#define MZ_VER_MAJOR 9 -#define MZ_VER_MINOR 1 -#define MZ_VER_REVISION 15 -#define MZ_VER_SUBREVISION 0 +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "10.0.2" +#define MZ_VERNUM 0xA020 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 0 +#define MZ_VER_REVISION 2 +#define MZ_VER_SUBREVISION 0 -// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). -enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; +#ifndef MINIZ_NO_ZLIB_APIS -// Return status codes. MZ_PARAM_ERROR is non-standard. -enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; -// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. -enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; -// Window bits +/* Window bits */ #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; -// Compression/decompression stream struct. +/* Compression/decompression stream struct. */ typedef struct mz_stream_s { - const unsigned char *next_in; // pointer to next byte to read - unsigned int avail_in; // number of bytes available at next_in - mz_ulong total_in; // total number of bytes consumed so far + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ - unsigned char *next_out; // pointer to next byte to write - unsigned int avail_out; // number of bytes that can be written to next_out - mz_ulong total_out; // total number of bytes produced so far + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ - char *msg; // error msg (unused) - struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ - mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) - mz_free_func zfree; // optional heap free function (defaults to free) - void *opaque; // heap alloc function user pointer + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ - int data_type; // data_type (unused) - mz_ulong adler; // adler32 of the source or uncompressed data - mz_ulong reserved; // not used + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ } mz_stream; typedef mz_stream *mz_streamp; -// Returns the version string of miniz.c. +/* Returns the version string of miniz.c. */ const char *mz_version(void); -// mz_deflateInit() initializes a compressor with default options: -// Parameters: -// pStream must point to an initialized mz_stream struct. -// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. -// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. -// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if the input parameters are bogus. -// MZ_MEM_ERROR on out of memory. +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ int mz_deflateInit(mz_streamp pStream, int level); -// mz_deflateInit2() is like mz_deflate(), except with more control: -// Additional parameters: -// method must be MZ_DEFLATED -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) -// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); -// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ int mz_deflateReset(mz_streamp pStream); -// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. -// Return values: -// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). -// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ int mz_deflate(mz_streamp pStream, int flush); -// mz_deflateEnd() deinitializes a compressor: -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ int mz_deflateEnd(mz_streamp pStream); -// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); -// Single-call compression functions mz_compress() and mz_compress2(): -// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); -// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ mz_ulong mz_compressBound(mz_ulong source_len); -// Initializes a decompressor. +/* Initializes a decompressor. */ int mz_inflateInit(mz_streamp pStream); -// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ int mz_inflateInit2(mz_streamp pStream, int window_bits); -// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. -// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). -// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. -// Return values: -// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. -// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_DATA_ERROR if the deflate stream is invalid. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again -// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ int mz_inflate(mz_streamp pStream, int flush); -// Deinitializes a decompressor. +/* Deinitializes a decompressor. */ int mz_inflateEnd(mz_streamp pStream); -// Single-call decompression. -// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); -// Returns a string description of the specified error code, or NULL if the error code is invalid. +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ const char *mz_error(int err); -// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES - typedef unsigned char Byte; - typedef unsigned int uInt; - typedef mz_ulong uLong; - typedef Byte Bytef; - typedef uInt uIntf; - typedef char charf; - typedef int intf; - typedef void *voidpf; - typedef uLong uLongf; - typedef void *voidp; - typedef void *const voidpc; - #define Z_NULL 0 - #define Z_NO_FLUSH MZ_NO_FLUSH - #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH - #define Z_SYNC_FLUSH MZ_SYNC_FLUSH - #define Z_FULL_FLUSH MZ_FULL_FLUSH - #define Z_FINISH MZ_FINISH - #define Z_BLOCK MZ_BLOCK - #define Z_OK MZ_OK - #define Z_STREAM_END MZ_STREAM_END - #define Z_NEED_DICT MZ_NEED_DICT - #define Z_ERRNO MZ_ERRNO - #define Z_STREAM_ERROR MZ_STREAM_ERROR - #define Z_DATA_ERROR MZ_DATA_ERROR - #define Z_MEM_ERROR MZ_MEM_ERROR - #define Z_BUF_ERROR MZ_BUF_ERROR - #define Z_VERSION_ERROR MZ_VERSION_ERROR - #define Z_PARAM_ERROR MZ_PARAM_ERROR - #define Z_NO_COMPRESSION MZ_NO_COMPRESSION - #define Z_BEST_SPEED MZ_BEST_SPEED - #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION - #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION - #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY - #define Z_FILTERED MZ_FILTERED - #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY - #define Z_RLE MZ_RLE - #define Z_FIXED MZ_FIXED - #define Z_DEFLATED MZ_DEFLATED - #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS - #define alloc_func mz_alloc_func - #define free_func mz_free_func - #define internal_state mz_internal_state - #define z_stream mz_stream - #define deflateInit mz_deflateInit - #define deflateInit2 mz_deflateInit2 - #define deflateReset mz_deflateReset - #define deflate mz_deflate - #define deflateEnd mz_deflateEnd - #define deflateBound mz_deflateBound - #define compress mz_compress - #define compress2 mz_compress2 - #define compressBound mz_compressBound - #define inflateInit mz_inflateInit - #define inflateInit2 mz_inflateInit2 - #define inflate mz_inflate - #define inflateEnd mz_inflateEnd - #define uncompress mz_uncompress - #define crc32 mz_crc32 - #define adler32 mz_adler32 - #define MAX_WBITS 15 - #define MAX_MEM_LEVEL 9 - #define zError mz_error - #define ZLIB_VERSION MZ_VERSION - #define ZLIB_VERNUM MZ_VERNUM - #define ZLIB_VER_MAJOR MZ_VER_MAJOR - #define ZLIB_VER_MINOR MZ_VER_MINOR - #define ZLIB_VER_REVISION MZ_VER_REVISION - #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION - #define zlibVersion mz_version - #define zlib_version mz_version() -#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES - -#endif // MINIZ_NO_ZLIB_APIS - -// ------------------- Types and macros +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif +#pragma once +#include +#include +#include +/* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; @@ -478,4439 +511,8380 @@ typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) -// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ #ifdef _MSC_VER - #define MZ_MACRO_END while (0, 0) +#define MZ_MACRO_END while (0, 0) #else - #define MZ_MACRO_END while (0) +#define MZ_MACRO_END while (0) #endif -// ------------------- ZIP archive reading/writing - -#ifndef MINIZ_NO_ARCHIVE_APIS - -enum -{ - MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, - MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, - MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 -}; +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ -typedef struct +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag { - mz_uint32 m_file_index; - mz_uint32 m_central_dir_ofs; - mz_uint16 m_version_made_by; - mz_uint16 m_version_needed; - mz_uint16 m_bit_flag; - mz_uint16 m_method; -#ifndef MINIZ_NO_TIME - time_t m_time; + int m_dummy; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t #endif - mz_uint32 m_crc32; - mz_uint64 m_comp_size; - mz_uint64 m_uncomp_size; - mz_uint16 m_internal_attr; - mz_uint32 m_external_attr; - mz_uint64 m_local_header_ofs; - mz_uint32 m_comment_size; - char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; - char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; -} mz_zip_archive_file_stat; - -typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); -typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); -struct mz_zip_internal_state_tag; -typedef struct mz_zip_internal_state_tag mz_zip_internal_state; - -typedef enum -{ - MZ_ZIP_MODE_INVALID = 0, - MZ_ZIP_MODE_READING = 1, - MZ_ZIP_MODE_WRITING = 2, - MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 -} mz_zip_mode; - -typedef struct mz_zip_archive_tag -{ - mz_uint64 m_archive_size; - mz_uint64 m_central_directory_file_ofs; - mz_uint m_total_files; - mz_zip_mode m_zip_mode; - - mz_uint m_file_offset_alignment; - - mz_alloc_func m_pAlloc; - mz_free_func m_pFree; - mz_realloc_func m_pRealloc; - void *m_pAlloc_opaque; - - mz_file_read_func m_pRead; - mz_file_write_func m_pWrite; - void *m_pIO_opaque; +#define MZ_ASSERT(x) assert(x) - mz_zip_internal_state *m_pState; +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif -} mz_zip_archive; +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) -typedef enum -{ - MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, - MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, - MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, - MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 -} mz_zip_flags; +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif -// ZIP archive reading +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) -// Inits a ZIP archive reader. -// These functions read and validate the archive's central directory. -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#ifdef __cplusplus +extern "C" { #endif -// Returns the total number of files in the archive. -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); +extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern void miniz_def_free_func(void *opaque, void *address); +extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); -// Returns detailed information about an archive file entry. -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) -// Determines if an archive file entry is a directory entry. -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); +#ifdef __cplusplus +} +#endif +#pragma once -// Retrieves the filename of an archive file entry. -// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); -// Attempts to locates a file in the archive's central directory. -// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH -// Returns -1 if the file cannot be found. -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ -// Extracts a archive file to a memory buffer using no memory allocation. -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 -// Extracts a archive file to a memory buffer. -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; -// Extracts a archive file to a dynamically allocated heap buffer. -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; -// Extracts a archive file using a callback function to output the file's data. -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); -#ifndef MINIZ_NO_STDIO -// Extracts a archive file to a disk file and sets its last accessed and modified times. -// This function only extracts files, not archive directory records. -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); -#endif +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); -// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. -mz_bool mz_zip_reader_end(mz_zip_archive *pZip); +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); -// ZIP archive writing +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); -// Inits a ZIP archive writer. -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; #endif -// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. -// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. -// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). -// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. -// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before -// the archive is finalized the file's central directory will be hosed. -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); - -// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. -// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; -#ifndef MINIZ_NO_STDIO -// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); -#endif +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; -// Adds a file to an archive by fully cloning the data from another archive. -// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; -// Finalizes the archive by writing the central directory records followed by the end of central directory record. -// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). -// An archive must be manually finalized by calling this function for it to be valid. -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); -// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. -// Note for the archive to be valid, it must have been finalized before ending. -mz_bool mz_zip_writer_end(mz_zip_archive *pZip); +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); -// Misc. high-level helper functions: +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); -// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); -// Reads a single file from an archive into a heap block. -// Returns NULL on failure. -void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc(); +void tdefl_compressor_free(tdefl_compressor *pComp); -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS +#ifdef __cplusplus +} +#endif +#pragma once -// ------------------- Low-level Decompression API Definitions +/* ------------------- Low-level Decompression API Definitions */ -// Decompression flags used by tinfl_decompress(). -// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. -// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. -// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). -// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ enum { - TINFL_FLAG_PARSE_ZLIB_HEADER = 1, - TINFL_FLAG_HAS_MORE_INPUT = 2, - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, - TINFL_FLAG_COMPUTE_ADLER32 = 8 + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 }; -// High level decompression functions: -// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. -// On return: -// Function returns a pointer to the decompressed data, or NULL on failure. -// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must call mz_free() on the returned block when it's no longer needed. +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); -// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. -// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); -// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. -// Returns 1 on success or 0 on failure. -typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); -struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; -// Max size of LZ dictionary. +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ + +tinfl_decompressor *tinfl_decompressor_alloc(); +void tinfl_decompressor_free(tinfl_decompressor *pDecomp); + +/* Max size of LZ dictionary. */ #define TINFL_LZ_DICT_SIZE 32768 -// Return status. -typedef enum -{ - TINFL_STATUS_BAD_PARAM = -3, - TINFL_STATUS_ADLER32_MISMATCH = -2, - TINFL_STATUS_FAILED = -1, - TINFL_STATUS_DONE = 0, - TINFL_STATUS_NEEDS_MORE_INPUT = 1, - TINFL_STATUS_HAS_MORE_OUTPUT = 2 +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; -// Initializes the decompressor to its initial state. -#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 -// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. -// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); -// Internal/private bits follow. +/* Internal/private bits follow. */ enum { - TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, - TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS - #define TINFL_USE_64BIT_BITBUF 1 +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 #endif #if TINFL_USE_64BIT_BITBUF - typedef mz_uint64 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (64) +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) #else - typedef mz_uint32 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (32) +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { - mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; - tinfl_bit_buf_t m_bit_buf; - size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; - mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; -// ------------------- Low-level Compression API Definitions +#ifdef __cplusplus +} +#endif + +#pragma once -// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). -#define TDEFL_LESS_MEMORY 0 -// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): -// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). -enum -{ - TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF -}; +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif -// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. -// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). -// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. -// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). -// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) -// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. -// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. -// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. -// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). enum { - TDEFL_WRITE_ZLIB_HEADER = 0x01000, - TDEFL_COMPUTE_ADLER32 = 0x02000, - TDEFL_GREEDY_PARSING_FLAG = 0x04000, - TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, - TDEFL_RLE_MATCHES = 0x10000, - TDEFL_FILTER_MATCHES = 0x20000, - TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, - TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 }; -// High level compression functions: -// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of source block to compress. -// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must free() the returned block when it's no longer needed. -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; -// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. -// Returns 0 on failure. -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; -// Compresses an image to a compressed PNG file in memory. -// On entry: -// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. -// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. -// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL -// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pLen_out will be set to the size of the PNG image file. -// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; -// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. -typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif -// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; -enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; + /* File's compressed size. */ + mz_uint64 m_comp_size; -// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). -#if TDEFL_LESS_MEMORY -enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#else -enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#endif + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; -// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. -typedef enum -{ - TDEFL_STATUS_BAD_PARAM = -2, - TDEFL_STATUS_PUT_BUF_FAILED = -1, - TDEFL_STATUS_OKAY = 0, - TDEFL_STATUS_DONE = 1, -} tdefl_status; + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; -// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums -typedef enum -{ - TDEFL_NO_FLUSH = 0, - TDEFL_SYNC_FLUSH = 2, - TDEFL_FULL_FLUSH = 3, - TDEFL_FINISH = 4 -} tdefl_flush; + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; -// tdefl's compression state structure. -typedef struct -{ - tdefl_put_buf_func_ptr m_pPut_buf_func; - void *m_pPut_buf_user; - mz_uint m_flags, m_max_probes[2]; - int m_greedy_parsing; - mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; - mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; - mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; - mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; - tdefl_status m_prev_return_status; - const void *m_pIn_buf; - void *m_pOut_buf; - size_t *m_pIn_buf_size, *m_pOut_buf_size; - tdefl_flush m_flush; - const mz_uint8 *m_pSrc; - size_t m_src_buf_left, m_out_buf_ofs; - mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; - mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; - mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; - mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; - mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; -} tdefl_compressor; + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; -// Initializes the compressor. -// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. -// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. -// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. -// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; -// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; -// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. -// tdefl_compress_buffer() always consumes the entire input buffer. -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); -mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; -// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. -#ifndef MINIZ_NO_ZLIB_APIS -// Create tdefl_compress() flags given zlib-style compression parameters. -// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) -// window_bits may be -15 (raw deflate) or 15 (zlib) -// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); -#endif // #ifndef MINIZ_NO_ZLIB_APIS + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; -#ifdef __cplusplus -} -#endif +} mz_zip_archive_file_stat; -#endif // MINIZ_HEADER_INCLUDED +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); -// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; -#ifndef MINIZ_HEADER_FILE_ONLY +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; -typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; -typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; -typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 +} mz_zip_flags; -#include -#include +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; -#define MZ_ASSERT(x) assert(x) +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; -#ifdef MINIZ_NO_MALLOC - #define MZ_MALLOC(x) NULL - #define MZ_FREE(x) (void)x, ((void)0) - #define MZ_REALLOC(p, x) NULL -#else - #define MZ_MALLOC(x) malloc(x) - #define MZ_FREE(x) free(x) - #define MZ_REALLOC(p, x) realloc(p, x) -#endif + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; -#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) -#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) -#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + mz_uint64 m_file_offset_alignment; -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) - #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) -#else - #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) - #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) -#endif + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; -#ifdef _MSC_VER - #define MZ_FORCEINLINE __forceinline -#elif defined(__GNUC__) - #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) -#else - #define MZ_FORCEINLINE inline -#endif + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; -#ifdef __cplusplus - extern "C" { -#endif + mz_zip_internal_state *m_pState; -// ------------------- zlib-style API's +} mz_zip_archive; -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +typedef struct { - mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; - if (!ptr) return MZ_ADLER32_INIT; - while (buf_len) { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - return (s2 << 16) + s1; -} + mz_zip_archive *pZip; + mz_uint flags; -// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ -mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) -{ - static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; - mz_uint32 crcu32 = (mz_uint32)crc; - if (!ptr) return MZ_CRC32_INIT; - crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } - return ~crcu32; -} + int status; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; +#endif + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; -void mz_free(void *p) -{ - MZ_FREE(p); -} + size_t out_blk_remain; -#ifndef MINIZ_NO_ZLIB_APIS + tinfl_decompressor inflator; -static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } -static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } -static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } +} mz_zip_reader_extract_iter_state; -const char *mz_version(void) -{ - return MZ_VERSION; -} +/* -------- ZIP reading */ -int mz_deflateInit(mz_streamp pStream, int level) -{ - return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); -} +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) -{ - tdefl_compressor *pComp; - mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); - - if (!pStream) return MZ_STREAM_ERROR; - if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); - pStream->data_type = 0; - pStream->adler = MZ_ADLER32_INIT; - pStream->msg = NULL; - pStream->reserved = 0; - pStream->total_in = 0; - pStream->total_out = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; - - pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - return MZ_MEM_ERROR; +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); - pStream->state = (struct mz_internal_state *)pComp; +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif - if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) - { - mz_deflateEnd(pStream); - return MZ_PARAM_ERROR; - } +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); - return MZ_OK; -} +/* -------- ZIP reading or writing */ -int mz_deflateReset(mz_streamp pStream) -{ - if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; - pStream->total_in = pStream->total_out = 0; - tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); - return MZ_OK; -} +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +void mz_zip_zero_struct(mz_zip_archive *pZip); -int mz_deflate(mz_streamp pStream, int flush) -{ - size_t in_bytes, out_bytes; - mz_ulong orig_total_in, orig_total_out; - int mz_status = MZ_OK; +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); - if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; - if (!pStream->avail_out) return MZ_BUF_ERROR; +/* Returns the total number of files in the archive. */ +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +int mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +/* Returns MZ_FALSE if the file cannot be found. */ +mz_bool mz_zip_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); - if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) - return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); - orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; - for ( ; ; ) - { - tdefl_status defl_status; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); - defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; - pStream->total_out += (mz_uint)out_bytes; +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); - if (defl_status < 0) - { - mz_status = MZ_STREAM_ERROR; - break; - } - else if (defl_status == TDEFL_STATUS_DONE) - { - mz_status = MZ_STREAM_END; - break; - } - else if (!pStream->avail_out) - break; - else if ((!pStream->avail_in) && (flush != MZ_FINISH)) - { - if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) - break; - return MZ_BUF_ERROR; // Can't make forward progress without some input. - } - } - return mz_status; -} +/* Returns detailed information about an archive file entry. */ +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); -int mz_deflateEnd(mz_streamp pStream) -{ - if (!pStream) return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) -{ - (void)pStream; - // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) - return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); -} +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) -{ - int status; - mz_stream stream; - memset(&stream, 0, sizeof(stream)); +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; +/* Extracts a archive file to a memory buffer. */ +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); - status = mz_deflateInit(&stream, level); - if (status != MZ_OK) return status; +/* Extracts a archive file using a callback function to output the file's data. */ +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); - status = mz_deflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_deflateEnd(&stream); - return (status == MZ_OK) ? MZ_BUF_ERROR : status; - } +/* Extract a file iteratively */ +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); - *pDest_len = stream.total_out; - return mz_deflateEnd(&stream); -} +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); -} +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif -mz_ulong mz_compressBound(mz_ulong source_len) -{ - return mz_deflateBound(NULL, source_len); -} +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif -typedef struct -{ - tinfl_decompressor m_decomp; - mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; - mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; - tinfl_status m_last_status; -} inflate_state; +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); -int mz_inflateInit2(mz_streamp pStream, int window_bits) -{ - inflate_state *pDecomp; - if (!pStream) return MZ_STREAM_ERROR; - if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); - pStream->data_type = 0; - pStream->adler = 0; - pStream->msg = NULL; - pStream->total_in = 0; - pStream->total_out = 0; - pStream->reserved = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; +/* Misc utils/helpers, valid for ZIP reading or writing */ +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); - pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); - if (!pDecomp) return MZ_MEM_ERROR; +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +mz_bool mz_zip_end(mz_zip_archive *pZip); - pStream->state = (struct mz_internal_state *)pDecomp; +/* -------- ZIP writing */ - tinfl_init(&pDecomp->m_decomp); - pDecomp->m_dict_ofs = 0; - pDecomp->m_dict_avail = 0; - pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; - pDecomp->m_first_call = 1; - pDecomp->m_has_flushed = 0; - pDecomp->m_window_bits = window_bits; +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - return MZ_OK; -} +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); -int mz_inflateInit(mz_streamp pStream) -{ - return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); -} +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); -int mz_inflate(mz_streamp pStream, int flush) -{ - inflate_state* pState; - mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; - size_t in_bytes, out_bytes, orig_avail_in; - tinfl_status status; - - if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; - if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - - pState = (inflate_state*)pStream->state; - if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; - orig_avail_in = pStream->avail_in; - - first_call = pState->m_first_call; pState->m_first_call = 0; - if (pState->m_last_status < 0) return MZ_DATA_ERROR; - - if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - pState->m_has_flushed |= (flush == MZ_FINISH); - - if ((flush == MZ_FINISH) && (first_call)) - { - // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. - decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); - pState->m_last_status = status; - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; - pStream->adler = tinfl_get_adler32(&pState->m_decomp); - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; - - if (status < 0) - return MZ_DATA_ERROR; - else if (status != TINFL_STATUS_DONE) - { - pState->m_last_status = TINFL_STATUS_FAILED; - return MZ_BUF_ERROR; - } - return MZ_STREAM_END; - } - // flush != MZ_FINISH then we must assume there's more input. - if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; - - if (pState->m_dict_avail) - { - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; - } - - for ( ; ; ) - { - in_bytes = pStream->avail_in; - out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; - - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); - pState->m_last_status = status; - - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); - - pState->m_dict_avail = (mz_uint)out_bytes; - - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - - if (status < 0) - return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). - else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) - return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. - else if (flush == MZ_FINISH) - { - // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. - if (status == TINFL_STATUS_DONE) - return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; - // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. - else if (!pStream->avail_out) - return MZ_BUF_ERROR; - } - else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) - break; - } - - return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; -} +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif -int mz_inflateEnd(mz_streamp pStream) -{ - if (!pStream) - return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - mz_stream stream; - int status; - memset(&stream, 0, sizeof(stream)); +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); - status = mz_inflateInit(&stream); - if (status != MZ_OK) - return status; +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - status = mz_inflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_inflateEnd(&stream); - return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; - } - *pDest_len = stream.total_out; +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif - return mz_inflateEnd(&stream); -} +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); -const char *mz_error(int err) -{ - static struct { int m_err; const char *m_pDesc; } s_error_descs[] = - { - { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, - { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } - }; - mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; - return NULL; -} +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); -#endif //MINIZ_NO_ZLIB_APIS +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); -// ------------------- Low-level Decompression (completely independent from all compression API's) +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); -#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) -#define TINFL_MEMSET(p, c, l) memset(p, c, l) +/* -------- Misc. high-level helper functions: */ -#define TINFL_CR_BEGIN switch(r->m_state) { case 0: -#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END -#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END -#define TINFL_CR_FINISH } +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); -// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never -// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. -#define TINFL_GET_BYTE(state_index, c) do { \ - if (pIn_buf_cur >= pIn_buf_end) { \ - for ( ; ; ) { \ - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ - TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ - if (pIn_buf_cur < pIn_buf_end) { \ - c = *pIn_buf_cur++; \ - break; \ - } \ - } else { \ - c = 0; \ - break; \ - } \ - } \ - } else c = *pIn_buf_cur++; } MZ_MACRO_END - -#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) -#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END -#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END - -// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. -// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a -// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the -// bit buffer contains >=15 bits (deflate's max. Huffman code size). -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ - do { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ - if (temp >= 0) { \ - code_len = temp >> 9; \ - if ((code_len) && (num_bits >= code_len)) \ - break; \ - } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ - code_len = TINFL_FAST_LOOKUP_BITS; \ - do { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ - } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ - } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ - } while (num_bits < 15); - -// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read -// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully -// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. -// The slow path is only executed at the very end of the input buffer. -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ - int temp; mz_uint code_len, c; \ - if (num_bits < 15) { \ - if ((pIn_buf_end - pIn_buf_cur) < 2) { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ - } else { \ - bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ - } \ - } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ - code_len = temp >> 9, temp &= 511; \ - else { \ - code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ - } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) -{ - static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; - static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; - - tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; - const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; - size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; - - // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). - if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } - - num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; - TINFL_CR_BEGIN - - bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); - counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); - if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); - if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } - } - - do - { - TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; - if (r->m_type == 0) - { - TINFL_SKIP_BITS(5, num_bits & 7); - for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } - if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } - while ((counter) && (num_bits)) - { - TINFL_GET_BITS(51, dist, 8); - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)dist; - counter--; - } - while (counter) - { - size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } - while (pIn_buf_cur >= pIn_buf_end) - { - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) - { - TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); - } - else - { - TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); - } - } - n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); - TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; - } - } - else if (r->m_type == 3) - { - TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); - } - else - { - if (r->m_type == 1) - { - mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; - r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); - for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; - } - else - { - for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } - r->m_table_sizes[2] = 19; - } - for ( ; (int)r->m_type >= 0; r->m_type--) - { - int tree_next, tree_cur; tinfl_huff_table *pTable; - mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); - for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; - used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; - for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } - if ((65536 != total) && (used_syms > 1)) - { - TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); - } - for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) - { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; - cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); - if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } - rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); - for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) - { - tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; - } - tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; - } - if (r->m_type == 2) - { - for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) - { - mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } - if ((dist == 16) && (!counter)) - { - TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); - } - num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; - TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; - } - if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) - { - TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); - } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); - } - } - for ( ; ; ) - { - mz_uint8 *pSrc; - for ( ; ; ) - { - if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) - { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); - if (counter >= 256) - break; - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)counter; - } - else - { - int sym2; mz_uint code_len; -#if TINFL_USE_64BIT_BITBUF - if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } -#else - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - counter = sym2; bit_buf >>= code_len; num_bits -= code_len; - if (counter & 256) - break; +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ -#if !TINFL_USE_64BIT_BITBUF - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#ifdef __cplusplus +} #endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - bit_buf >>= code_len; num_bits -= code_len; - pOut_buf_cur[0] = (mz_uint8)counter; - if (sym2 & 256) - { - pOut_buf_cur++; - counter = sym2; - break; - } - pOut_buf_cur[1] = (mz_uint8)sym2; - pOut_buf_cur += 2; - } - } - if ((counter &= 511) == 256) break; +#endif /* MINIZ_NO_ARCHIVE_APIS */ - num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); - num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; - dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; - if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - { - TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); - } +#ifdef __cplusplus +extern "C" { +#endif - pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); +/* ------------------- zlib-style API's */ - if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) - { - while (counter--) - { - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; - } - continue; - } -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - else if ((counter >= 9) && (counter <= dist)) - { - const mz_uint8 *pSrc_end = pSrc + (counter & ~7); - do - { - ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; - ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; - pOut_buf_cur += 8; - } while ((pSrc += 8) < pSrc_end); - if ((counter &= 7) < 3) - { - if (counter) - { - pOut_buf_cur[0] = pSrc[0]; - if (counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - continue; - } - } -#endif - do - { - pOut_buf_cur[0] = pSrc[0]; - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur[2] = pSrc[2]; - pOut_buf_cur += 3; pSrc += 3; - } while ((int)(counter -= 3) > 2); - if ((int)counter > 0) +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { - pOut_buf_cur[0] = pSrc[0]; - if ((int)counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; } - } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; } - } while (!(r->m_final & 1)); - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } - } - TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - TINFL_CR_FINISH + return (s2 << 16) + s1; +} -common_exit: - r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; - *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; - if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) - { - const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; - mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; - while (buf_len) +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) - { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; } - r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; - } - return status; -} - -// Higher level helper functions. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { - tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; - *pOut_len = 0; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, - (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; } - src_buf_ofs += src_buf_size; - *pOut_len += dst_buf_size; - if (status == TINFL_STATUS_DONE) break; - new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; - pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); - if (!pNew_buf) + + while (buf_len) { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; } - pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; - } - return pBuf; + + return ~crc32; } +#endif -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +void mz_free(void *p) { - tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); - status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; + MZ_FREE(p); } -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { - int result = 0; - tinfl_decompressor decomp; - mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; - if (!pDict) - return TINFL_STATUS_FAILED; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, - (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); - in_buf_ofs += in_buf_size; - if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) - break; - if (status != TINFL_STATUS_HAS_MORE_OUTPUT) - { - result = (status == TINFL_STATUS_DONE); - break; - } - dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); - } - MZ_FREE(pDict); - *pIn_buf_size = in_buf_ofs; - return result; -} - -// ------------------- Low-level Compression (independent from all decompression API's) - -// Purposely making these tables static for faster init and thread safety. -static const mz_uint16 s_tdefl_len_sym[256] = { - 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, - 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, - 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, - 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, - 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, - 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, - 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, - 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; - -static const mz_uint8 s_tdefl_len_extra[256] = { - 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; - -static const mz_uint8 s_tdefl_small_dist_sym[512] = { - 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, - 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; - -static const mz_uint8 s_tdefl_small_dist_extra[512] = { - 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7 }; - -static const mz_uint8 s_tdefl_large_dist_sym[128] = { - 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, - 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; - -static const mz_uint8 s_tdefl_large_dist_extra[128] = { - 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, - 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; - -// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. -typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; -static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) -{ - mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); - for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } - while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; - for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) - { - const mz_uint32* pHist = &hist[pass << 8]; - mz_uint offsets[256], cur_ofs = 0; - for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } - for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; - { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } - } - return pCur_syms; -} - -// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. -static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +void miniz_def_free_func(void *opaque, void *address) { - int root, leaf, next, avbl, used, dpth; - if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } - A[0].m_key += A[1].m_key; root = 0; leaf = 2; - for (next=1; next < n-1; next++) - { - if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; - avbl = 1; used = dpth = 0; root = n-2; next = n-1; - while (avbl>0) - { - while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } - while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } - avbl = 2*used; dpth++; used = 0; - } -} - -// Limits canonical Huffman code table's max code size. -enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; -static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) + (void)opaque, (void)address; + MZ_FREE(address); +} +void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { - int i; mz_uint32 total = 0; if (code_list_len <= 1) return; - for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; - for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); - while (total != (1UL << max_code_size)) - { - pNum_codes[max_code_size]--; - for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } - total--; - } + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); } -static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +const char *mz_version(void) { - int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); - if (static_table) - { - for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; - } - else - { - tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; - int num_used_syms = 0; - const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; - for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } - - pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); - - for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; - - tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - - MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); - for (i = 1, j = num_used_syms; i <= code_size_limit; i++) - for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); - } - - next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); - - for (i = 0; i < table_len; i++) - { - mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; - code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); - d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; - } -} - -#define TDEFL_PUT_BITS(b, l) do { \ - mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ - d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ - while (d->m_bits_in >= 8) { \ - if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ - *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ - d->m_bit_buffer >>= 8; \ - d->m_bits_in -= 8; \ - } \ -} MZ_MACRO_END - -#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ - if (rle_repeat_count < 3) { \ - d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ - while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ - } else { \ - d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ -} rle_repeat_count = 0; } } - -#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ - if (rle_z_count < 3) { \ - d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ - } else if (rle_z_count <= 10) { \ - d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ - } else { \ - d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ -} rle_z_count = 0; } } + return MZ_VERSION; +} -static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; +#ifndef MINIZ_NO_ZLIB_APIS -static void tdefl_start_dynamic_block(tdefl_compressor *d) +int mz_deflateInit(mz_streamp pStream, int level) { - int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; - mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} - d->m_huff_count[0][256] = 1; +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); - tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); - tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; - for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; - for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; - memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); - memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); - total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; - memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); - for (i = 0; i < total_code_sizes_to_pack; i++) - { - mz_uint8 code_size = code_sizes_to_pack[i]; - if (!code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } - } - else + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { - TDEFL_RLE_ZERO_CODE_SIZE(); - if (code_size != prev_code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; - } - else if (++rle_repeat_count == 6) - { - TDEFL_RLE_PREV_CODE_SIZE(); - } + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; } - prev_code_size = code_size; - } - if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } - - tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); - - TDEFL_PUT_BITS(2, 2); - - TDEFL_PUT_BITS(num_lit_codes - 257, 5); - TDEFL_PUT_BITS(num_dist_codes - 1, 5); - for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; - num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); - for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); - - for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) - { - mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); - TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); - if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); - } + return MZ_OK; } -static void tdefl_start_static_block(tdefl_compressor *d) +int mz_deflateReset(mz_streamp pStream) { - mz_uint i; - mz_uint8 *p = &d->m_huff_code_sizes[0][0]; - - for (i = 0; i <= 143; ++i) *p++ = 8; - for ( ; i <= 255; ++i) *p++ = 9; - for ( ; i <= 279; ++i) *p++ = 7; - for ( ; i <= 287; ++i) *p++ = 8; - - memset(d->m_huff_code_sizes[1], 5, 32); - - tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); - tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); - - TDEFL_PUT_BITS(1, 2); + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; } -static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +int mz_deflate(mz_streamp pStream, int flush) { - mz_uint flags; - mz_uint8 *pLZ_codes; - mz_uint8 *pOutput_buf = d->m_pOutput_buf; - mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; - mz_uint64 bit_buffer = d->m_bit_buffer; - mz_uint bits_in = d->m_bits_in; + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; -#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; - if (flags & 1) - { - mz_uint s0, s1, n0, n1, sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; - // This sequence coaxes MSVC into using cmov's vs. jmp's. - s0 = s_tdefl_small_dist_sym[match_dist & 511]; - n0 = s_tdefl_small_dist_extra[match_dist & 511]; - s1 = s_tdefl_large_dist_sym[match_dist >> 8]; - n1 = s_tdefl_large_dist_extra[match_dist >> 8]; - sym = (match_dist < 512) ? s0 : s1; - num_extra_bits = (match_dist < 512) ? n0 : n1; - - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) - { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ } - } } + return mz_status; +} - if (pOutput_buf >= d->m_pOutput_buf_end) - return MZ_FALSE; +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} - *(mz_uint64*)pOutput_buf = bit_buffer; - pOutput_buf += (bits_in >> 3); - bit_buffer >>= (bits_in & ~7); - bits_in &= 7; - } +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} -#undef TDEFL_PUT_BITS_FAST +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); - d->m_pOutput_buf = pOutput_buf; - d->m_bits_in = 0; - d->m_bit_buffer = 0; + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; - while (bits_in) - { - mz_uint32 n = MZ_MIN(bits_in, 16); - TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); - bit_buffer >>= n; - bits_in -= n; - } + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; - return (d->m_pOutput_buf < d->m_pOutput_buf_end); -} -#else -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) -{ - mz_uint flags; - mz_uint8 *pLZ_codes; - - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; - if (flags & 1) - { - mz_uint sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - - if (match_dist < 512) - { - sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; - } - else - { - sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; - } - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; } - } - - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); - return (d->m_pOutput_buf < d->m_pOutput_buf_end); + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); } -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS -static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { - if (static_block) - tdefl_start_static_block(d); - else - tdefl_start_dynamic_block(d); - return tdefl_compress_lz_codes(d); + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } -static int tdefl_flush_block(tdefl_compressor *d, int flush) +mz_ulong mz_compressBound(mz_ulong source_len) { - mz_uint saved_bit_buf, saved_bits_in; - mz_uint8 *pSaved_output_buf; - mz_bool comp_block_succeeded = MZ_FALSE; - int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; - mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; - - d->m_pOutput_buf = pOutput_buf_start; - d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; - - MZ_ASSERT(!d->m_output_flush_remaining); - d->m_output_flush_ofs = 0; - d->m_output_flush_remaining = 0; - - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); - d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); - - if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) - { - TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); - } + return mz_deflateBound(NULL, source_len); +} - TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; - pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} - if (!use_raw_block) - comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} - // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. - if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && - ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) - { - mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - TDEFL_PUT_BITS(0, 2); - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) { - TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; } - for (i = 0; i < d->m_total_lz_bytes; ++i) + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) { - TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } - } - // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. - else if (!comp_block_succeeded) - { - d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - tdefl_compress_block(d, MZ_TRUE); - } - if (flush) - { - if (flush == TDEFL_FINISH) + for (;;) { - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; } - else + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) { - mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; } - } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); - MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; - if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) - { - if (d->m_pPut_buf_func) + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) - return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } - else if (pOutput_buf_start == d->m_output_buf) - { - int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); - d->m_out_buf_ofs += bytes_to_copy; - if ((n -= bytes_to_copy) != 0) - { - d->m_output_flush_ofs = bytes_to_copy; - d->m_output_flush_remaining = n; - } - } - else - { - d->m_out_buf_ofs += n; - } - } + *pDest_len = stream.total_out; - return d->m_output_flush_remaining; + return mz_inflateEnd(&stream); } -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES -#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +const char *mz_error(int err) { - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; - mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for ( ; ; ) - { - for ( ; ; ) + static struct { - if (--num_probes_left == 0) return; - #define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; - } - if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; - do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); - if (!probe_len) + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = { - *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; - } - else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = { - *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; - c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); - } - } -} -#else -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct { - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint8 *s = d->m_dict + pos, *p, *q; - mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for ( ; ; ) - { - for ( ; ; ) + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { - if (--num_probes_left == 0) return; - #define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; } - if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; - if (probe_len > match_len) + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { - *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; - c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } } - } + return pCur_syms; } -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN -static mz_bool tdefl_compress_fast(tdefl_compressor *d) +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { - // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. - mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; - mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; - mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - - while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) - { - const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; - mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); - d->m_src_buf_left -= num_bytes_to_process; - lookahead_size += num_bytes_to_process; - - while (num_bytes_to_process) - { - mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); - memcpy(d->m_dict + dst_pos, d->m_pSrc, n); - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); - d->m_pSrc += n; - dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; - num_bytes_to_process -= n; - } - - dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); - if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; - - while (lookahead_size >= 4) - { - mz_uint cur_match_dist, cur_match_len = 1; - mz_uint8 *pCur_dict = d->m_dict + cur_pos; - mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; - mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; - mz_uint probe_pos = d->m_hash[hash]; - d->m_hash[hash] = (mz_uint16)lookahead_pos; - - if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) - { - const mz_uint16 *p = (const mz_uint16 *)pCur_dict; - const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); - mz_uint32 probe_len = 32; - do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); - cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); - if (!probe_len) - cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; - - if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) { - cur_match_len = 1; - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; } else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { - mz_uint32 s0, s1; - cur_match_len = MZ_MIN(cur_match_len, lookahead_size); - - MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); - - cur_match_dist--; - - pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); - *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; - pLZ_code_buf += 3; - *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); - - s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; - s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; - d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; - - d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; } - } - else - { - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; - } - - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } - - total_lz_bytes += cur_match_len; - lookahead_pos += cur_match_len; - dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; - MZ_ASSERT(lookahead_size >= cur_match_len); - lookahead_size -= cur_match_len; + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; } +} - while (lookahead_size) +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else { - mz_uint8 lit = d->m_dict[cur_pos]; + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } - total_lz_bytes++; - *pLZ_code_buf++ = lit; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); - d->m_huff_count[0][lit]++; + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; - lookahead_pos++; - dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; - lookahead_size--; + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } - } - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - return MZ_TRUE; -} -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); -static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) -{ - d->m_total_lz_bytes++; - *d->m_pLZ_code_buf++ = lit; - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - d->m_huff_count[0][lit]++; + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } } -static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) -{ - mz_uint32 s0, s1; +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } - MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } - d->m_total_lz_bytes += match_len; +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; - match_dist -= 1; - d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); - d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; + d->m_huff_count[0][256] = 1; - *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); - s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; - d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; - if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; -} + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; -static mz_bool tdefl_compress_normal(tdefl_compressor *d) -{ - const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; - tdefl_flush flush = d->m_flush; - - while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) - { - mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; - // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. - if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) - { - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; - mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); - const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; - src_buf_left -= num_bytes_to_process; - d->m_lookahead_size += num_bytes_to_process; - while (pSrc != pSrc_end) - { - mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; - } - } - else - { - while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - { - mz_uint8 c = *pSrc++; - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - src_buf_left--; - d->m_dict[dst_pos] = c; - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) - { - mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; - mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - } - } - } - d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); - if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - break; - - // Simple lazy/greedy parsing state machine. - len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) - { - if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) - { - mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; - cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } - if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; - } - } - else - { - tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); - } - if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) { - cur_match_dist = cur_match_len = 0; - } - if (d->m_saved_match_len) - { - if (cur_match_len > d->m_saved_match_len) - { - tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); - if (cur_match_len >= 128) + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) { - tdefl_record_match(d, cur_match_len, cur_match_dist); - d->m_saved_match_len = 0; len_to_move = cur_match_len; + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } } else { - d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } } - } - else - { - tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); - len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; - } + prev_code_size = code_size; } - else if (!cur_match_dist) - tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); - else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + if (rle_repeat_count) { - tdefl_record_match(d, cur_match_len, cur_match_dist); - len_to_move = cur_match_len; + TDEFL_RLE_PREV_CODE_SIZE(); } else { - d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; - } - // Move the lookahead forward by len_to_move bytes. - d->m_lookahead_pos += len_to_move; - MZ_ASSERT(d->m_lookahead_size >= len_to_move); - d->m_lookahead_size -= len_to_move; - d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); - // Check if it's time to flush the current LZ codes to the internal output buffer. - if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || - ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) - { - int n; - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; + TDEFL_RLE_ZERO_CODE_SIZE(); } - } - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - return MZ_TRUE; -} + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); -static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) -{ - if (d->m_pIn_buf_size) - { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - } + TDEFL_PUT_BITS(2, 2); - if (d->m_pOut_buf_size) - { - size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); - d->m_output_flush_ofs += (mz_uint)n; - d->m_output_flush_remaining -= (mz_uint)n; - d->m_out_buf_ofs += n; + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); - *d->m_pOut_buf_size = d->m_out_buf_ofs; - } + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); - return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } } -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +static void tdefl_start_static_block(tdefl_compressor *d) { - if (!d) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return TDEFL_STATUS_BAD_PARAM; - } - - d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; - d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; - d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; - d->m_out_buf_ofs = 0; - d->m_flush = flush; - - if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || - (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); - } - d->m_wants_to_finish |= (flush == TDEFL_FINISH); - - if ((d->m_output_flush_remaining) || (d->m_finished)) - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && - ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && - ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) - { - if (!tdefl_compress_fast(d)) - return d->m_prev_return_status; - } - else -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - { - if (!tdefl_compress_normal(d)) - return d->m_prev_return_status; - } - - if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) - d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); - - if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) - { - if (tdefl_flush_block(d, flush) < 0) - return d->m_prev_return_status; - d->m_finished = (flush == TDEFL_FINISH); - if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } - } - - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); -} + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) -{ - MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); -} + memset(d->m_huff_code_sizes[1], 5, 32); -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; - d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; - d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; - if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); - d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; - d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; - d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; - d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; - d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; - d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; - d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - return TDEFL_STATUS_OKAY; -} + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) -{ - return d->m_prev_return_status; + TDEFL_PUT_BITS(1, 2); } -mz_uint32 tdefl_get_adler32(tdefl_compressor *d) -{ - return d->m_adler32; -} +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { - tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; - pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; - succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); - succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); - MZ_FREE(pComp); return succeeded; -} + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } -typedef struct -{ - size_t m_size, m_capacity; - mz_uint8 *m_pBuf; - mz_bool m_expandable; -} tdefl_output_buffer; + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; -static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) -{ - tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; - size_t new_size = p->m_size + len; - if (new_size > p->m_capacity) - { - size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; - do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); - pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; - p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; - } - memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; - return MZ_TRUE; -} + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; - out_buf.m_expandable = MZ_TRUE; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; - *pOut_len = out_buf.m_size; return out_buf.m_pBuf; -} + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_buf) return 0; - out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; - return out_buf.m_size; -} + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; -#ifndef MINIZ_NO_ZLIB_APIS -static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } -// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) -{ - mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); - if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; +#undef TDEFL_PUT_BITS_FAST - if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; - else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; - else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; - else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; - else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; - return comp_flags; -} -#endif //MINIZ_NO_ZLIB_APIS + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) -#endif + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); -// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at -// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. -// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) -{ - // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. - static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; - if (!pComp) return NULL; - MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } - // write dummy header - for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); - // compress image data - tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); - for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } - if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - // write real header - *pLen_out = out_buf.m_size-41; - { - static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; - mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, - 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, - (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; - c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); - memcpy(out_buf.m_pBuf, pnghdr, 41); - } - // write footer (IDAT CRC-32, followed by IEND chunk) - if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); - // compute final size of file, grab compressed data buffer and return - *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; + return (d->m_pOutput_buf < d->m_pOutput_buf_end); } -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { - // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) - return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); -} - -#ifdef _MSC_VER -#pragma warning (pop) -#endif + mz_uint flags; + mz_uint8 *pLZ_codes; -// ------------------- .ZIP archive reading - -#ifndef MINIZ_NO_ARCHIVE_APIS + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; -#ifdef MINIZ_NO_STDIO - #define MZ_FILE void * -#else - #include - #include + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - #if defined(_MSC_VER) || defined(__MINGW64__) - static FILE *mz_fopen(const char *pFilename, const char *pMode) - { - FILE* pFile = NULL; - fopen_s(&pFile, pFilename, pMode); - return pFile; + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } } - static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) - { - FILE* pFile = NULL; - if (freopen_s(&pFile, pPath, pMode, pStream)) - return NULL; - return pFile; - } - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN mz_fopen - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 _ftelli64 - #define MZ_FSEEK64 _fseeki64 - #define MZ_FILE_STAT_STRUCT _stat - #define MZ_FILE_STAT _stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN mz_freopen - #define MZ_DELETE_FILE remove - #elif defined(__MINGW32__) - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello64 - #define MZ_FSEEK64 fseeko64 - #define MZ_FILE_STAT_STRUCT _stat - #define MZ_FILE_STAT _stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #elif defined(__TINYC__) - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftell - #define MZ_FSEEK64 fseek - #define MZ_FILE_STAT_STRUCT stat - #define MZ_FILE_STAT stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #elif defined(__GNUC__) && _LARGEFILE64_SOURCE - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen64(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello64 - #define MZ_FSEEK64 fseeko64 - #define MZ_FILE_STAT_STRUCT stat64 - #define MZ_FILE_STAT stat64 - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) - #define MZ_DELETE_FILE remove - #else - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello - #define MZ_FSEEK64 fseeko - #define MZ_FILE_STAT_STRUCT stat - #define MZ_FILE_STAT stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #endif // #ifdef _MSC_VER -#endif // #ifdef MINIZ_NO_STDIO -#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); -// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. -enum -{ - // ZIP archive identifiers and record sizes - MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, - MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, - // Central directory header record offsets - MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, - MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, - MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, - MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, - // Local directory header offsets - MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, - MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, - MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, - // End of central directory offsets - MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, - MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, -}; + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ -typedef struct +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { - void *m_p; - size_t m_size, m_capacity; - mz_uint m_element_size; -} mz_zip_array; + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} -struct mz_zip_internal_state_tag +static int tdefl_flush_block(tdefl_compressor *d, int flush) { - mz_zip_array m_central_dir; - mz_zip_array m_central_dir_offsets; - mz_zip_array m_sorted_central_dir_offsets; - MZ_FILE *m_pFile; - void *m_pMem; - size_t m_mem_size; - size_t m_mem_capacity; -}; + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; -#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size -#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; -static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) -{ - pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); - memset(pArray, 0, sizeof(mz_zip_array)); -} + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; -static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) -{ - void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; - if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } - if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; - pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; - return MZ_TRUE; -} + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); -static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) -{ - if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } - return MZ_TRUE; -} + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } -static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) -{ - if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } - pArray->m_size = new_size; - return MZ_TRUE; -} + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); -static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) -{ - return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); -} + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; -static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) -{ - size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; - memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); - return MZ_TRUE; + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; } -#ifndef MINIZ_NO_TIME -static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) { - struct tm tm; - memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; - tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; - tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; - return mktime(&tm); + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; } - -static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) { -#ifdef _MSC_VER - struct tm tm_struct; - struct tm *tm = &tm_struct; - errno_t err = localtime_s(tm, &time); - if (err) - { - *pDOS_date = 0; *pDOS_time = 0; - return; - } -#else - struct tm *tm = localtime(&time); -#endif - *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); - *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; } +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif - -#ifndef MINIZ_NO_STDIO -static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { -#ifdef MINIZ_NO_TIME - (void)pFilename; *pDOS_date = *pDOS_time = 0; -#else - struct MZ_FILE_STAT_STRUCT file_stat; - // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. - if (MZ_FILE_STAT(pFilename, &file_stat) != 0) - return MZ_FALSE; - mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); -#endif // #ifdef MINIZ_NO_TIME - return MZ_TRUE; + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } } - -#ifndef MINIZ_NO_TIME -static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { - struct utimbuf t; t.actime = access_time; t.modtime = modified_time; - return !utime(pFilename, &t); + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } } -#endif // #ifndef MINIZ_NO_TIME -#endif // #ifndef MINIZ_NO_STDIO +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ -static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) { - (void)flags; - if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; - pZip->m_zip_mode = MZ_ZIP_MODE_READING; - pZip->m_archive_size = 0; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; } +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ -static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (l_len < r_len) : (l < r); + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; } -#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END - -// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) -static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - int start = (size - 2) >> 1, end; - while (start >= 0) - { - int child, root = start; - for ( ; ; ) - { - if ((child = (root << 1) + 1) >= size) - break; - child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; - } - start--; - } + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; - end = size - 1; - while (end > 0) - { - int child, root = 0; - MZ_SWAP_UINT32(pIndices[end], pIndices[0]); - for ( ; ; ) + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) { - if ((child = (root << 1) + 1) >= end) - break; - child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; } - end--; - } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } -static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) +static mz_bool tdefl_compress_normal(tdefl_compressor *d) { - mz_uint cdir_size, num_this_disk, cdir_disk_index; - mz_uint64 cdir_ofs; - mz_int64 cur_file_ofs; - const mz_uint8 *p; - mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; - mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); - // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. - if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - // Find the end of central directory record by scanning the file from the end towards the beginning. - cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); - for ( ; ; ) - { - int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) - return MZ_FALSE; - for (i = n - 4; i >= 0; --i) - if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) - break; - if (i >= 0) + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { - cur_file_ofs += i; - break; + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } } - if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) - return MZ_FALSE; - cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); - } - // Read and verify the end of central directory record. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || - ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) - return MZ_FALSE; - num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); - cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); - if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) - return MZ_FALSE; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} - if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } - cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); - if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) - return MZ_FALSE; + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; - pZip->m_central_directory_file_ofs = cdir_ofs; + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } - if (pZip->m_total_files) - { - mz_uint i, n; + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} - // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. - if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || - (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) - return MZ_FALSE; +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } - if (sort_central_dir) + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { - if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) - return MZ_FALSE; + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); - if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) - return MZ_FALSE; + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); - // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). - p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; - for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { - mz_uint total_header_size, comp_size, decomp_size, disk_index; - if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) - return MZ_FALSE; - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); - if (sort_central_dir) - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) - return MZ_FALSE; - disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); - if ((disk_index != num_this_disk) && (disk_index != 1)) - return MZ_FALSE; - if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) - return MZ_FALSE; - if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) - return MZ_FALSE; - n -= total_header_size; p += total_header_size; + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; } - } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); - if (sort_central_dir) - mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } - return MZ_TRUE; + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { - if ((!pZip) || (!pZip->m_pRead)) - return MZ_FALSE; - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } -static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); - memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); - return s; + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; } -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - pZip->m_pRead = mz_zip_mem_read_func; - pZip->m_pIO_opaque = pZip; -#ifdef __cplusplus - pZip->m_pState->m_pMem = const_cast(pMem); -#else - pZip->m_pState->m_pMem = (void *)pMem; -#endif - pZip->m_pState->m_mem_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; + return d->m_prev_return_status; } -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); + return d->m_adler32; } -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { - mz_uint64 file_size; - MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); - if (!pFile) - return MZ_FALSE; - if (MZ_FSEEK64(pFile, 0, SEEK_END)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - file_size = MZ_FTELL64(pFile); - if (!mz_zip_reader_init_internal(pZip, flags)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - pZip->m_pRead = mz_zip_file_read_func; - pZip->m_pIO_opaque = pZip; - pZip->m_pState->m_pFile = pFile; - pZip->m_archive_size = file_size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; } -#endif // #ifndef MINIZ_NO_STDIO -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +typedef struct { - return pZip ? pZip->m_total_files : 0; -} + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; -static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { - if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return NULL; - return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; } -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc() +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do \ + { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) + { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +tinfl_decompressor *tinfl_decompressor_alloc() +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__GNUC__) && _LARGEFILE64_SOURCE +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__APPLE__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_OBJ(*pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const uint32_t size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + uint32_t file_index = pIndices[(uint32_t)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return NULL; + } + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; + mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + uint32_t i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_OBJ(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { - mz_uint m_bit_flag; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; - m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - return (m_bit_flag & 1); + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; } -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) { - mz_uint filename_len, external_attr; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + + pArchive_name++; + } - // First see if the filename ends with a '/' character. - filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_len) - { - if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') - return MZ_TRUE; - } - - // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. - // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. - // FIXME: Remove this check? Is it necessary - we already check the filename. - external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - if ((external_attr & 0x10) != 0) return MZ_TRUE; +} - return MZ_FALSE; +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); } -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if ((!p) || (!pStat)) - return MZ_FALSE; + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_OBJ(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (uncomp_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_OBJ(local_dir_header); + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } - // Unpack the central directory record. - pStat->m_file_index = file_index; - pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); - pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); - pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); - pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); -#ifndef MINIZ_NO_TIME - pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); -#endif - pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); - pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); - pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + for (;;) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } - // Copy as much of the filename and comment as possible. - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); - memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); - pStat->m_comment_size = n; - memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } - return MZ_TRUE; -} + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) -{ - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_buf_size) - { - n = MZ_MIN(n, filename_buf_size - 1); - memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); - pFilename[n] = '\0'; - } - return n + 1; -} - -static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) -{ - mz_uint i; - if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) - return 0 == memcmp(pA, pB, len); - for (i = 0; i < len; ++i) - if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) - return MZ_FALSE; - return MZ_TRUE; -} - -static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) -{ - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (int)(l_len - r_len) : (l - r); -} - -static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) -{ - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - const mz_uint filename_len = (mz_uint)strlen(pFilename); - int l = 0, h = size - 1; - while (l <= h) - { - int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); - if (!comp) - return file_index; - else if (comp < 0) - l = m + 1; - else - h = m - 1; - } - return -1; -} + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) -{ - mz_uint file_index; size_t name_len, comment_len; - if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return -1; - if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) - return mz_zip_reader_locate_file_binary_search(pZip, pName); - name_len = strlen(pName); if (name_len > 0xFFFF) return -1; - comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; - for (file_index = 0; file_index < pZip->m_total_files; file_index++) - { - const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); - mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); - const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - if (filename_len < name_len) - continue; - if (comment_len) - { - mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); - const char *pFile_comment = pFilename + filename_len + file_extra_len; - if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) - continue; - } - if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) - { - int ofs = filename_len - 1; - do - { - if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) - break; - } while (--ofs >= 0); - ofs++; - pFilename += ofs; filename_len -= ofs; - } - if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) - return file_index; - } - return -1; -} + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int status = TINFL_STATUS_DONE; - mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; - mz_zip_archive_file_stat file_stat; - void *pRead_buf; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - tinfl_decompressor inflator; + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); - if ((buf_size) && (!pBuf)) - return MZ_FALSE; + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; + cur_archive_file_ofs += local_dir_footer_size; + } - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; - // Ensure supplied output buffer is large enough. - needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; - if (buf_size < needed_size) - return MZ_FALSE; + return MZ_TRUE; +} - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; + memset(&file_modified_time, 0, sizeof(file_modified_time)); - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) - return MZ_FALSE; - return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); - } - - // Decompress the file either directly from memory or from a file input buffer. - tinfl_init(&inflator); - - if (pZip->m_pState->m_pMem) - { - // Read directly from the archive in memory. - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else if (pUser_read_buf) - { - // Use a user provided read buffer. - if (!user_read_buf_size) - return MZ_FALSE; - pRead_buf = (mz_uint8 *)pUser_read_buf; - read_buf_size = user_read_buf_size; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - else - { - // Temporarily allocate a read buffer. - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); #endif - return MZ_FALSE; - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - do - { - size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; - } - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - out_buf_ofs += out_buf_size; - } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); - if (status == TINFL_STATUS_DONE) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); - if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); - return status == TINFL_STATUS_DONE; -} + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); -} + MZ_FCLOSE(pSrc_file); -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) -{ - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); + return status; } +#endif /* #ifndef MINIZ_NO_STDIO */ -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { - return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); -} + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) -{ - mz_uint64 comp_size, uncomp_size, alloc_size; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - void *pBuf; + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); - if (pSize) - *pSize = 0; - if (!p) - return NULL; + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } - alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#endif - return NULL; - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) - return NULL; + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } - if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return NULL; - } + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } - if (pSize) *pSize = (size_t)alloc_size; - return pBuf; -} + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - { - if (pSize) *pSize = 0; - return MZ_FALSE; - } - return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); -} + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; - mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; - mz_zip_archive_file_stat file_stat; - void *pRead_buf = NULL; void *pWrite_buf = NULL; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; + do + { + mz_uint32 field_id, field_data_size, field_total_size; - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } - // Decompress the file either directly from memory or from a file input buffer. - if (pZip->m_pState->m_pMem) - { - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else - { - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pState->m_pMem) + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) { -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#endif - return MZ_FALSE; - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) - status = TINFL_STATUS_FAILED; - else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); - cur_file_ofs += file_stat.m_comp_size; - out_buf_ofs += file_stat.m_comp_size; - comp_remaining = 0; + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { - while (comp_remaining) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } - if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - out_buf_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - } - } - } - else - { - tinfl_decompressor inflator; - tinfl_init(&inflator); + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; - if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) - status = TINFL_STATUS_FAILED; - else + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { - do - { - mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - - if (out_buf_size) + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { - if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) - { - status = TINFL_STATUS_FAILED; - break; - } - file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); - if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) - { - status = TINFL_STATUS_FAILED; - break; - } + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } - } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); - } - } - if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } + pExtra_data = (const mz_uint8 *)file_data_array.m_p; - if (!pZip->m_pState->m_pMem) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - if (pWrite_buf) - pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + do + { + mz_uint32 field_id, field_data_size, field_total_size; - return status == TINFL_STATUS_DONE; -} + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); -} + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) -{ - (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); -} + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) -{ - mz_bool status; - mz_zip_archive_file_stat file_stat; - MZ_FILE *pFile; - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - pFile = MZ_FOPEN(pDst_filename, "wb"); - if (!pFile) - return MZ_FALSE; - status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); - if (MZ_FCLOSE(pFile) == EOF) - return MZ_FALSE; -#ifndef MINIZ_NO_TIME - if (status) - mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); -#endif - return status; -} -#endif // #ifndef MINIZ_NO_STDIO + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); -mz_bool mz_zip_reader_end(mz_zip_archive *pZip) -{ - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } - if (pZip->m_pState) - { - mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - } - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); - return MZ_TRUE; -} + mz_zip_array_clear(pZip, &file_data_array); + } -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); -} -#endif + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; -// ------------------- .ZIP archive writing + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; -static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } -static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } -#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) -#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + cur_dst_file_ofs += num_alignment_padding_bytes; -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) -{ - if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } - if (pZip->m_file_offset_alignment) - { - // Ensure user specified file offset alignment is a power of 2. - if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) - return MZ_FALSE; - } + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_archive_size = existing_size; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; -} + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; -static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); -#ifdef _MSC_VER - if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#else - if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#endif - return 0; - if (new_size > pState->m_mem_capacity) - { - void *pNew_block; - size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; - if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) - return 0; - pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; - } - memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); - pState->m_mem_size = (size_t)new_size; - return n; -} + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) -{ - pZip->m_pWrite = mz_zip_heap_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) - { - if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; + src_archive_bytes_remaining -= n; } - pZip->m_pState->m_mem_capacity = initial_allocation_size; - } - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); -} -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) -{ - MZ_FILE *pFile; - pZip->m_pWrite = mz_zip_file_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - pZip->m_pState->m_pFile = pFile; - if (size_to_reserve_at_beginning) - { - mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); - do + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) { - size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - cur_ofs += n; size_to_reserve_at_beginning -= n; - } while (size_to_reserve_at_beginning); - } - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) -{ - mz_zip_internal_state *pState; - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; - // No sense in trying to write to an archive that's already at the support max size - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - pState = pZip->m_pState; + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } - if (pState->m_pFile) - { -#ifdef MINIZ_NO_STDIO - pFilename; return MZ_FALSE; -#else - // Archive is being read from stdio - try to reopen as writable. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - if (!pFilename) - return MZ_FALSE; - pZip->m_pWrite = mz_zip_file_write_func; - if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) - { - // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. - mz_zip_reader_end(pZip); - return MZ_FALSE; - } -#endif // #ifdef MINIZ_NO_STDIO - } - else if (pState->m_pMem) - { - // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - pState->m_mem_capacity = pState->m_mem_size; - pZip->m_pWrite = mz_zip_heap_write_func; - } - // Archive is being read via a user provided read function - make sure the user has specified a write function too. - else if (!pZip->m_pWrite) - return MZ_FALSE; + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; - // Start writing new files at the archive's current central directory location. - pZip->m_archive_size = pZip->m_central_directory_file_ofs; - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_central_directory_file_ofs = 0; + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } - return MZ_TRUE; -} + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) -{ - return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); -} + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } -typedef struct -{ - mz_zip_archive *m_pZip; - mz_uint64 m_cur_archive_file_ofs; - mz_uint64 m_comp_size; -} mz_zip_writer_add_state; + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } -static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) -{ - mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; - if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) - return MZ_FALSE; - pState->m_cur_archive_file_ofs += len; - pState->m_comp_size += len; - return MZ_TRUE; -} + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); -static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; - size_t orig_central_dir_size = pState->m_central_dir.m_size; - mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - - // No zip64 support yet - if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) - return MZ_FALSE; + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; - if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) - return MZ_FALSE; + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) - { - // Try to push the central directory array back into its original state. - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; - return MZ_TRUE; -} + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); -static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) -{ - // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. - if (*pArchive_name == '/') - return MZ_FALSE; - while (*pArchive_name) - { - if ((*pArchive_name == '\\') || (*pArchive_name == ':')) - return MZ_FALSE; - pArchive_name++; - } - return MZ_TRUE; -} + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); -static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) -{ - mz_uint32 n; - if (!pZip->m_file_offset_alignment) - return 0; - n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); - return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); -} + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } -static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) -{ - char buf[4096]; - memset(buf, 0, MZ_MIN(sizeof(buf), n)); - while (n) - { - mz_uint32 s = MZ_MIN(sizeof(buf), n); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) - return MZ_FALSE; - cur_file_ofs += s; n -= s; - } - return MZ_TRUE; -} - -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) -{ - mz_uint16 method = 0, dos_time = 0, dos_date = 0; - mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - tdefl_compressor *pComp = NULL; - mz_bool store_data_uncompressed; - mz_zip_internal_state *pState; - - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; - store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); - pState = pZip->m_pState; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } - if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) - return MZ_FALSE; - // No zip64 support yet - if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } -#ifndef MINIZ_NO_TIME - { - time_t cur_time; time(&cur_time); - mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); - } -#endif // #ifndef MINIZ_NO_TIME - - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); - if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) - { - // Set DOS Subdirectory attribute bit. - ext_attributes |= 0x10; - // Subdirectories cannot contain data. - if ((buf_size) || (uncomp_size)) - return MZ_FALSE; - } - - // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) - if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) - return MZ_FALSE; + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); - if ((!store_data_uncompressed) && (buf_size)) - { - if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) - return MZ_FALSE; - } + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); - if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - { - uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); - uncomp_size = buf_size; - if (uncomp_size <= 3) + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { - level = 0; - store_data_uncompressed = MZ_TRUE; + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); } - } - if (store_data_uncompressed) - { - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } - cur_archive_file_ofs += buf_size; - comp_size = buf_size; + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - method = MZ_DEFLATED; - } - else if (buf_size) - { - mz_zip_writer_add_state state; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || - (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + pState = pZip->m_pState; + + if (pState->m_zip64) { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; - - method = MZ_DEFLATED; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pComp = NULL; + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; + /* Write end of central directory record */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; - return MZ_TRUE; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; } -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) { - mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; - mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - MZ_FILE *pSrc_file = NULL; + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; + *ppBuf = NULL; + *pSize = 0; - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; - if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) - return MZ_FALSE; - - pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); - if (!pSrc_file) - return MZ_FALSE; - MZ_FSEEK64(pSrc_file, 0, SEEK_END); - uncomp_size = MZ_FTELL64(pSrc_file); - MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + return MZ_TRUE; +} - if (uncomp_size > 0xFFFFFFFF) - { - // No zip64 support yet - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - if (uncomp_size <= 3) - level = 0; +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } - if (uncomp_size) - { - mz_uint64 uncomp_remaining = uncomp_size; - void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); - if (!pRead_buf) + if (!mz_zip_writer_validate_archive_name(pArchive_name)) { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; } - if (!level) + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { - while (uncomp_remaining) - { - mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); - if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; } - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); - uncomp_remaining -= n; - cur_archive_file_ofs += n; - } - comp_size = uncomp_size; + + created_new_archive = MZ_TRUE; } else { - mz_bool result = MZ_FALSE; - mz_zip_writer_add_state state; - tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; - - if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - for ( ; ; ) - { - size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); - tdefl_status status; - - if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) - break; - - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); - uncomp_remaining -= in_buf_size; - - status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); - if (status == TDEFL_STATUS_DONE) + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { - result = MZ_TRUE; - break; + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; } - else if (status != TDEFL_STATUS_OKAY) - break; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - if (!result) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); - method = MZ_DEFLATED; + return MZ_FALSE; + } } - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - } + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; - MZ_FCLOSE(pSrc_file); pSrc_file = NULL; + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; + status = MZ_FALSE; + } - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; + status = MZ_FALSE; + } - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; + if (pErr) + *pErr = actual_err; - return MZ_TRUE; + return status; } -#endif // #ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) { - mz_uint n, bit_flags, num_alignment_padding_bytes; - mz_uint64 comp_bytes_remaining, local_dir_header_ofs; - mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - size_t orig_central_dir_size; - mz_zip_internal_state *pState; - void *pBuf; const mz_uint8 *pSrc_central_header; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) - return MZ_FALSE; - pState = pZip->m_pState; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); - cur_dst_file_ofs = pZip->m_archive_size; + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) - return MZ_FALSE; - cur_dst_file_ofs += num_alignment_padding_bytes; - local_dir_header_ofs = cur_dst_file_ofs; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) - return MZ_FALSE; + if (pSize) + *pSize = 0; - while (comp_bytes_remaining) - { - n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + if ((!pZip_filename) || (!pArchive_name)) { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - cur_src_file_ofs += n; + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; + return NULL; } - cur_dst_file_ofs += n; - comp_bytes_remaining -= n; - } - - bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); - if (bit_flags & 8) - { - // Copy data descriptor - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; } - n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); } - cur_src_file_ofs += n; - cur_dst_file_ofs += n; - } - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + mz_zip_reader_end_internal(&zip_archive, p != NULL); - // no zip64 support yet - if (cur_dst_file_ofs > 0xFFFFFFFF) - return MZ_FALSE; + if (pErr) + *pErr = zip_archive.m_last_error; - orig_central_dir_size = pState->m_central_dir.m_size; + return p; +} - memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) - return MZ_FALSE; +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} - n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } +#endif /* #ifndef MINIZ_NO_STDIO */ - if (pState->m_central_dir.m_size > 0xFFFFFFFF) - return MZ_FALSE; - n = (mz_uint32)orig_central_dir_size; - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ - pZip->m_total_files++; - pZip->m_archive_size = cur_dst_file_ofs; +/* ------------------- Misc utils */ - return MZ_TRUE; +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; } -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) { - mz_zip_internal_state *pState; - mz_uint64 central_dir_ofs, central_dir_size; - mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - - pState = pZip->m_pState; + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} - // no zip64 support yet - if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; - central_dir_ofs = 0; - central_dir_size = 0; - if (pZip->m_total_files) - { - // Write central directory - central_dir_ofs = pZip->m_archive_size; - central_dir_size = pState->m_central_dir.m_size; - pZip->m_central_directory_file_ofs = central_dir_ofs; - if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) - return MZ_FALSE; - pZip->m_archive_size += central_dir_size; - } - - // Write end of central directory record - MZ_CLEAR_OBJ(hdr); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); - - if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) - return MZ_FALSE; -#ifndef MINIZ_NO_STDIO - if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) - return MZ_FALSE; -#endif // #ifndef MINIZ_NO_STDIO + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; - pZip->m_archive_size += sizeof(hdr); + prev_err = pZip->m_last_error; - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; - return MZ_TRUE; + pZip->m_last_error = err_num; + return prev_err; } -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) { - if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) - return MZ_FALSE; - if (pZip->m_pWrite != mz_zip_heap_write_func) - return MZ_FALSE; - if (!mz_zip_writer_finalize_archive(pZip)) - return MZ_FALSE; + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; - *pBuf = pZip->m_pState->m_pMem; - *pSize = pZip->m_pState->m_mem_size; - pZip->m_pState->m_pMem = NULL; - pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; - return MZ_TRUE; + return pZip->m_last_error; } -mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) { - mz_zip_internal_state *pState; - mz_bool status = MZ_TRUE; - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) - return MZ_FALSE; + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} - pState = pZip->m_pState; - pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; - if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); - pState->m_pMem = NULL; - } + prev_err = pZip->m_last_error; - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; - return status; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; } -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +const char *mz_zip_get_error_string(mz_zip_error mz_err) { - mz_bool status, created_new_archive = MZ_FALSE; - mz_zip_archive zip_archive; - struct MZ_FILE_STAT_STRUCT file_stat; - MZ_CLEAR_OBJ(zip_archive); - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) - { - // Create a new archive. - if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) - return MZ_FALSE; - created_new_archive = MZ_TRUE; - } - else - { - // Append to an existing archive. - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return MZ_FALSE; - if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) - { - mz_zip_reader_end(&zip_archive); - return MZ_FALSE; - } - } - status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); - // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) - if (!mz_zip_writer_finalize_archive(&zip_archive)) - status = MZ_FALSE; - if (!mz_zip_writer_end(&zip_archive)) - status = MZ_FALSE; - if ((!status) && (created_new_archive)) - { - // It's a new archive and something went wrong, so just delete it. - int ignoredStatus = MZ_DELETE_FILE(pZip_filename); - (void)ignoredStatus; - } - return status; + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + default: + break; + } + + return "unknown error"; } -void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) { - int file_index; - mz_zip_archive zip_archive; - void *p = NULL; + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; - if (pSize) - *pSize = 0; + return pZip->m_pState->m_zip64; +} - if ((!pZip_filename) || (!pArchive_name)) - return NULL; +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; - MZ_CLEAR_OBJ(zip_archive); - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return NULL; + return pZip->m_pState->m_central_dir.m_size; +} - if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) - p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} - mz_zip_reader_end(&zip_archive); - return p; +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; } -#endif // #ifndef MINIZ_NO_STDIO +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); -#ifdef __cplusplus + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); } -#endif -#endif // MINIZ_HEADER_FILE_ONLY +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} -/* - This is free and unencumbered software released into the public domain. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. + return MZ_FALSE; +} - For more information, please refer to -*/ +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/@gifti/private/mvtk_read.m b/@gifti/private/mvtk_read.m index a614e1b4..601af03c 100644 --- a/@gifti/private/mvtk_read.m +++ b/@gifti/private/mvtk_read.m @@ -14,7 +14,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: mvtk_read.m 6601 2015-11-19 13:55:32Z guillaume $ +% $Id: mvtk_read.m 7354 2018-06-22 10:44:22Z guillaume $ [pth,name,ext] = fileparts(filename); @@ -123,6 +123,7 @@ [N,l] = strtok(l(2:end)); N = str2double(N); break; + case '' otherwise error('Invalid VTK file.'); end @@ -138,14 +139,16 @@ %l = fgetl(fid); % {POINT_DATA,CELL_DATA} N l = fgetl(fid); % SCALARS dataName dataType numComp [P,l] = strtok(l); - [S,l] = strtok(l(2:end)); - [S,l] = strtok(l(2:end)); - S = strtok(l(2:end)); S = str2double(S); - l = fgetl(fid); % LOOKUP_TABLE default - fmt = repmat('%f ',1,S); - fmt = [fmt(1:end-1) '\n']; - M.cdata = textscan(fid,fmt,N,'CollectOutput',true); - M.cdata = M.cdata{1}; + if strcmp(P,'SCALARS') + [S,l] = strtok(l(2:end)); + [S,l] = strtok(l(2:end)); + S = strtok(l(2:end)); S = str2double(S); + l = fgetl(fid); % LOOKUP_TABLE default + fmt = repmat('%f ',1,S); + fmt = [fmt(1:end-1) '\n']; + M.cdata = textscan(fid,fmt,N,'CollectOutput',true); + M.cdata = M.cdata{1}; + end end fclose(fid); diff --git a/@gifti/private/mz3_read.m b/@gifti/private/mz3_read.m new file mode 100644 index 00000000..73a08804 --- /dev/null +++ b/@gifti/private/mz3_read.m @@ -0,0 +1,114 @@ +function M = mz3_read(filename) +% Read MZ3-formatted data from disk +% FORMAT M = mz3_read(filename) +% +% filename - MZ3-formatted file name +% M - data structure +%__________________________________________________________________________ +% +% MZ3 Format Specification: +% https://github.com/neurolabusc/surf-ice/tree/master/mz3 +%__________________________________________________________________________ +% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: mz3_read.m 7428 2018-09-25 13:25:13Z guillaume $ + + +M = struct(); + +%-Open file +%-------------------------------------------------------------------------- +fid = fopen(filename,'r','ieee-le'); +if fid == -1 + error('Cannot open %s.',filename); +end + +%-Load (uncompressed) data +%-------------------------------------------------------------------------- +magic = fread(fid,1,'uint16'); +if magic == 23117 % uncompressed 0x4D5A, 23117, [77 90], 'MZ' + frewind(fid); + D = fread(fid,Inf,'*uint8'); +elseif magic == 35615 % GZipped compressed 0x1F8B, 35615, [31 139] + % Read gzip file + % http://zlib.org/rfc-gzip.html + member = fread(fid,8,'uint8'); % |CM |FLG| MTIME |XFL|OS | + if member(1) ~= 8 + fclose(fid); + error('Unknown compression method.'); + end + flag = uint8(member(2)); + if bitget(flag,3) % (if FLG.FEXTRA set) + xlen = fread(fid,1,'uint16'); % | XLEN | + fread(fid,xlen,'uint8'); % |...XLEN bytes of "extra field"...| + end + if bitget(flag,4) % (if FLG.FNAME set) + while true + if fread(fid,1,'uint8') == 0 + break; % |...original file name, zero-terminated...| + end + end + end + if bitget(flag,5) % (if FLG.FCOMMENT set) + while true + if fread(fid,1,'uint8') == 0 + break; % |...file comment, zero-terminated...| + end + end + end + if bitget(flag,2) % (if FLG.FHCRC set) + fread(fid,2,'uint8'); % | CRC16 | + end + % |...compressed blocks...| CRC32 | ISIZE | + d = fread(fid,Inf,'*uint8'); + isize = typecast(d(end-3:end),'uint32'); + D = zstream('d',d); + if isize ~= mod(numel(D),2^32) + % size of the uncompressed input data modulo 2^32 + warning('Decompression failed.'); + end +else + fclose(fid); + error('Invalid file %s.',filename); +end + +%-Close file +%-------------------------------------------------------------------------- +fclose(fid); + + +%-Parse data +%========================================================================== +if ~isequal(D(1:2),[77;90]) % 'MZ' + error('Invalid file %s.',filename); +end + +attr = typecast(D(3:4),'uint16'); +Nf = typecast(D(5:8),'uint32'); +Nv = typecast(D(9:12),'uint32'); +Nskip = typecast(D(13:16),'uint32') + 16; % total header size + + if attr > 15 + warning('Unsupported MZ3 format.'); +end + +if bitget(attr,1) % isFace + M.faces = reshape(typecast(D(Nskip+(1:Nf*3*4)),'uint32'),3,[])' + 1; + Nskip = Nskip + Nf*3*4; +end +if bitget(attr,2) % isVert + M.vertices = reshape(typecast(D(Nskip+(1:Nv*3*4)),'single'),3,[])'; + Nskip = Nskip + Nv*3*4; +end +if bitget(attr,3) % isRGBA + M.cdata = reshape(D(Nskip+(1:Nv*4)),4,[])'; + Nskip = Nskip + Nv*4; +end +if bitget(attr,4) % isScalar + %Ns = 1; + %M.cdata = reshape(typecast(D(Nskip+(1:Nv*Ns*4)),'single'),Nv,Ns); + M.cdata = reshape(typecast(D(Nskip+1:end),'single'),Nv,[]); + Ns = size(M.cdata,2); + Nskip = Nskip + Nv*Ns*4; +end diff --git a/@gifti/private/mz3_write.m b/@gifti/private/mz3_write.m new file mode 100644 index 00000000..75bedf58 --- /dev/null +++ b/@gifti/private/mz3_write.m @@ -0,0 +1,72 @@ +function mz3_write(M,filename,fmt) +% Write MZ3-formatted data from disk +% FORMAT mz3_write(M,filename,fmt) +% +% M - data structure +% filename - MZ3 output filename [Default: 'untitled'] +% fmt - compress data [Default: false] +%__________________________________________________________________________ +% +% MZ3 Format Specification: +% https://github.com/neurolabusc/surf-ice/tree/master/mz3 +%__________________________________________________________________________ +% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: mz3_write.m 7470 2018-11-01 17:40:18Z guillaume $ + + +if nargin < 3, fmt = false; end + +%-Open file +%-------------------------------------------------------------------------- +fid = fopen(filename,'wb','ieee-le'); +if fid == -1 + error('Cannot open %s.',filename); +end + +%-Write header +%-------------------------------------------------------------------------- +attr = uint16(0); +Nf = 0; Nv = 0; Ns = 0; +if isfield(M,'faces') + attr = bitset(attr,1); + Nf = size(M.faces,1); +end +if isfield(M,'vertices') + attr = bitset(attr,2); + Nv = size(M.vertices,1); +end +if isfield(M,'cdata') + if isa(M.cdata,'uint8') && size(M.cdata,2) == 4 + attr = bitset(attr,3); + Ns = -1; % RGBA + else + attr = bitset(attr,4); + Ns = size(M.cdata,2); + end +end + +fwrite(fid,23117,'uint16'); % 0x4D5A, 23117, [77 90], 'MZ' +fwrite(fid,attr,'uint16'); +fwrite(fid,Nf,'uint32'); +fwrite(fid,Nv,'uint32'); +fwrite(fid,0,'uint32'); + +%-Write data +%-------------------------------------------------------------------------- +if Nf > 0, fwrite(fid,M.faces'-1,'uint32'); end +if Nv > 0, fwrite(fid,M.vertices','single'); end +if Ns < 0, fwrite(fid,M.cdata','uint8'); end +if Ns > 0, fwrite(fid,M.cdata,'single'); end + +%-Close file +%-------------------------------------------------------------------------- +fclose(fid); + +%-Optional compression +%-------------------------------------------------------------------------- +if fmt + gzip(filename); + movefile([filename '.gz'], filename); +end diff --git a/@gifti/private/obj_read.m b/@gifti/private/obj_read.m index 5d303321..521ca8e2 100644 --- a/@gifti/private/obj_read.m +++ b/@gifti/private/obj_read.m @@ -12,7 +12,7 @@ % Copyright (C) 2017 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: obj_read.m 7004 2017-02-03 10:57:17Z guillaume $ +% $Id: obj_read.m 7390 2018-08-13 09:51:20Z guillaume $ fid = fopen(filename,'rt'); @@ -46,11 +46,11 @@ case 'f' f = sscanf(l(2:end),'%d %d %d'); if numel(f) ~= 3 - f = sscanf(l(2:end),'%d/%d %d/d %d/%d'); + f = sscanf(l(2:end),'%d/%d'); % '%d/%d %d/d %d/%d' if numel(f) ~= 6 - f = sscanf(l(2:end),'%d//%d %d//d %d//%d'); + f = sscanf(l(2:end),'%d//%d'); % '%d//%d %d//d %d//%d' if numel(f) ~= 6 - f = sscanf(l(2:end),'%d/%d/%d %d/%d/%d %d/%d/%d'); + f = sscanf(l(2:end),'%d/%d/%d'); % '%d/%d/%d %d/%d/%d %d/%d/%d' if numel(f) == 9 f = f([1 4 7]); else @@ -74,7 +74,7 @@ case 's' fprintf('Ignoring smooth shading.\n'); otherwise - if ~isempty(strmatch('mtllib',l)) || ~isempty(strmatch('usemtl',l)) + if strncmp('mtllib',l,6) || strncmp('usemtl',l,6) fprintf('Ignoring materials.\n'); else fprintf('Ignoring line starting with %c.\n',l(1)); diff --git a/@gifti/private/ply_read.m b/@gifti/private/ply_read.m new file mode 100644 index 00000000..a8c9bf35 --- /dev/null +++ b/@gifti/private/ply_read.m @@ -0,0 +1,46 @@ +function M = ply_read(filename) +% Read PLY-formatted data from disk +% FORMAT M = ply_read(filename) +% +% filename - PLY-formatted file name +% M - data structure +%__________________________________________________________________________ +% +% Stanford Triangle Format Specification: +% https://en.wikipedia.org/wiki/PLY_%28file_format%29 +%__________________________________________________________________________ +% Copyright (C) 2017 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: ply_read.m 7239 2017-12-15 17:14:33Z guillaume $ + + +fid = fopen(filename,'rt'); +if fid == -1 + error('Cannot open %s.',filename); +end + +M = struct('vertices',[],'faces',[]); + +%-Read header +while true + l = fgetl(fid); + if strcmp(l,'end_header'), break; end + [l,r] = strtok(l); + if strcmp(l,'element') + [l,r] = strtok(r); + switch l + case 'vertex' + nv = str2double(r); + case 'face' + nf = str2double(r); + end + end +end + +%-Read data +M.vertices = fscanf(fid,'%f %f %f',[3 nv])'; +M.faces = fscanf(fid,'%d %d %d %d',[4 nf])'; +M.faces = M.faces(:,2:4) + 1; + +fclose(fid); diff --git a/@gifti/private/read_freesurfer_file.m b/@gifti/private/read_freesurfer_file.m index 00f39d3d..a890ef76 100644 --- a/@gifti/private/read_freesurfer_file.m +++ b/@gifti/private/read_freesurfer_file.m @@ -1,16 +1,34 @@ function this = read_freesurfer_file(filename) -% Low level reader of FreeSurfer files (ASCII triangle surface file) +% Low level reader of FreeSurfer file % FORMAT this = read_freesurfer_file(filename) % filename - FreeSurfer file % -% See http://wideman-one.com/gw/brain/fs/surfacefileformats.htm +% Read ASCII triangle surface file and part of binary mgh file. +% See https://surfer.nmr.mgh.harvard.edu/fswiki/FileFormats %__________________________________________________________________________ % Copyright (C) 2013 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: read_freesurfer_file.m 5322 2013-03-13 15:04:14Z guillaume $ +% $Id: read_freesurfer_file.m 7471 2018-11-02 11:14:39Z guillaume $ +[p,n,e] = fileparts(filename); +switch lower(e) + case {'.asc','.srf'} + this = read_fs_file_ascii(filename); + case {'.mgh','.mgz'} + this.cdata = read_fs_file_mgh(filename); + case {'.pial','.white','.inflated','.nofix','.orig','.smoothwm',... + '.sphere','.reg','.surf'} + this = read_fs_file_mesh(filename); + case {'.curv','.area','.sulc'} + this = read_fs_file_cdata(filename); + otherwise + error('Unknown file format.'); +end + + +function this = read_fs_file_ascii(filename) fid = fopen(filename,'rt'); if fid == -1, error('Cannot open "%s".',filename); end @@ -23,3 +41,113 @@ this.vertices = this.vertices(:,1:3); this.faces = this.faces(:,1:3) + 1; + + +function [dat,hdr] = read_fs_file_mgh(filename) +[p,n,e] = fileparts(filename); +if strcmpi(e,'.mgz') + filename = char(gunzip(filename,tempname)); + c = onCleanup(@()rmdir(fileparts(filename),'s')); +end + +fid = fopen(filename,'r','ieee-be'); +if fid == -1, error('Cannot open "%s".',filename); end + +% version integer current value is 1 +hdr.version = fread(fid,1,'int32'); +% width integer first dimension of the image buffer (fastest) +% height integer second dimension of the image buffer (2nd fastest) +% depth integer slowest dimension when reading the buffer +% nframes integer number of scalar components contained in the buffer +hdr.dim = fread(fid,4,'int32')'; +% type integer data type of the image buffer: 0 (UCHAR), 4 (SHORT), 1 (INT) or 3 (FLOAT) +hdr.type = fread(fid,1,'int32'); +% dof integer degrees of freedom (where appropriate) +hdr.dof = fread(fid,1,'int32'); +% goodRASFlag short if true, the direction cosines are in the header +% if false, a CORONAL orientation will be assumed +hdr.goodRASFlag = fread(fid,1,'int16'); +hdr.spacing = [1 1 1]'; +hdr.xyz = [-1 0 0;0 0 1;0 -1 0]; +hdr.c = [0 0 0]'; +if hdr.goodRASFlag + % spacing X float spacing in the X direction (ranging [0...width]) - default is 1 + % spacing Y float spacing in the Y direction (ranging [0...height]) - default is 1 + % spacing Z float spacing in the Z direction (ranging [0...depth]) - default is 1 + hdr.spacing = fread(fid,3,'float32'); + % xr float default is -1 + % xa float default is 0 + % xs float default is 0 + % yr float default is 0 + % ya float default is 0 + % ys float default is -1 + % zr float default is 0 + % za float default is 1 + % zs float default is 0 + hdr.xyz = reshape(fread(fid,9,'float32'),[3 3]); + % cr float default is 0 + % ca float default is 0 + % cs float default is 0 + hdr.c = fread(fid,3,'float32'); +end +% The image data starts at a specified offset from the beginning of the file, +% which is currently byte 284 (bytes 0-283 are header) +fseek(fid,284,'bof'); +dt = {'uchar', 'int32', NaN, 'float32', 'int16'}; +dat = fread(fid,prod(hdr.dim),dt{hdr.type+1}); +dat = reshape(dat,hdr.dim); + +% Immediately after the data buffer can be found optional data structures +% TR float milliseconds +% FlipAngle float radians +% TE float milliseconds +% TI float milliseconds +if ~feof(fid) + hdr.params = fread(fid,4,'float32'); +end +% tags variable length char strings +if ~feof(fid) + % hdr.tags = fread(fid,Inf,'uchar'); +end + +fclose(fid); + + +function this = read_fs_file_mesh(filename) +fid = fopen(filename,'r','ieee-be'); +if fid == -1, error('Cannot open "%s".',filename); end + +magic = fread(fid,3,'uchar'); +magic = [65536 256 1]*magic; +if magic ~= 16777214 + fclose(fid); + error('File format not supported.'); +end +fgets(fid); fgets(fid); +nv = fread(fid,1,'int32'); +nf = fread(fid,1,'int32'); +this.vertices = fread(fid,3*nv,'float32'); +this.vertices = reshape(this.vertices,3,nv)'; +this.faces = fread(fid,3*nf,'int32'); +this.faces = reshape(this.faces,3,nf)' + 1; + +fclose(fid); + + +function this = read_fs_file_cdata(filename) +fid = fopen(filename,'r','ieee-be'); +if fid == -1, error('Cannot open "%s".',filename); end + +magic = fread(fid,3,'uchar'); +magic = [65536 256 1]*magic; +if magic ~= 16777215 + fclose(fid); + error('File format not supported.'); +end + +nv = fread(fid,1,'int32'); +nf = fread(fid,1,'int32'); +n = fread(fid,1,'int32'); +this.cdata = fread(fid,[n nv],'float32')'; + +fclose(fid) ; diff --git a/@gifti/private/read_gifti_file.m b/@gifti/private/read_gifti_file.m index b11d30d4..e4d09129 100644 --- a/@gifti/private/read_gifti_file.m +++ b/@gifti/private/read_gifti_file.m @@ -7,7 +7,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: read_gifti_file.m 6895 2016-10-03 11:08:49Z guillaume $ +% $Id: read_gifti_file.m 7390 2018-08-13 09:51:20Z guillaume $ % Import XML-based GIfTI file %-------------------------------------------------------------------------- @@ -119,7 +119,12 @@ case 'CoordinateSystemTransformMatrix' s(1).space(end+1) = gifti_Space(t,c(i)); case 'Data' - s(1).data = gifti_Data(t,c(i),s(1).attributes); + if isfield(s(1).attributes,'Dim') && isequal(s(1).attributes.Dim,[0 0]) + warning('Ignoring empty DataArray'); + s(1).data = []; + else + s(1).data = gifti_Data(t,c(i),s(1).attributes); + end otherwise error('[GIFTI] Unknown DataArray element "%s".',get(t,c(i),'name')); end @@ -155,8 +160,8 @@ [unused,unused,mach] = fopen(1); sb = @(x) x; try - if (strcmp(s.Endian,'LittleEndian') && ~isempty(strmatch('ieee-be',mach))) ... - || (strcmp(s.Endian,'BigEndian') && ~isempty(strmatch('ieee-le',mach))) + if (strcmp(s.Endian,'LittleEndian') && strncmp('ieee-be',mach,7)) ... + || (strcmp(s.Endian,'BigEndian') && strncmp('ieee-le',mach,7)) sb = @swapbyte; end catch diff --git a/@gifti/private/stl_read.m b/@gifti/private/stl_read.m new file mode 100644 index 00000000..83d2216d --- /dev/null +++ b/@gifti/private/stl_read.m @@ -0,0 +1,83 @@ +function M = stl_read(filename) +% Read STL-formatted data from disk +% FORMAT M = stl_read(filename) +% +% filename - STL-formatted file name +% M - data structure +%__________________________________________________________________________ +% +% STL Format Specification: +% https://en.wikipedia.org/wiki/STL_%28file_format%29 +%__________________________________________________________________________ +% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: stl_read.m 7254 2018-02-05 17:51:37Z guillaume $ + + +M = struct('vertices',[],'faces',[]); + +%-Detect ASCII vs Binary STL +%-------------------------------------------------------------------------- +fid = fopen(filename,'rt'); +if fid == -1 + error('Cannot open %s.',filename); +end +hdr = fscanf(fid,'%c',80); +fclose(fid); + +%-ASCII STL +%-------------------------------------------------------------------------- +if strncmp(hdr,'solid',5) + % solid name + % facet normal ni nj nk + % outer loop + % vertex v1x v1y v1z + % vertex v2x v2y v2z + % vertex v3x v3y v3z + % endloop + % endfacet + % endsolid name + fid = fopen(filename,'rt'); + while true + tline = fgetl(fid); + if ~ischar(tline), break, end + tline = strtrim(tline); + switch strtok(tline) + case {'solid','facet','outer','endloop','endfacet'} + case 'vertex' + tri = sscanf(tline,'vertex %f %f %f'); + M.vertices = [M.vertices tri]; + end + end + fclose(fid); + M.vertices = M.vertices'; + M.faces = reshape(1:size(M.vertices,1),3,[])'; + return; +end + +%-Binary STL +%-------------------------------------------------------------------------- +fid = fopen(filename,'r','ieee-le'); +% INT8[80] – Header +fread(fid,80,'uchar'); +% UINT32 – Number of triangles +nf = fread(fid,1,'uint32'); +M.vertices = zeros(3,3*nf); +M.faces = zeros(3,nf); +% foreach triangle +% REAL32[3] – Normal vector +% REAL32[3] – Vertex 1 +% REAL32[3] – Vertex 2 +% REAL32[3] – Vertex 3 +% UINT16 – Attribute byte count +% end +for i=1:nf + d = fread(fid,12,'float32'); + fread(fid,1,'uint16'); + M.vertices((1:9)+9*(i-1)) = d(4:end); + M.faces((1:3)+3*(i-1)) = (1:3)+3*(i-1); +end +fclose(fid); +M.vertices = M.vertices'; +M.faces = M.faces'; diff --git a/@gifti/private/zstream.c b/@gifti/private/zstream.c index d8cfd280..120d77a3 100644 --- a/@gifti/private/zstream.c +++ b/@gifti/private/zstream.c @@ -1,5 +1,5 @@ /* - * $Id: zstream.c 6417 2015-04-21 16:03:44Z guillaume $ + * $Id: zstream.c 7400 2018-08-17 13:53:33Z guillaume $ * Guillaume Flandin */ @@ -8,7 +8,7 @@ /* setenv CFLAGS "`mkoctfile -p CFLAGS` -std=c99" */ /* mkoctfile --mex zstream.c */ -/* miniz: http://code.google.com/p/miniz/ */ +/* miniz: https://github.com/richgel999/miniz */ #define MINIZ_NO_STDIO #define MINIZ_NO_ARCHIVE_APIS #define MINIZ_NO_TIME @@ -24,6 +24,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) char *action; unsigned char *IN = NULL, *OUT = NULL; size_t INlen, OUTlen; +int flag = 0; /* Check for proper number of arguments */ if (nrhs < 2) @@ -45,17 +46,25 @@ if (!mxIsUint8(prhs[1]) || mxIsComplex(prhs[1])) INlen = mxGetNumberOfElements(prhs[1]); IN = mxGetData(prhs[1]); +/* zlib stream (zlib header with adler32 checksum) or raw deflate stream */ if (!strcmp(action,"D")) { + flag = TINFL_FLAG_PARSE_ZLIB_HEADER; +} +else if (!strcmp(action,"C")) { + flag = TDEFL_WRITE_ZLIB_HEADER; +} + +if (!strcmp(action,"D") || !strcmp(action,"d")) { /* Decompress data */ - OUT = tinfl_decompress_mem_to_heap(IN, INlen, &OUTlen, TINFL_FLAG_PARSE_ZLIB_HEADER); + OUT = tinfl_decompress_mem_to_heap(IN, INlen, &OUTlen, flag); if (OUT == NULL) mexErrMsgTxt("Error when decompressing data."); } -else if (!strcmp(action,"C")) { +else if (!strcmp(action,"C") || !strcmp(action,"c")) { /* Compress data */ - OUT = tdefl_compress_mem_to_heap(IN, INlen, &OUTlen, TDEFL_WRITE_ZLIB_HEADER); + OUT = tdefl_compress_mem_to_heap(IN, INlen, &OUTlen, flag); if (OUT == NULL) mexErrMsgTxt("Error when compressing data."); @@ -64,7 +73,7 @@ else { mexErrMsgTxt("Unknown ACTION type."); } -/* */ +/* Store output */ plhs[0] = mxCreateNumericMatrix(OUTlen,1,mxUINT8_CLASS,mxREAL); if (plhs[0] == NULL) mexErrMsgTxt("Error when creating output variable."); diff --git a/@gifti/private/zstream.m b/@gifti/private/zstream.m index 2da8c43c..4eaa596f 100644 --- a/@gifti/private/zstream.m +++ b/@gifti/private/zstream.m @@ -6,19 +6,23 @@ % FORMAT D = zstream('D',Z) % Z - data stream to decompress (uint8 vector) % D - decompressed data stream (uint8 vector) +% +% If action is upper case ('C','D'), a zlib stream is used (zlib header +% with an adler32 checksum). Otherwise, if action is lower case ('c','d'), +% a raw deflate stream is assumed. %__________________________________________________________________________ % % This C-MEX file relies on: % * miniz, by Rich Geldreich -% http://code.google.com/p/miniz/ +% https://github.com/richgel999/miniz % Fallback Java implementation is adapted from: % * dzip/dunzip, by Michael Kleder -% http://www.mathworks.com/matlabcentral/fileexchange/8899 +% https://www.mathworks.com/matlabcentral/fileexchange/8899 %__________________________________________________________________________ -% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2015-2018 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: zstream.m 6417 2015-04-21 16:03:44Z guillaume $ +% $Id: zstream.m 7399 2018-08-16 12:04:14Z guillaume $ if exist('OCTAVE_VERSION','builtin') @@ -29,7 +33,7 @@ case 'C' D = typecast(D(:),'uint8'); f = java.io.ByteArrayOutputStream(); - g = java.util.zip.DeflaterOutputStream(f); + g = java.util.zip.DeflaterOutputStream(f,java.util.zip.Inflater(action~='C')); g.write(D); g.close; Z = typecast(f.toByteArray,'uint8'); @@ -38,7 +42,7 @@ case 'D' import com.mathworks.mlwidgets.io.InterruptibleStreamCopier a = java.io.ByteArrayInputStream(D); - b = java.util.zip.InflaterInputStream(a); + b = java.util.zip.InflaterInputStream(a,java.util.zip.Inflater(action~='D')); isc = InterruptibleStreamCopier.getInterruptibleStreamCopier; c = java.io.ByteArrayOutputStream; isc.copyStream(b,c); diff --git a/@gifti/private/zstream.mexa64 b/@gifti/private/zstream.mexa64 index fa41125b..59f38ef4 100755 Binary files a/@gifti/private/zstream.mexa64 and b/@gifti/private/zstream.mexa64 differ diff --git a/@gifti/private/zstream.mexmaci64 b/@gifti/private/zstream.mexmaci64 index 9324133a..6e452fed 100755 Binary files a/@gifti/private/zstream.mexmaci64 and b/@gifti/private/zstream.mexmaci64 differ diff --git a/@gifti/private/zstream.mexw32 b/@gifti/private/zstream.mexw32 index 8153a1d4..e21ed08b 100644 Binary files a/@gifti/private/zstream.mexw32 and b/@gifti/private/zstream.mexw32 differ diff --git a/@gifti/private/zstream.mexw64 b/@gifti/private/zstream.mexw64 index cd58fb3b..dafd7da8 100644 Binary files a/@gifti/private/zstream.mexw64 and b/@gifti/private/zstream.mexw64 differ diff --git a/@gifti/saveas.m b/@gifti/saveas.m index 3f81a381..4064ec15 100644 --- a/@gifti/saveas.m +++ b/@gifti/saveas.m @@ -4,12 +4,14 @@ function saveas(this,filename,format) % this - GIfTI object % filename - name of file to be created [Default: 'untitled.vtk'] % format - optional argument to specify encoding format, among -% VTK (.vtk,.vtp), Collada (.dae), IDTF (.idtf). [Default: VTK] +% VTK (.vtk,.vtp), Collada (.dae), IDTF (.idtf), Wavefront OBJ +% (.obj), JavaScript (.js), JSON (.json), FreeSurfer +% (.surf,.curv), MZ3 (.mz3) [Default: VTK] %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: saveas.m 7004 2017-02-03 10:57:17Z guillaume $ +% $Id: saveas.m 7470 2018-11-01 17:40:18Z guillaume $ % Check filename and file format @@ -18,6 +20,12 @@ function saveas(this,filename,format) if nargin == 1 filename = ['untitled' ext]; else + if nargin == 3 && strcmpi(format,'js') + ext = '.js'; + end + if nargin == 3 && strcmpi(format,'json') + ext = '.json'; + end if nargin == 3 && strcmpi(format,'collada') ext = '.dae'; end @@ -28,10 +36,18 @@ function saveas(this,filename,format) format = lower(format(5:end)); ext = '.vtk'; end - if nargin == 3 && strncmpi(format,'obj',3) - format = lower(format(5:end)); + if nargin == 3 && strcmpi(format,'obj') ext = '.obj'; end + if nargin == 3 && strcmpi(format,'curv') + ext = '.curv'; + end + if nargin == 3 && strcmpi(format,'surf') + ext = '.surf'; + end + if nargin == 3 && strcmpi(format,'mz3') + ext = '.mz3'; + end [p,f,e] = fileparts(filename); if strcmpi(e,'.gii') warning('Use save instead of saveas.'); @@ -50,6 +66,8 @@ function saveas(this,filename,format) s = struct(this); switch ext + case {'.js','.json'} + save_js(s,filename); case '.dae' save_dae(s,filename); case '.idtf' @@ -59,11 +77,79 @@ function saveas(this,filename,format) mvtk_write(s,filename,format); case '.obj' save_obj(s,filename); + case {'.curv','.surf'} + write_freesurfer_file(s,filename,format); + case '.mz3' + mz3_write(s,filename,true); otherwise error('Unknown file format.'); end +%========================================================================== +% function save_js(s,filename) +%========================================================================== +function save_js(s,filename) + +% Vertices & faces +%-------------------------------------------------------------------------- +trace = struct(... + 'type','mesh3d',... + 'x',s.vertices(:,1),... + 'y',s.vertices(:,2),... + 'z',s.vertices(:,3),... + 'i',s.faces(:,1)-1,... + 'j',s.faces(:,2)-1,... + 'k',s.faces(:,3)-1); +if isfield(s,'cdata') && ~isempty(s.cdata) + if size(s.cdata,2) == 1 + trace.intensity = s.cdata(:,1); + elseif size(s.cdata,2) == 3 + trace.vertexcolor = cell(1,size(trace.x,1)); + for i=1:numel(trace.vertexcolor) + trace.vertexcolor{i} = sprintf('rgb(%d,%d,%d)',floor(s.cdata(i,:)*255)); + end + end +end +data = {trace}; + +%-Save as JS variable or JSON file +%-------------------------------------------------------------------------- +if filename(end) == 's' % JS + if exist('jsonencode','builtin') + data = jsonencode(data); + else + data = spm_jsonwrite(data); + end + fid = fopen(filename,'wt'); + if fid == -1 + error('Unable to write file %s: permission denied.',filename); + end + fprintf(fid,'/*\n * JavaScript file saved by %s\n */\n\n','SPM'); % spm('Version') + fprintf(fid,'/*\n \n'); + fprintf(fid,' \n','https://cdn.plot.ly/plotly-latest.min.js'); + fprintf(fid,' \n'); + fprintf(fid,'
\n','plotly',650,800); + fprintf(fid,' \n'); + fprintf(fid,' \n','plotly'); + fprintf(fid,' \n*/\n'); + fclose(fid); +else % JSON + if exist('jsonencode','builtin') + fid = fopen(filename,'wt'); + if fid == -1 + error('Unable to write file %s: permission denied.',filename); + end + fprintf(fid,'%s',jsonencode(struct('data',{data},'layout',struct()))); + fclose(fid); + else + spm_jsonwrite(filename,struct('data',{data},'layout',struct()),struct('indent',' ')); + end +end + + %========================================================================== % function save_dae(s,filename) %========================================================================== @@ -385,10 +471,46 @@ function save_obj(s,filename) % Vertices & faces %-------------------------------------------------------------------------- -fprintf(fid,'# Wavefront OBJ file saved by %s\n',spm('Version')); +fprintf(fid,'# Wavefront OBJ file saved by %s\n','SPM'); % spm('Version') fprintf(fid,'v %f %f %f\n',s.vertices'); fprintf(fid,'f %d %d %d\n',s.faces'); % Close file %-------------------------------------------------------------------------- fclose(fid); + + +%========================================================================== +% function write_freesurfer_file(s,filename,format) +%========================================================================== +function write_freesurfer_file(s,filename,format) + +switch lower(format) + case 'surf' + fid = fopen(filename,'wb','ieee-be'); + if fid == -1, error('Cannot open "%s".',filename); end + fwrite(fid,[255 255 254],'uchar'); + fprintf(fid,'created by @gifti on %s\n\n',... + datestr(now,'ddd mmm dd HH:MM:SS yyyy')); + fwrite(fid,size(s.vertices,1),'int32'); % nv + fwrite(fid,size(s.faces,1),'int32'); % nf + fwrite(fid,s.vertices','float32'); + fwrite(fid,s.faces'-1,'int32'); + fclose(fid); + case 'curv' + fid = fopen(filename,'wb','ieee-be'); + if fid == -1, error('Cannot open "%s".',filename); end + fwrite(fid,[255 255 255],'uchar'); + fwrite(fid,size(s.cdata,1),'int32'); % nv + if isfield(s,'faces') + nf = size(s.faces,1); + else + nf = 0; + end + fwrite(fid,nf,'int32'); % nf + fwrite(fid,size(s.cdata,2),'int32'); % n + fwrite(fid,s.cdata','float32'); + fclose(fid); + otherwise + error('File format not supported.'); +end diff --git a/@gifti/subsref.m b/@gifti/subsref.m index 0c7c0823..3514ae0c 100644 --- a/@gifti/subsref.m +++ b/@gifti/subsref.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: subsref.m 6345 2015-02-20 12:25:50Z guillaume $ +% $Id: subsref.m 7291 2018-04-12 08:43:08Z guillaume $ if length(this) > 1 && ~strcmp(subs(1).type,'()') warning('Not implemented.'); @@ -42,7 +42,7 @@ end end if strcmp(subs(1).subs,'faces') || strcmp(subs(1).subs,'indices') - varargout{1} = varargout{1} + 1; % indices start at 1 + varargout{1} = varargout{1}() + 1; % indices start at 1 end if length(subs) > 1 varargout{1} = subsref(varargout{1},subs(2:end)); diff --git a/@meeg/clone.m b/@meeg/clone.m index 3e2ca061..e072ac2f 100644 --- a/@meeg/clone.m +++ b/@meeg/clone.m @@ -1,9 +1,12 @@ -function new = clone(this, fnamedat, dim, reset) +function new = clone(this, fnamedat, dim, reset, forcefloat) % Creates a copy of the object with a new, empty data file, % possibly changing dimensions % FORMAT new = clone(this, fnamedat, dim, reset) % reset - 0 (default) do not reset channel or trial info unless dimensions % change, 1 - reset channels only, 2 - trials only, 3 both +% forcefloat - force the new data file to be float (0 by default) +% this is to fix an issue with TF analysis of files using int16 +% for the raw data % Note that when fnamedat comes with a path, the cloned meeg object uses % it. Otherwise, its path is by definition that of the meeg object to be % cloned. @@ -11,7 +14,11 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel, Vladimir Litvak -% $Id: clone.m 6829 2016-07-07 10:16:46Z vladimir $ +% $Id: clone.m 7449 2018-10-16 13:52:04Z vladimir $ + +if nargin < 5 + forcefloat = 0; +end if nargin < 4 reset = 0; @@ -43,18 +50,26 @@ d.fname = newFileName; dim_o = d.dim; -% This takes care of an issue specific to int data files which are not -% officially supported in SPM8/12. -if dim(1)>dim_o(1) && length(d.scl_slope)>1 - % adding channel to montage and scl_slope defined for old montage - % -> need to increase scl_slope - v_slope = mode(d.scl_slope); - if length(v_slope)>1 - warning(['Trying to guess the scaling factor for new channels.',... - ' This might be not exact.']); +if forcefloat + d.dtype = 'FLOAT32-LE'; + d.offset = 0; + d.scl_slope =[]; + d.scl_inter =[]; +else + % This takes care of an issue specific to int data files which are not + % officially supported in SPM8/12. + if dim(1)>dim_o(1) && length(d.scl_slope)>1 + % adding channel to montage and scl_slope defined for old montage + % -> need to increase scl_slope + v_slope = mode(d.scl_slope); + if length(v_slope)>1 + warning(['Trying to guess the scaling factor for new channels.',... + ' This might be not exact.']); + end + d.scl_slope = [d.scl_slope' ones(1,dim(1)-dim_o(1))*v_slope]'; end - d.scl_slope = [d.scl_slope' ones(1,dim(1)-dim_o(1))*v_slope]'; end + d.dim = dim; % physically initialise file @@ -88,6 +103,16 @@ for i = 1:dim(1) new.channels(i).label = ['Ch' num2str(i)]; end +elseif montage(this, 'getindex') + new.channels = []; + lbl = chanlabels(this); + for i = 1:dim(1) + new.channels(i).label = lbl{i}; + end + new = chantype(new, ':', chantype(this)); + new = units(new, ':', units(this)); + new = badchannels(new, ':', badchannels(this)); + new = coor2D(new, ':', coor2D(this)); end if ntrial ~= ntrials(this) || ismember(reset, [2 3]) diff --git a/@meeg/coor2D.m b/@meeg/coor2D.m index 6cc11a86..525e366e 100644 --- a/@meeg/coor2D.m +++ b/@meeg/coor2D.m @@ -5,7 +5,7 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak, Laurence Hunt -% $Id: coor2D.m 5933 2014-03-28 13:22:28Z vladimir $ +% $Id: coor2D.m 7445 2018-10-12 13:24:48Z vladimir $ megind = indchantype(this, {'MEG', 'MEGPLANAR', 'MEGCOMB'}); @@ -39,12 +39,26 @@ if nargin < 3 || isempty(val) if ~isempty(intersect(ind, megind)) - if ~any(cellfun('isempty', {this.channels(megind).X_plot2D this.channels(megind).Y_plot2D})) - meg_xy = [this.channels(megind).X_plot2D; this.channels(megind).Y_plot2D]; - elseif all(cellfun('isempty', {this.channels(megind).X_plot2D this.channels(megind).Y_plot2D})) - meg_xy = grid(length(megind)); + + if this.montage.Mind==0 + if ~any(cellfun('isempty', {this.channels(megind).X_plot2D this.channels(megind).Y_plot2D})) + meg_xy = [this.channels(megind).X_plot2D; this.channels(megind).Y_plot2D]; + elseif all(cellfun('isempty', {this.channels(megind).X_plot2D this.channels(megind).Y_plot2D})) + meg_xy = grid(length(megind)); + else + error('Either all or none of MEG channels should have 2D coordinates defined.'); + end else - error('Either all or none of MEG channels should have 2D coordinates defined.'); + if ~any(cellfun('isempty', {this.montage.M(this.montage.Mind).channels(megind).X_plot2D ... + this.montage.M(this.montage.Mind).channels(megind).Y_plot2D})) + meg_xy = [this.montage.M(this.montage.Mind).channels(megind).X_plot2D; ... + this.montage.M(this.montage.Mind).channels(megind).Y_plot2D]; + elseif all(cellfun('isempty', {this.montage.M(this.montage.Mind).channels(megind).X_plot2D... + this.montage.M(this.montage.Mind).channels(megind).Y_plot2D})) + meg_xy = grid(length(megind)); + else + error('Either all or none of EEG channels should have 2D coordinates defined.'); + end end end diff --git a/@meeg/selectchannels.m b/@meeg/selectchannels.m index ef6d81f8..ec7028c6 100644 --- a/@meeg/selectchannels.m +++ b/@meeg/selectchannels.m @@ -10,7 +10,7 @@ % Copyright (C) 2010-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: selectchannels.m 6535 2015-08-25 11:45:26Z vladimir $ +% $Id: selectchannels.m 7253 2018-02-04 17:20:57Z vladimir $ if ischar(channels) channels = {channels}; @@ -18,17 +18,20 @@ chanind = []; -for i = 1:numel(channels) - if ismember(upper(channels{i}), ... - {'ALL','MEG', 'MEGPLANAR', 'MEGMAG', 'MEGGRAD', 'MEGCOMB','EEG',... - 'EOG', 'ECG', 'EMG', 'LFP', 'SRC', 'PHYS', 'ILAM', 'OTHER', 'REF', 'REFMAG', 'REFGRAD'}) - chanind = [chanind indchantype(this, upper(channels{i}))]; - elseif strncmpi('regexp_', channels{i}, 7) +for i = 1:numel(channels) + if strncmpi('regexp_', channels{i}, 7) re = channels{i}(8:end); match = regexp(chanlabels(this), re); chanind = [chanind find(~cellfun('isempty', match))]; else - chanind = [chanind indchannel(this, channels{i})]; + cind = indchannel(this, channels{i}); + if ~isempty(cind) + chanind = [chanind cind]; + elseif ismember(upper(channels{i}), ... + {'ALL','MEG', 'MEGPLANAR', 'MEGMAG', 'MEGGRAD', 'MEGCOMB','EEG',... + 'EOG', 'ECG', 'EMG', 'LFP', 'SRC', 'PHYS', 'ILAM', 'OTHER', 'REF', 'REFMAG', 'REFGRAD'}) + chanind = [chanind indchantype(this, upper(channels{i}))]; + end end if any(size(chanind) == 0) diff --git a/@nifti/private/read_extras.m b/@nifti/private/read_extras.m index dfeb94a9..d14cbf7e 100644 --- a/@nifti/private/read_extras.m +++ b/@nifti/private/read_extras.m @@ -1,10 +1,10 @@ function extras = read_extras(fname) % Read extra bits of information %__________________________________________________________________________ -% Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2005-2018 Wellcome Trust Centre for Neuroimaging % -% $Id: read_extras.m 7147 2017-08-03 14:07:01Z spm $ +% $Id: read_extras.m 7370 2018-07-09 10:44:51Z guillaume $ extras = struct; @@ -18,7 +18,13 @@ mname = fullfile(pth,[nam '.mat']); end -if spm_existfile(mname) +try + is_extra = spm_existfile(mname); +catch + is_extra = exist(mname,'file') > 0; +end + +if is_extra try extras = load(mname); catch diff --git a/@nifti/private/read_hdr.m b/@nifti/private/read_hdr.m index 1f252adc..fcd8f8d0 100644 --- a/@nifti/private/read_hdr.m +++ b/@nifti/private/read_hdr.m @@ -7,13 +7,17 @@ % Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging % -% $Id: read_hdr.m 7147 2017-08-03 14:07:01Z spm $ +% $Id: read_hdr.m 7370 2018-07-09 10:44:51Z guillaume $ persistent d if isempty(d), d = getdict; end -[pth,nam,ext] = spm_fileparts(fname); +try + [pth,nam,ext] = spm_fileparts(fname); +catch + [pth,nam,ext] = fileparts(fname); +end switch ext case {'.hdr','.img'} hname = fullfile(pth,[nam '.hdr']); diff --git a/@nifti/private/write_hdr_raw.m b/@nifti/private/write_hdr_raw.m index 57477fc8..41519766 100644 --- a/@nifti/private/write_hdr_raw.m +++ b/@nifti/private/write_hdr_raw.m @@ -10,7 +10,7 @@ % Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging % -% $Id: write_hdr_raw.m 7147 2017-08-03 14:07:01Z spm $ +% $Id: write_hdr_raw.m 7370 2018-07-09 10:44:51Z guillaume $ [pth,nam] = fileparts(fname); @@ -46,7 +46,12 @@ end sts = true; -if spm_existfile(hname) +try + is_file = spm_existfile(hname); +catch + is_file = exist(hname,'file') > 0; +end +if is_file [fp,msg] = fopen(hname,'r+',mach); else [fp,msg] = fopen(hname,'w+',mach); diff --git a/@nifti/subsasgn.m b/@nifti/subsasgn.m index 8fcf2aeb..6b809d64 100644 --- a/@nifti/subsasgn.m +++ b/@nifti/subsasgn.m @@ -5,7 +5,7 @@ % Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging % -% $Id: subsasgn.m 7147 2017-08-03 14:07:01Z spm $ +% $Id: subsasgn.m 7370 2018-07-09 10:44:51Z guillaume $ switch subs(1).type @@ -351,7 +351,7 @@ sz = [sz 1 1 1 1 1 1 1]; sz = sz(1:7); use_nifti2 = obj.hdr.sizeof_hdr ~= 348; % i.e. == 540 - if any(sz > spm_type('int16','maxval')) && ~use_nifti2 + if any(sz > 32767) && ~use_nifti2 % spm_type('int16','maxval') = 32767 warning('Image dimensions are too large for NIfTI-1 format.'); obj.hdr = empty_hdr('nifti2'); % should also copy modified fields use_nifti2 = true; diff --git a/@xmltree/private/xml_findstr.mexw32 b/@xmltree/private/xml_findstr.mexw32 index 536ae560..be1ce98b 100755 Binary files a/@xmltree/private/xml_findstr.mexw32 and b/@xmltree/private/xml_findstr.mexw32 differ diff --git a/@xmltree/private/xml_findstr.mexw64 b/@xmltree/private/xml_findstr.mexw64 index 9f3950da..34b1a0a6 100755 Binary files a/@xmltree/private/xml_findstr.mexw64 and b/@xmltree/private/xml_findstr.mexw64 differ diff --git a/Contents.m b/Contents.m index 3e69b70c..6793fdce 100644 --- a/Contents.m +++ b/Contents.m @@ -1,10 +1,10 @@ % Statistical Parametric Mapping -% Version 7219 (SPM12) 16-Nov-17 +% Version 7487 (SPM12) 14-Nov-18 %__________________________________________________________________________ % ___ ____ __ __ % / __)( _ \( \/ ) % \__ \ )___/ ) ( Statistical Parametric Mapping -% (___/(__) (_/\/\_) SPM - http://www.fil.ion.ucl.ac.uk/spm/ +% (___/(__) (_/\/\_) SPM - https://www.fil.ion.ucl.ac.uk/spm/ %__________________________________________________________________________ % % This Contents.m file holds the version ID for this release of SPM. @@ -31,13 +31,13 @@ % FOR A PARTICULAR PURPOSE. See the GNU General Public License for more % details. % You should have received a copy of the GNU General Public License along -% with SPM, in LICENCE.txt. If not, see . +% with SPM, in LICENCE.txt. If not, see . % -% See README.txt for details of this release. +% See README.md for details of this release. %__________________________________________________________________________ -% Copyright (C) 1991,1994-2017 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1991,1994-2018 Wellcome Trust Centre for Neuroimaging -% $Id: Contents.m 7218 2017-11-16 10:03:14Z guillaume $ +% $Id: Contents.m 7486 2018-11-14 11:10:29Z guillaume $ %========================================================================== % PROGRAMMERS NOTE: diff --git a/LICENCE.txt b/LICENCE.txt index 95a6b4e8..57a58041 100644 --- a/LICENCE.txt +++ b/LICENCE.txt @@ -359,7 +359,7 @@ % library. If this is what you want to do, use the GNU Library General % Public License instead of this License. %__________________________________________________________________________ -% Copyright (C) 1991,1994-2017 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1991,1994-2018 Wellcome Trust Centre for Neuroimaging % The FIL Methods Group -% $Id: LICENCE.txt 6980 2017-01-04 10:10:29Z guillaume $ +% $Id: LICENCE.txt 7241 2018-01-03 17:45:42Z guillaume $ diff --git a/README.md b/README.md new file mode 100644 index 00000000..de348c02 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +``` + ___ ____ __ __ + / __)( _ \( \/ ) + \__ \ )___/ ) ( Statistical Parametric Mapping + (___/(__) (_/\/\_) SPM - https://www.fil.ion.ucl.ac.uk/spm/ +``` + +This README gives a brief introduction to the SPM software. Full details can be found +on the [SPM website](https://www.fil.ion.ucl.ac.uk/spm/). + +See also `Contents.m`, `AUTHORS.txt` and `LICENCE.txt`. + +# SPM + +Statistical Parametric Mapping refers to the construction and assessment of spatially +extended statistical process used to test hypotheses about functional imaging data. +These ideas have been instantiated in software that is called SPM. The SPM software +package has been designed for the analysis of brain imaging data sequences. The +sequences can be a series of images from different cohorts, or time-series from the +same subject. The current release is designed for the analysis of fMRI, PET, SPECT, +EEG and MEG. + +Please refer to this version as "**SPM12**" in papers and communications. + +SPM was written to organise and interpret our data (at The Wellcome Centre for Human +Neuroimaging). The distributed version is the same as that we use ourselves. + +SPM is made freely available to the [neuro]imaging community, to promote +collaboration and a common analysis scheme across laboratories. + +# Software + +The SPM software is a suite of MATLAB functions, scripts and data files, with some +externally compiled C routines, implementing Statistical Parametric Mapping. MATLAB, +a commercial engineering mathematics package, is required to use SPM. MATLAB is +produced by [MathWorks, Natick, MA, USA](https://www.mathworks.com/). + +SPM requires only core MATLAB to run (no special toolboxes are required). + +SPM12 is written for MATLAB version 7.4 (R2007a) onwards under Windows, Linux and Mac +(SPM12 will not work with versions of MATLAB prior to 7.4). Binaries of the external +C-MEX routines are provided for Windows, Linux and Mac. The source code is supplied +and can be compiled with a C compiler (Makefile provided). + +See https://www.fil.ion.ucl.ac.uk/spm/software/spm12/ for details. + +Later versions of MATLAB (released after SPM12), will probably need additional +patches in order to run. Once developed, these will be made available from: +https://www.fil.ion.ucl.ac.uk/spm/download/spm12_updates/ + +Although SPM12 will read image files from previous versions of SPM, there are +differences in the algorithms, templates and models used. Therefore, we recommend +you use a single SPM version for any given project. + +The SPM12 Release Notes can be found online: +https://www.fil.ion.ucl.ac.uk/spm/software/spm12/ + +# File format + +SPM12 uses the NIFTI-1 data format as standard. Take a look at +https://nifti.nimh.nih.gov/ for more information on the NIFTI-1 file format. + +The old SPM2 version of Analyze format can be read straight into SPM12, but results +will be written out as NIFTI-1. If you still use this format, then it is important +that you ensure that `spm_flip_analyze_images` has been set appropriately for your +data. + +The MINC and ECAT7 formats can not be read straight into SPM12, although conversion +utilities have been provided. Similarly, a number of DICOM flavours can also be +converted to NIFTI-1 using tools in SPM12. + +# Resources + +The SPM website is the central repository for SPM resources: +https://www.fil.ion.ucl.ac.uk/spm/ + +Introductory material, installation details, documentation, course details and +patches are published on the site. + +There is an SPM email discussion list, hosted at . The list is +monitored by the authors, and discusses theoretical, methodological and practical +issues of Statistical Parametric Mapping and SPM. The SPM website has further +details: +https://www.fil.ion.ucl.ac.uk/spm/support/ + +Please report bugs to the authors at . + +Peculiarities may actually be features, and should be raised on the SPM email +discussion list, . + +# Authors + +SPM is developed under the auspices of Functional Imaging Laboratory (FIL), The +Wellcome Centre for Human NeuroImaging, in the Queen Square Institute of Neurology at +University College London (UCL), UK. + +SPM94 was written primarily by Karl Friston in the first half of 1994, with +assistance from John Ashburner (MRC-CU), Jon Heather (WDoIN), and Andrew Holmes +(Department of Statistics, University of Glasgow). Subsequent development, under the +direction of Prof. Karl Friston at the Wellcome Department of Imaging Neuroscience, +has benefited from substantial input (technical and theoretical) from: John Ashburner +(WDoIN), Andrew Holmes (WDoIN & Robertson Centre for Biostatistics, University of +Glasgow, Scotland), Jean-Baptiste Poline (WDoIN & CEA/DRM/SHFJ, Orsay, France), +Christian Buechel (WDoIN), Matthew Brett (MRC-CBU, Cambridge, England), Chloe Hutton +(WDoIN) and Keith Worsley (Department of Statistics, McGill University, Montreal +Canada). + +See `AUTHORS.txt` for a complete list of SPM co-authors. + +We would like to thank everyone who has provided feedback on SPM. + +# Disclaimer, copyright & licencing + +SPM (being the collection of files given in the manifest in the `Contents.m` file) is +free but copyright software, distributed under the terms of the GNU General Public +Licence as published by the Free Software Foundation (either version 2, as given in +file `LICENCE.txt`, or at your option, any later version). Further details on +"copyleft" can be found at https://www.gnu.org/copyleft/. In particular, SPM is +supplied as is. No formal support or maintenance is provided or implied. + +``` +Copyright (C) 1991,1994-2018 Wellcome Centre for Human Neuroimaging +$Id: README.md 7475 2018-11-07 13:03:49Z guillaume $ +``` \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index 6566b5e4..00000000 --- a/README.txt +++ /dev/null @@ -1,145 +0,0 @@ - -% ___ ____ __ __ -% / __)( _ \( \/ ) -% \__ \ )___/ ) ( Statistical Parametric Mapping -% (___/(__) (_/\/\_) SPM - http://www.fil.ion.ucl.ac.uk/spm/ -% -% R E A D M E -% -%__________________________________________________________________________ -% -% This README gives a brief introduction to the SPM software. -% Full details can be found on the SPM website: -% -% http://www.fil.ion.ucl.ac.uk/spm/ -% -% See also Contents.m, AUTHORS.txt and LICENCE.txt. -% -%__________________________________________________________________________ -% SPM -% -% Statistical Parametric Mapping refers to the construction and -% assessment of spatially extended statistical process used to -% test hypotheses about functional imaging data. These ideas have -% been instantiated in software that is called SPM. -% The SPM software package has been designed for the analysis of -% brain imaging data sequences. The sequences can be a series of -% images from different cohorts, or time-series from the same -% subject. The current release is designed for the analysis of -% fMRI, PET, SPECT, EEG and MEG. -% -% ---------------- -% -% Please refer to this version as "SPM12" in papers and communications. -% -% ---------------- -% -% SPM was written to organise and interpret our data (at the Wellcome -% Trust Centre for Neuroimaging). The distributed version is the same as -% that we use ourselves. -% -% SPM is made freely available to the [neuro]imaging community, to -% promote collaboration and a common analysis scheme across laboratories. -% -%__________________________________________________________________________ -% Software -% -% The SPM software is a suite of MATLAB functions, scripts and data files, -% with some externally compiled C routines, implementing Statistical -% Parametric Mapping. MATLAB, a commercial engineering mathematics -% package, is required to use SPM. MATLAB is produced by MathWorks, -% Natick, MA, USA. http://www.mathworks.com/ -% SPM requires only core MATLAB to run (no special toolboxes are required). -% -% SPM12 is written for MATLAB version 7.4 (R2007a) onwards under Windows, -% Linux and Mac (SPM12 will not work with versions of MATLAB prior to 7.4). -% Binaries of the external C-MEX routines are provided for Windows, Linux -% and Mac. The source code is supplied and can be compiled with a C -% compiler (Makefile provided). -% See http://www.fil.ion.ucl.ac.uk/spm/software/spm12/ for details. -% -% Later versions of MATLAB (released after SPM12), will probably need -% additional patches in order to run. Once developed, these will be made -% available from: -% http://www.fil.ion.ucl.ac.uk/spm/download/spm12_updates/ -% -% Although SPM12 will read image files from previous versions of SPM, there -% are differences in the algorithms, templates and models used. Therefore, -% we recommend you use a single SPM version for any given project. -% -% The SPM12 Release Notes can be found online: -% http://www.fil.ion.ucl.ac.uk/spm/software/spm12/ -% -%__________________________________________________________________________ -% File format -% -% SPM12 uses the NIFTI-1 data format as standard. Take a look at -% http://nifti.nimh.nih.gov/ -% for more information on the NIFTI-1 file format. -% -% The old SPM2 version of Analyze format can be read straight into SPM12, -% but results will be written out as NIFTI-1. If you still use this -% format, then it is important that you ensure that spm_flip_analyze_images -% has been set appropriately for your data. -% -% The MINC and ECAT7 formats can not be read straight into SPM12, although -% conversion utilities have been provided. Similarly, a number of DICOM -% flavours can also be converted to NIFTI-1 using tools in SPM12. -% -%__________________________________________________________________________ -% Resources -% -% The SPM website is the central repository for SPM resources: -% http://www.fil.ion.ucl.ac.uk/spm/ -% Introductory material, installation details, documentation, course -% details and patches are published on the site. -% -% There is an SPM eMail discussion list, hosted at . -% The list is monitored by the authors, and discusses theoretical, -% methodological and practical issues of Statistical Parametric Mapping -% and SPM. The SPM website has further details: -% http://www.fil.ion.ucl.ac.uk/spm/support/ -% -% Please report bugs to the authors at . -% Peculiarities may actually be features, and should be raised on the SPM -% eMail discussion list, . -% -%__________________________________________________________________________ -% Authors -% -% SPM is developed under the auspices of Functional Imaging Laboratory -% (FIL), The Wellcome Trust Centre for NeuroImaging, in the Institute of -% Neurology at University College London (UCL), UK. -% -% SPM94 was written primarily by Karl Friston in the first half of -% 1994, with assistance from John Ashburner (MRC-CU), Jon Heather -% (WDoIN), and Andrew Holmes (Department of Statistics, University of -% Glasgow). Subsequent development, under the direction of Prof. Karl -% Friston at the Wellcome Department of Imaging Neuroscience, has -% benefited from substantial input (technical and theoretical) from: -% John Ashburner (WDoIN), Andrew Holmes (WDoIN & Robertson Centre for -% Biostatistics, University of Glasgow, Scotland), Jean-Baptiste Poline -% (WDoIN & CEA/DRM/SHFJ, Orsay, France), Christian Buechel (WDoIN), -% Matthew Brett (MRC-CBU, Cambridge, England), Chloe Hutton (WDoIN) and -% Keith Worsley (Department of Statistics, McGill University, Montreal, -% Canada). -% -% See AUTHORS.txt for a complete list of SPM co-authors. -% -% We would like to thank everyone who has provided feedback on SPM. -% -%__________________________________________________________________________ -% Disclaimer, copyright & licencing -% -% SPM (being the collection of files given in the manifest in the -% Contents.m file) is free but copyright software, distributed under the -% terms of the GNU General Public Licence as published by the Free -% Software Foundation (either version 2, as given in file LICENCE.txt, -% or at your option, any later version). Further details on "copyleft" can -% be found at http://www.gnu.org/copyleft/. In particular, SPM is supplied -% as is. No formal support or maintenance is provided or implied. -% -%__________________________________________________________________________ -% % Copyright (C) 1991,1994-2017 Wellcome Trust Centre for Neuroimaging -% -% $Id: README.txt 6980 2017-01-04 10:10:29Z guillaume $ diff --git a/bin/spm12-octave b/bin/spm12-octave index b8b2f5b3..db9de132 100755 --- a/bin/spm12-octave +++ b/bin/spm12-octave @@ -6,7 +6,7 @@ # Copyright (C) 2017 Wellcome Trust Centre for Neuroimaging # # Guillaume Flandin -# $Id: spm12-octave 7053 2017-04-03 11:04:13Z guillaume $ +# $Id: spm12-octave 7284 2018-03-29 09:42:26Z guillaume $ spm_dir = getenv ("SPM_HOME"); @@ -37,5 +37,7 @@ end spm_standalone (argv (){:}); -H = get (0, "CurrentFigure"); -if ! (isempty (H)), waitfor (H); endif +while (! isempty (get (0, "CurrentFigure"))) + waitfor (get (0, "CurrentFigure")); +endwhile +exit (0); diff --git a/config/spm_cfg_cat.m b/config/spm_cfg_cat.m index b1be91cc..ac542b71 100644 --- a/config/spm_cfg_cat.m +++ b/config/spm_cfg_cat.m @@ -1,10 +1,10 @@ function cat = spm_cfg_cat % SPM Configuration file for 3D to 4D volumes conversion %__________________________________________________________________________ -% Copyright (C) 2008-2016 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2008-2018 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_cfg_cat.m 6918 2016-11-02 14:33:11Z guillaume $ +% $Id: spm_cfg_cat.m 7290 2018-04-10 16:43:01Z guillaume $ %-------------------------------------------------------------------------- % vols 3D Volumes @@ -46,13 +46,25 @@ name.num = [1 Inf]; name.val = {'4D.nii'}; +%-------------------------------------------------------------------------- +% RT Interscan interval +%-------------------------------------------------------------------------- +RT = cfg_entry; +RT.tag = 'RT'; +RT.name = 'Interscan interval'; +RT.help = {'Interscan interval, TR, (specified in seconds).' + 'This is the time between acquiring a plane of one volume and the same plane in the next volume. It is assumed to be constant throughout.'}; +RT.strtype = 'r'; +RT.num = [1 1]; +RT.val = {NaN}; + %-------------------------------------------------------------------------- % cat 3D to 4D File Conversion %-------------------------------------------------------------------------- cat = cfg_exbranch; cat.tag = 'cat'; cat.name = '3D to 4D File Conversion'; -cat.val = {vols name dtype}; +cat.val = {vols name dtype RT}; cat.help = {'Concatenate a number of 3D volumes into a single 4D file.'}; cat.prog = @(job)spm_run_cat('run',job); cat.vout = @(job)spm_run_cat('vout',job); @@ -66,7 +78,8 @@ V = char(job.vols{:}); dt = job.dtype; fname = job.name; - V4 = spm_file_merge(V,fname,dt); + RT = job.RT; + V4 = spm_file_merge(V,fname,dt,RT); out.mergedfile = {V4(1).fname}; case 'vout' diff --git a/config/spm_cfg_dcm_est.m b/config/spm_cfg_dcm_est.m index b687051a..cd9f34d2 100644 --- a/config/spm_cfg_dcm_est.m +++ b/config/spm_cfg_dcm_est.m @@ -4,7 +4,7 @@ % Copyright (C) 2008-2017 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin & Peter Zeidman -% $Id: spm_cfg_dcm_est.m 7153 2017-08-10 10:18:14Z adeel $ +% $Id: spm_cfg_dcm_est.m 7479 2018-11-09 14:17:33Z peter $ % ------------------------------------------------------------------------- % dcmmat Select DCM_*.mat @@ -103,7 +103,7 @@ %-------------------------------------------------------------------------- output_overwrite_gcm = cfg_branch; output_overwrite_gcm.tag = 'overwrite_gcm'; -output_overwrite_gcm.name = 'Overwrite existing GCM_*.mat file'; +output_overwrite_gcm.name = 'Overwrite existing GCM/DCM files'; output_overwrite_gcm.val = {}; output_overwrite_gcm.help = {['Overwrites existing group-level DCM file ' ... 'with estimated models.']}; @@ -113,7 +113,7 @@ %-------------------------------------------------------------------------- output_separate = cfg_branch; output_separate.tag = 'separate'; -output_separate.name = 'Overwrite existing individual DCM files'; +output_separate.name = 'Only save individual DCM files'; output_separate.val = {}; output_separate.help = {'Updated existing individual DCM.mat files'}; @@ -125,10 +125,20 @@ output.name = 'Output'; output.values = { output_single output_overwrite_gcm output_separate }; output.val = { output_single }; -output.help = {['Whether to create a single DCM file across all ' ... - 'subjects / models (default, required for second ' ... - 'level analysis) or just update the separate' ... - 'first-level DCM files.']}; +output.help = {'How to store the estimated DCMs. The options are: ' ... + ['1. Create group GCM_*.mat file - this will create a single '... + 'file containing a cell array, with one row per subject ' ... + 'and one column per DCM, containing the filenames of ' ... + 'the DCMs. The original DCM files will be overwritten ' ... + 'with the outcome of the estimation. ' ... + '(Alternatively, if the input is a GCM file containing ' ... + 'DCM structures rather than filenames, then the output ' ... + 'will also be an array containing DCM structures.)'] ... + ['2. Overwrite existing GCM/DCM files - as above but with ' ... + 'with an existing GCM filename.'] ... + ['3. Only save individual DCM files - does not change the ' ... + 'GCM file and simply overwrites each subject''s DCM ' ... + 'with estimated values.']}; % ------------------------------------------------------------------------- % way Choice of ways to select DCMs (nested models) @@ -258,11 +268,6 @@ error('Unknown output type'); end -% Validate options -if (input_type == INPUT_GCM && output_type == OUTPUT_DCM) - error('If the input is a single GCM file, the output must be too'); -end - if (output_type == OUTPUT_GCM_OVERWRITE && input_type ~= INPUT_GCM) error('To overwrite an existing GCM file, the input must be a GCM'); end @@ -310,6 +315,12 @@ end end +% Validate +if output_type == OUTPUT_DCM && isempty(P) + error(['Cannot save individual DCM files when the input is a GCM ' ... + 'array of DCM structures']); +end + % Load all models into memory if ~isempty(P) GCM = spm_dcm_load(P); @@ -319,7 +330,7 @@ for s = 1:ns for m = 1:nm if strcmpi(job.fmri.analysis,'CSD') - GCM{s,m}.options = 'CSD'; + GCM{s,m}.options.analysis = 'CSD'; GCM{s,m}.options.induced = 1; GCM{s,m}.options.stochastic = 0; else @@ -362,7 +373,7 @@ end % Save individual DCM .mat files if requested -if ~isempty(P) && (est_type == OUTPUT_DCM) +if ~isempty(P) && (est_type ~= EST_NONE) for s = 1:ns for m = 1:nm DCM = GCM{s,m}; @@ -395,22 +406,24 @@ save(filename,'GCM', spm_get_defaults('mat.format')); out.gcmmat = {filename}; -else - out.dcmmat = P; +end + +if ~isempty(P) + out.dcmmat = P(:,1); end %========================================================================== function dep = vout_dcm(job) %========================================================================== +dep(1) = cfg_dep; +dep(1).sname = 'DCM mat File(s) - full models'; +dep(1).src_output = substruct('.','dcmmat'); +dep(1).tgt_spec = cfg_findspec({{'filter','mat','strtype','e'}}); + if isfield(job.output,'single') || ... isfield(job.output,'overwrite_gcm') - dep(1) = cfg_dep; - dep(1).sname = 'GCM mat File(s)'; - dep(1).src_output = substruct('.','gcmmat'); - dep(1).tgt_spec = cfg_findspec({{'filter','mat','strtype','e'}}); -else - dep(1) = cfg_dep; - dep(1).sname = 'DCM mat File(s)'; - dep(1).src_output = substruct('.','dcmmat'); - dep(1).tgt_spec = cfg_findspec({{'filter','mat','strtype','e'}}); + dep(2) = cfg_dep; + dep(2).sname = 'GCM mat File(s)'; + dep(2).src_output = substruct('.','gcmmat'); + dep(2).tgt_spec = cfg_findspec({{'filter','mat','strtype','e'}}); end \ No newline at end of file diff --git a/config/spm_cfg_dcm_fmri.m b/config/spm_cfg_dcm_fmri.m index f5ea242d..b70be6c0 100644 --- a/config/spm_cfg_dcm_fmri.m +++ b/config/spm_cfg_dcm_fmri.m @@ -4,7 +4,7 @@ % Copyright (C) 2008-2016 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin & Peter Zeidman -% $Id: spm_cfg_dcm_fmri.m 6952 2016-11-25 16:03:13Z guillaume $ +% $Id: spm_cfg_dcm_fmri.m 7479 2018-11-09 14:17:33Z peter $ % ------------------------------------------------------------------------- @@ -18,6 +18,29 @@ dcmmat.ufilter = '^DCM_.*\.mat$'; dcmmat.num = [1 Inf]; +% ------------------------------------------------------------------------- +% dcmmat_full Select template full model +% ------------------------------------------------------------------------- +dcmmat_full = dcmmat; +dcmmat_full.tag = 'fulldcm'; +dcmmat_full.name = 'Full DCM'; +dcmmat_full.help = {['Select a DCM .mat file, specified using the GUI ' ... + 'for a single subject. This should be a ''full'' model with all ' ... + 'parameters of interest (e.g. connections) enabled.']}; +dcmmat_full.num = [1 1]; + +% ------------------------------------------------------------------------- +% dcmmat_alt Select alternative template models +% ------------------------------------------------------------------------- +dcmmat_alt = dcmmat; +dcmmat_alt.tag = 'altdcm'; +dcmmat_alt.name = 'Alternative DCMs'; +dcmmat_alt.help = {['Select alternative DCMs, specified using the GUI ' ... + 'for a single subject. These should be reduced or nested versions of the ' ... + 'full template DCM, e.g. with some connections switched off (fixed ' ... + 'at their priors).']}; +dcmmat_alt.num = [0 Inf]; +dcmmat_alt.val = {''}; % ------------------------------------------------------------------------- % voimat Select VOI_*.mat % ------------------------------------------------------------------------- @@ -29,6 +52,27 @@ voimat.ufilter = '^VOI_.*\.mat$'; voimat.num = [1 Inf]; +% ------------------------------------------------------------------------- +% region Select VOI_*.mat (for a single region across subjects) +% ------------------------------------------------------------------------- +region = cfg_files; +region.tag = 'region'; +region.name = 'Region (VOI files)'; +region.help = {'Select all subjects'' VOI_*.mat for this region.'}; +region.filter = 'mat'; +region.ufilter = '^VOI_.*\.mat$'; +region.num = [1 Inf]; + +% ------------------------------------------------------------------------- +% regions Create set of regions +%-------------------------------------------------------------------------- +vois = cfg_repeat; +vois.tag = 'vois'; +vois.name = 'Regions of interest'; +vois.values = {region}; +vois.help = {'Select the regions of interest (VOIs) for all subjects'}; +vois.num = [1 Inf]; + % ------------------------------------------------------------------------- % spmmat Select SPM.mat % ------------------------------------------------------------------------- @@ -40,37 +84,59 @@ spmmat.ufilter = '^SPM\.mat$'; spmmat.num = [1 1]; + +% ------------------------------------------------------------------------- +% spmmat Select SPM.mat for each subject +% ------------------------------------------------------------------------- +spmmats = spmmat; +spmmats.tag = 'spmmats'; +spmmats.name = 'SPM.mat files'; +spmmats.help = {'Select all subjects'' SPM.mat files'}; +spmmats.num = [1 Inf]; + % ------------------------------------------------------------------------- % session Session index % ------------------------------------------------------------------------- session = cfg_entry; session.tag = 'session'; session.name = 'Which session'; -session.help = {'Enter the session number.'}; +session.help = {'Enter the session (run) number.'}; session.strtype = 'n'; session.num = [1 1]; +session.val = {1}; %-------------------------------------------------------------------------- % dir Directory %-------------------------------------------------------------------------- dir = cfg_files; dir.tag = 'dir'; -dir.name = 'Directory'; +dir.name = 'Output directory'; dir.help = {'Select the directory where the output will be written.'}; dir.filter = 'dir'; dir.ufilter = '.*'; dir.num = [1 1]; +% ------------------------------------------------------------------------- +% name Model name +%-------------------------------------------------------------------------- +name = cfg_entry; +name.tag = 'name'; +name.name = 'Output name'; +name.help = {['Specify a name for the group DCM file. The prefix ''GCM_'' ' ... + 'and suffix ''.mat'' are added automatically.']}; +name.strtype = 's'; +name.num = [0 Inf]; + % ------------------------------------------------------------------------- % val Val % ------------------------------------------------------------------------- val = cfg_entry; val.tag = 'val'; -val.name = 'Values'; -val.help = {'Inputs to include for one condition. Enter ''1'' ' ... +val.name = 'Condition'; +val.help = {['Values to include for this condition. Enter ''1'' ' ... 'to include this condition (with no parameteric regressor). '... 'Entering [1 0 1] would include this condition and '... - 'its second parametric regressor.'}; + 'its second parametric regressor.']}; val.strtype = 'w'; val.num = [1 Inf]; @@ -80,10 +146,14 @@ inp = cfg_repeat; inp.tag = 'inputs'; inp.name = 'Inputs'; -inp.help = {'Inputs to include and their parametric modulations (PMs). '... - 'You should click ''New: Values'' for each condition in '... - 'your SPM (i.e. SPM.U), up to the last condition you wish '... - 'to include.'}; +inp.help = {['Conditions to include and their parametric modulations (PMs). '... + 'Click ''New: Condition'' for each condition in your '... + 'SPM (i.e. SPM.U), up to the last condition you wish '... + 'to include. For each Condition, enter:'] ... + '1 (to include the condition in the DCM) or' ... + '[1 1] (the condition and its 1st parameteric regressor) or' ... + '[1 0 1] (the condition and its 2nd parameteric regressor)' ... + 'etc'}; inp.values = { val }; inp.num = [1 Inf]; @@ -106,6 +176,50 @@ models.help = {'Select DCM.mat files per model.'}; models.num = [1 Inf]; +% ------------------------------------------------------------------------- +% output Output branch (specify group batch) +% ------------------------------------------------------------------------- +output = cfg_branch; +output.tag = 'output'; +output.name = 'Output'; +output.val = {dir name}; +output.help = {['Select where the group DCM (GCM) file containg all ' ... + 'the DCMs'' filenames will be stored.']}; + +% ------------------------------------------------------------------------- +% templates Templates branch (specify group batch) +% ------------------------------------------------------------------------- +template = cfg_branch; +template.tag = 'template'; +template.name = 'Template DCMs'; +template.val = {dcmmat_full dcmmat_alt}; +template.help = {'Template DCMs to replicate over subjects.'}; + +% ------------------------------------------------------------------------- +% data Design & data branch (specify group batch) +% ------------------------------------------------------------------------- +data = cfg_branch; +data.tag = 'data'; +data.name = 'Design & data'; +data.val = {spmmats session vois}; +data.help = {'Experimental timing and timeseries for the DCMs.'}; + +% ------------------------------------------------------------------------- +% group Specify group model space +% ------------------------------------------------------------------------- +group = cfg_exbranch; +group.tag = 'group'; +group.name = 'Specify group'; +group.val = {output template data}; +group.help = {'Duplicates template DCM(s) for each subject.'... + ['Before running this, create one or more example DCMs for one ' ... + 'subject using the Dynamic Causal Modelling button in the main ' ... + 'SPM window. Then use this batch to duplicate the DCM(s) for each ' ... + 'subject. The output is a file containing a cell array, with one ' ... + 'row per subject and one column per DCM (named GCM_.mat).']}; +group.prog = @spm_run_create_gcm; +group.vout = @vout_gcm_fmri; + % ------------------------------------------------------------------------- % regions Specify regions % ------------------------------------------------------------------------- @@ -145,8 +259,132 @@ fmri.tag = 'fmri'; fmri.name = 'DCM for fMRI'; fmri.help = {'Dynamic Causal Modelling for fMRI'}; -fmri.values = { regions inputs }; +fmri.values = { group regions inputs }; +%========================================================================== +function out = spm_run_create_gcm(job) +% Replicates template DCM(s) across subjects and creates a GCM file + +% Unpack +out_dir = job.output.dir; +out_name = job.output.name; +full_dcm = job.template.fulldcm; +alt_dcm = job.template.altdcm; +spms = job.data.spmmats; +sess = job.data.session; +regions = job.data.region; + +nr = length(regions); % number of regions +ns = length(spms); % number of subjects +nm = length(alt_dcm) + 1; % number of models + +% Validate length of VOI arrays +for r = 1:nr + if length(regions{r}) ~= ns + error('Pleasure ensure region %d has 1 VOI per subject',r); + end +end + +% Get template DCMs +templates = cell(1,nm); +templates{1} = full_dcm{1}; +for m = 1:length(alt_dcm) + templates{m + 1} = alt_dcm{m}; +end + +% Get indices in the SPM of experimental inputs +DCM = load(templates{1}); +DCM = DCM.DCM; +if isfield(DCM.U,'idx') + idx = DCM.U.idx; +elseif isfield(DCM.U,'u') && size(DCM.U.u,2)==1 && ... + strcmp(DCM.U.name{1},'null') ... + % Resting state (backward compatibility) + idx = 0; +else + % GUI (backward compatibility) + idx = get_conditions_from_ui(spms{1},sess); +end + +% Convert inputs to cell format for spm_dcm_U +inputs = {}; +if ~isempty(idx) && (idx(1) ~= 0) + max_cond = max(idx(:,1)); + inputs = cell(1, max_cond); + for i = 1:size(idx,1) + cond = idx(i,1); + col = idx(i,2); + inputs{cond}(col) = 1; + end +end + +GCM = cell(ns,nm); + +for s = 1:ns + % Get subject SPM dir + subject_spm = spms{s}; + subject_spm_dir = fileparts(subject_spm); + + % Get subject VOIs + subject_vois = cell(nr,1); + for r = 1:nr + subject_vois{r} = regions{r}{s}; + end + + for m = 1:nm + % Copy template to subject + new_dcm = fullfile(subject_spm_dir, ... + sprintf('DCM_%s_m%04d.mat',out_name,m)); + copyfile(templates{m}, new_dcm); + GCM{s,m} = new_dcm; + + % Update regions + if ~isempty(inputs) + spm_dcm_U(new_dcm,subject_spm,sess,inputs); + end + + % Update timeseries + spm_dcm_voi(new_dcm,subject_vois); + end + +end + +% Save GCM +out_file = fullfile(out_dir{1}, ['GCM_' out_name '.mat']); +save(out_file,'GCM'); + +out.gcmmat = {out_file}; + +%========================================================================== +function idx = get_conditions_from_ui(spmmat,sess) +% Prompts for condition names. Provides backward compatibility for DCMs +% specified prior to the introduction of DCM.U.idx +% +% idx - [n x 2] matrix for n conditions. The first column is the index in +% SPM.U and the second is the regressor within the condition. + +SPM = load(spmmat); +SPM = SPM.SPM; + +Sess = SPM.Sess(sess); +u = length(Sess.U); + +if isempty(Sess.U) + idx = 0; + return; +end + +idx = []; +for i = 1:u + for j = 1:length(Sess.U(i).name) + str = ['include ' Sess.U(i).name{j} '?']; + if spm_input(str,'+1','y/n',[1 0],1) + idx = [idx; i j]; + end + end +end + +if isempty(idx), idx = 0; end %========================================================================== function out = spm_run_dcm_fmri_inputs(job) @@ -171,3 +409,11 @@ dep(1).sname = 'DCM mat File(s)'; dep(1).src_output = substruct('.','dcmmat'); dep(1).tgt_spec = cfg_findspec({{'filter','mat','strtype','e'}}); + +%========================================================================== +function dep = vout_gcm_fmri(varargin) +%========================================================================== +dep(1) = cfg_dep; +dep(1).sname = 'GCM mat File(s)'; +dep(1).src_output = substruct('.','gcmmat'); +dep(1).tgt_spec = cfg_findspec({{'filter','mat','strtype','e'}}); diff --git a/config/spm_cfg_dcm_peb.m b/config/spm_cfg_dcm_peb.m index 50a97977..6be6a1a2 100644 --- a/config/spm_cfg_dcm_peb.m +++ b/config/spm_cfg_dcm_peb.m @@ -4,7 +4,7 @@ % Copyright (C) 2016-2017 Wellcome Trust Centre for Neuroimaging % Peter Zeidman -% $Id: spm_cfg_dcm_peb.m 7007 2017-02-07 10:15:24Z guillaume $ +% $Id: spm_cfg_dcm_peb.m 7479 2018-11-09 14:17:33Z peter $ %========================================================================== @@ -93,7 +93,7 @@ cov_design.help = {['Enter or paste the N x C design matrix for N ' ... 'subjects and C covariates. Note that a column of '... 'ones will automatically be added to the start of the '... - 'matrix, to model the group mean.']}; + 'matrix, to model the group mean, if one is not found.']}; cov_design.strtype = 'r'; cov_design.num = [Inf Inf]; @@ -157,7 +157,7 @@ regressors.help = {'Specify the second-level design matrix one '... 'covariate (regressor) at a time. Note that a ' ... 'column of ones to model the mean across subjects ' ... - 'is added automatically.'}; + 'is added automatically if one is not found.'}; regressors.num = [1 Inf]; %-------------------------------------------------------------------------- @@ -279,6 +279,23 @@ % Priors on log precision (between-subjects variability) entry %========================================================================== +%-------------------------------------------------------------------------- +% show_review Select whether to review results +%-------------------------------------------------------------------------- +components = cfg_menu; +components.tag = 'components'; +components.name = 'Precision components'; +components.labels = {'Single','Fields','All','None'}; +components.values = {'Single','Fields','All','None'}; +components.val = {'All'}; +components.help = {['The precision components to include. By default, ' ... + 'the between-subjects variability is separately ' ... + 'estimated for each DCM connectivity parameter.'], ... + 'Single: a single component for all DCM parameters. ', ... + 'Fields: one component per field (e.g. A,B,C)' ... + 'All (default): one component per DCM parameter', ... + 'None: all DCM parameters are treated as fixed effects.'}; + %-------------------------------------------------------------------------- % priors_log_precision_mu Priors on log precision expectation %-------------------------------------------------------------------------- @@ -347,7 +364,8 @@ priors_between = cfg_branch; priors_between.tag = 'priors_between'; priors_between.name = 'Between-subjects variability'; -priors_between.val = { priors_parameters_ratio ... +priors_between.val = { components ... + priors_parameters_ratio ... priors_log_precision_mu ... priors_log_precision_var}; priors_between.help = {['Between-subjects variability over second-' ... @@ -372,6 +390,28 @@ priors_glm.help = {['Priors on the expected values of the second ' ... 'level General Linear Model (GLM) parameters.']}; +%-------------------------------------------------------------------------- +% maxit Maximum VL iterations +%-------------------------------------------------------------------------- +maxit = cfg_entry; +maxit.name = 'Max iterations'; +maxit.tag = 'maxit'; +maxit.help = {'Maximum iterations of the VL estimation procedure.' + ['Only change this if you find the free energy is still ' ... + 'increasing non-trivially up to the default limit of 64.']}; +maxit.strtype = 'r'; +maxit.num = [1 1]; +maxit.val = {64}; + +%-------------------------------------------------------------------------- +% estimation Model estimation settings +%-------------------------------------------------------------------------- +estimation = cfg_branch; +estimation.tag = 'estimation'; +estimation.name = 'Estimation'; +estimation.val = { maxit }; +estimation.help = {'Settings for estimating the PEB model'}; + %-------------------------------------------------------------------------- % show_review Select whether to review results %-------------------------------------------------------------------------- @@ -394,7 +434,7 @@ specify.tag = 'specify'; specify.name = 'Specify / Estimate PEB'; specify.val = { name model_space_mat dcm_idx covariates fields ... - priors_between priors_glm sr }; + priors_between priors_glm estimation sr }; specify.help = {['Specifies and estimates a second-level DCM (PEB) model. ' ... 'A PEB model will be created for each first level DCM.' ]}; @@ -409,9 +449,14 @@ model_space_mat_op.val = {''}; null_prior_covariance = cfg_entry; -null_prior_covariance.name = 'Null prior covariance'; +null_prior_covariance.name = 'Null prior variance'; null_prior_covariance.tag = 'nullpcov'; -null_prior_covariance.help = {'gamma'}; +null_prior_covariance.help = {['This is the prior variance for any ''switched ' ... + 'off'' parameter. Setting this to 0 (zero) tests the point null ' ... + 'hypothesis that parameters are present vs absent. Setting this to a ' ... + 'small positive number tests the null hypothesis that the parameter is ' ... + 'trivially small. The smaller this setting, the fewer parameters will ' ... + 'survive the model search. (See setting gamma in spm_dcm_bmr_all.m)']}; null_prior_covariance.strtype = 'r'; null_prior_covariance.num = [1 1]; null_prior_covariance.val = {1/16}; @@ -419,7 +464,7 @@ peb_compare = cfg_exbranch; peb_compare.tag = 'compare'; peb_compare.name = 'Compare / Average PEB models'; -peb_compare.val = { peb_mat model_space_mat_op null_prior_covariance show_review}; +peb_compare.val = { peb_mat model_space_mat_op show_review}; peb_compare.help = {['Addresses the question: which combination of ' ... 'connections best explains the commonalities across subjects and ' ... 'the group differences between subjects?'] '' ... @@ -477,7 +522,7 @@ predict.tag = 'predict'; predict.name = 'Predict (cross-validation)'; predict.val = { name model_space_mat dcm_idx covariates_min1 fields ... - priors_between priors_glm }; + priors_between priors_glm estimation}; predict.help = {['Builds a PEB model on all but one subjects, and uses ' ... 'it to predict a between-subjects effect, such as ' ... 'group membership, in the remaining subject. This process ' ... @@ -538,10 +583,9 @@ %========================================================================== % Run the PEB specification / estimation batch -[GCM,M,field,gcm_file] = prepare_peb_inputs(job); +[GCM,M,field,dir_out] = prepare_peb_inputs(job); % Specify / estimate PEB on full model only -dir_out = fileparts(gcm_file); name = job.name; PEB = spm_dcm_peb(GCM,M,field); @@ -549,24 +593,23 @@ peb_filename = fullfile(dir_out,['PEB_' name '.mat']); save(peb_filename,'PEB', spm_get_defaults('mat.format')); +out.peb_mat = {peb_filename}; + % Review PEB if job.show_review == 1 spm_dcm_peb_review(peb_filename,GCM); end -out.peb_mat = {peb_filename}; - %========================================================================== function out = spm_run_dcm_loo(job) %========================================================================== % Run leave-one-out cross validation -[GCM,M,field,gcm_file] = prepare_peb_inputs(job); +[GCM,M,field,dir_out] = prepare_peb_inputs(job); [qE,qC,Q] = spm_dcm_loo(GCM,M,field); % Write output -dir_out = fileparts(gcm_file); name = job.name; loo_filename = fullfile(dir_out,['LOO_' name '.mat']); @@ -575,11 +618,11 @@ out.loo_mat = {loo_filename}; %========================================================================== -function [GCM,M,field,gcm_file] = prepare_peb_inputs(job) +function [GCM,M,field,dir_out] = prepare_peb_inputs(job) %========================================================================== % Prepare the inputs needed to specify a PEB or run LOO -[GCM,gcm_file] = load_dcm(job); +[GCM,dir_out] = load_dcm(job); ns = size(GCM,1); @@ -596,14 +639,12 @@ field = job.fields.custom; end -Xnames = {'Group mean'}; - -X = ones(ns,1); - % Covariates if isfield(job.cov, 'none') - % Do nothing - + % No covariates (except the mean, added later) + Xnames = {}; + X = []; + elseif isfield(job.cov, 'design_mtx') % Whole design matrix entered x = job.cov.design_mtx.cov_design; @@ -612,14 +653,17 @@ error('Please ensure design matrix has one row per subject.'); end - X = [X x]; - - Xnames = [Xnames job.cov.design_mtx.name]; + X = x; + Xnames = job.cov.design_mtx.name; + elseif isfield(job.cov, 'regressor') % Design matrix entered per-regressor regressors = job.cov.regressor; - + + X = []; + Xnames = {}; + for r = 1:length(regressors) regressor = regressors(r).value; name = regressors(r).name; @@ -627,13 +671,24 @@ if size(regressor,1) ~= ns error('Please ensure regressor %d has one row per subject.',r); end - + X = [X regressor]; Xnames = [Xnames name]; end end -% Ensure a mean column wasn't entered accidently +% Ensure there's a constant column +if isempty(X) + has_constant = false; +else + has_constant = ~any(diff(X(:,1))); +end +if ~has_constant + Xnames = ['Commonalities' Xnames]; + X = [ones(ns,1) X]; +end + +% Ensure a column colinear with the mean wasn't entered accidently if size(X,2) > 1 bad = find(~any(diff(X(:,2:end)))); if ~isempty(bad) @@ -645,15 +700,24 @@ error('Please ensure there is one covariate name per covariate.'); end +% Option for precision components +Q = job.priors_between.components; +if isempty(Q) + Q = 'all'; +else + Q = lower(Q); +end + % Priors / covariance components M = struct(); M.alpha = job.priors_glm.group_ratio; M.beta = job.priors_between.ratio; M.hE = job.priors_between.expectation; M.hC = job.priors_between.var; -M.Q = 'single'; +M.Q = Q; M.X = X; M.Xnames = Xnames; +M.maxit = job.estimation.maxit; %========================================================================== function out = spm_run_bmr_all(job) @@ -693,7 +757,9 @@ PEB = load(job.peb_mat{1}); PEB = PEB.PEB; -PEB.gamma = job.nullpcov; +if isfield(job,'nullpcov') + PEB.gamma = job.nullpcov; +end nm = size(GCM,2); @@ -706,10 +772,13 @@ % Write BMA [dir_out, name] = fileparts(job.peb_mat{1}); +if nm == 1 + name = ['search_' name]; +end filename = fullfile(dir_out, ['BMA_' name '.mat']); save(filename,'BMA', spm_get_defaults('mat.format')); -out.bmamat = filename; +out.bmamat = {filename}; % Review BMA if job.show_review == 1 @@ -728,16 +797,30 @@ end %========================================================================== -function [GCM,gcm_file] = load_dcm(job) +function [GCM,dir_out] = load_dcm(job) %========================================================================== % Load and validate selected model space -gcm_file = char(job.model_space_mat); -GCM = load(gcm_file); -if ~isfield(GCM,'GCM') - error('Provided file is not a valid model space.'); +if iscell(job.model_space_mat) && ischar(job.model_space_mat{1}) ... + && numel(job.model_space_mat) > 1 + % An array of DCM filenames was provided (via the dependency system) + GCM = job.model_space_mat; + dir_out = fileparts(GCM{1}); +else + % The filename of a single GCM mat file was provided + gcm_file = char(job.model_space_mat); + dir_out = fileparts(gcm_file); + GCM = load(gcm_file); + if ~isfield(GCM,'GCM') + error('Provided file is not a valid model space.'); + end + GCM = GCM.GCM; +end + +% If a GCM of filenames was provided, load the DCMs +if ischar(GCM{1}) + GCM = spm_dcm_load(GCM); end -GCM = GCM.GCM; % Limit to specific model(s) if requested if isfield(job,'dcm') && isfield(job.dcm,'index') diff --git a/config/spm_cfg_dicom.m b/config/spm_cfg_dicom.m index a4f2a926..4466f8ae 100644 --- a/config/spm_cfg_dicom.m +++ b/config/spm_cfg_dicom.m @@ -3,7 +3,7 @@ %__________________________________________________________________________ % Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging -% $Id: spm_cfg_dicom.m 7201 2017-11-08 11:13:25Z guillaume $ +% $Id: spm_cfg_dicom.m 7355 2018-06-22 11:40:55Z john $ %------------------------------------------------------------------------- % data DICOM files @@ -48,7 +48,6 @@ 'Output directory: .///' 'Output directory: ./' 'No directory hierarchy'}'; -% removed 'Output directory: .//' for anonymity purposes root.values = {'date_time' 'patid' 'patid_date' diff --git a/config/spm_cfg_eeg.m b/config/spm_cfg_eeg.m index 3a158a07..0b7bb587 100644 --- a/config/spm_cfg_eeg.m +++ b/config/spm_cfg_eeg.m @@ -3,7 +3,8 @@ %__________________________________________________________________________ % Copyright (C) 2008-2014 Wellcome Trust Centre for Neuroimaging -% $Id: spm_cfg_eeg.m 7206 2017-11-09 16:27:30Z gareth $ +% $Id: spm_cfg_eeg.m 7402 2018-08-17 16:18:12Z tim $ +% $Id: spm_cfg_eeg.m 7402 2018-08-17 16:18:12Z tim $ %-------------------------------------------------------------------------- % M/EEG preprocessing @@ -15,8 +16,7 @@ meegprep.values = {spm_cfg_eeg_epochs spm_cfg_eeg_prepare spm_cfg_eeg_montage spm_cfg_eeg_filter... spm_cfg_eeg_bc spm_cfg_eeg_artefact spm_cfg_eeg_downsample spm_cfg_eeg_merge... spm_cfg_eeg_fuse spm_cfg_eeg_combineplanar spm_cfg_eeg_reduce spm_cfg_eeg_crop... - spm_cfg_eeg_remove_bad_trials spm_cfg_eeg_spatial_confounds spm_cfg_eeg_correct_sensor_data... - spm_cfg_eeg_opmsetup}; + spm_cfg_eeg_remove_bad_trials spm_cfg_eeg_spatial_confounds spm_cfg_eeg_correct_sensor_data}; %-------------------------------------------------------------------------- % M/EEG averaging @@ -72,6 +72,15 @@ meegothr.help = {'M/EEG Other'}; meegothr.values = {spm_cfg_eeg_review, spm_cfg_eeg_copy, spm_cfg_eeg_delete}; +%-------------------------------------------------------------------------- +% OPM +%-------------------------------------------------------------------------- +meegopm = cfg_choice; +meegopm.tag = 'OPM'; +meegopm.name = 'OPM Preprocessing'; +meegopm.help = {'OPM Preprocessing'}; +meegopm.values = {spm_cfg_eeg_opmsetup,spm_cfg_opm_read_lvm, spm_cfg_opm_create,spm_cfg_opm_epoch_trigger,spm_cfg_opm_synth_gradiometer}; + %-------------------------------------------------------------------------- % M/EEG %-------------------------------------------------------------------------- @@ -79,4 +88,4 @@ meeg.tag = 'meeg'; meeg.name = 'M/EEG'; meeg.help = {'M/EEG functions.'}; -meeg.values = {spm_cfg_eeg_convert meegprep meegavg meegimg meegtf source meegmodel meegothr}; +meeg.values = {spm_cfg_eeg_convert meegprep meegavg meegimg meegtf source meegmodel meegopm meegothr}; diff --git a/config/spm_cfg_eeg_dipfit.m b/config/spm_cfg_eeg_dipfit.m index 25b251d6..316299ed 100644 --- a/config/spm_cfg_eeg_dipfit.m +++ b/config/spm_cfg_eeg_dipfit.m @@ -160,6 +160,7 @@ usesamples=intersect(find(D.time>=job.woi(1)./1000),find(D.time<=job.woi(2)./1000)); + condind=strmatch(job.whatconditions.condlabel,D.conditions) @@ -297,8 +298,8 @@ inverse=[]; inverse.F=maxF; -inverse.modelmniloc=mniloc{maxind}; -inverse.modelmnimom=megmom{maxind}; +inverse.modelmniloc=mniloc{maxind}(1:3)'; +inverse.modelmnimom=megmom{maxind}(1:3)'; inverse.Pout=Pout(maxind); inverse.P=P; D.inv{job.val}.inverse=inverse; @@ -309,9 +310,12 @@ subplot(3,2,1); plot(inverse.Pout.y,inverse.Pout.ypost,'o',inverse.Pout.y,inverse.Pout.y,':'); xlabel('measured');ylabel('modelled'); +title(sprintf('Free energy=%3.2f',inverse.F)); + + axesY = axes(... - 'Position',[0.02 0.3 0.3 0.2],... + 'Position',[0.1 0.4 0.3 0.2],... 'hittest','off'); in.f = hf; in.noButtons = 1; @@ -322,13 +326,51 @@ title(axesY,'measured data') axesY = axes(... - 'Position',[0.5 0.3 0.6 0.2],... + 'Position',[0.5 0.4 0.3 0.2],... 'hittest','off'); in.ParentAxes=axesY; spm_eeg_plotScalpData(inverse.Pout.ypost,D.coor2D',P.channels,in) -title(axesY,'Modelled data') - +title(axesY,'Modelled data'); + +subplot(3,3,7); hold on; + +M=D.inv{val}.mesh.tess_mni; +h=trisurf(M.face,M.vert(:,1),M.vert(:,2),M.vert(:,3)); +set(h,'Facecolor','cyan'); +set(h,'Edgecolor','none'); +alpha(0.1) +plot3(inverse.modelmniloc(:,1),inverse.modelmniloc(:,2),inverse.modelmniloc(:,3),'k*'); +view([0 0 1]); + + +subplot(3,3,8); hold on; +M=D.inv{val}.mesh.tess_mni; +h=trisurf(M.face,M.vert(:,1),M.vert(:,2),M.vert(:,3)); +set(h,'Facecolor','cyan'); +set(h,'Edgecolor','none'); +alpha(0.1) +plot3(inverse.modelmniloc(:,1),inverse.modelmniloc(:,2),inverse.modelmniloc(:,3),'k*'); + +view([0 1 0]); + +subplot(3,3,9); hold on; +M=D.inv{val}.mesh.tess_mni; +h=trisurf(M.face,M.vert(:,1),M.vert(:,2),M.vert(:,3)); +set(h,'Facecolor','cyan'); +set(h,'Edgecolor','none'); +alpha(0.1) +plot3(inverse.modelmniloc(:,1),inverse.modelmniloc(:,2),inverse.modelmniloc(:,3),'k*'); + +view([1 0 0]); + +subplot(3,3,7); +text(-50,-110,'MNI coord:'); +text(-50,-130,num2str(round(inverse.modelmniloc))); + +subplot(3,3,9); +text(0,-190,-40,'MNI mom:'); +text(0,-140,-50,num2str(round(inverse.modelmnimom))); if ~iscell(D) D = {D}; diff --git a/config/spm_cfg_eeg_inv_headmodel.m b/config/spm_cfg_eeg_inv_headmodel.m index b532dff1..30b092f5 100644 --- a/config/spm_cfg_eeg_inv_headmodel.m +++ b/config/spm_cfg_eeg_inv_headmodel.m @@ -4,7 +4,7 @@ % Copyright (C) 2010-2016 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_cfg_eeg_inv_headmodel.m 7169 2017-09-19 10:42:27Z vladimir $ +% $Id: spm_cfg_eeg_inv_headmodel.m 7304 2018-05-02 12:09:35Z guillaume $ D = cfg_files; @@ -173,10 +173,10 @@ fidjson = cfg_files; fidjson.tag = 'fidjson'; -fidjson.name = 'BIDS json file'; -fidjson.filter = '.*_fid.json$'; +fidjson.name = 'MRI BIDS json file'; +fidjson.filter = '.*.json$'; fidjson.num = [1 1]; -fidjson.help = {'Select BIDS json file with fiducials.'}; +fidjson.help = {'Select MRI BIDS json file with fiducials.'}; coregbids = cfg_branch; coregbids.tag = 'coregbids'; @@ -305,13 +305,10 @@ elseif isfield(job.coregistration, 'coregbids') meegfid = D.fiducials; - fidbids = spm_jsonread(char(job.coregistration.coregbids.fidjson)); + fidbids = spm_jsonread(char(job.coregistration.coregbids.fidjson)); + % Coordinates are always in voxel units - if ~(isa(sMRI, 'char') && isequal(spm_file(sMRI, 'basename'), spm_file(fidbids.IntendedFor, 'basename'))) - warning('The BIDS fiducials might be intended for a different structural image than used here.'); - end - - fidlabel = fieldnames(fidbids.CoilCoordinates); + fidlabel = fieldnames(fidbids.AnatomicalLandmarkCoordinates); selection = spm_match_str(meegfid.fid.label, fidlabel); meegfid.fid.pnt = meegfid.fid.pnt(selection, :); meegfid.fid.label = meegfid.fid.label(selection); @@ -321,9 +318,16 @@ mrifid.fid.pnt = []; mrifid.fid.label = fidlabel; + + pnt = []; for j = 1:numel(fidlabel) - mrifid.fid.pnt(j, :) = reshape(fidbids.CoilCoordinates.(fidlabel{j}), 1, 3); + pnt(j, :) = reshape(fidbids.AnatomicalLandmarkCoordinates.(fidlabel{j}), 1, 3); end + + M = getfield(nifti(D.inv{val}.mesh.sMRI), 'mat'); + + % In the convention used in BIDS the voxel indices start at 0 + mrifid.fid.pnt = spm_eeg_inv_transform_points(M, pnt+1); D = spm_eeg_inv_datareg_ui(D, D.val, meegfid, mrifid, job.coregistration.coregbids.useheadshape); else diff --git a/config/spm_cfg_eeg_opmsetup.m b/config/spm_cfg_eeg_opmsetup.m index 030c6790..59c12494 100644 --- a/config/spm_cfg_eeg_opmsetup.m +++ b/config/spm_cfg_eeg_opmsetup.m @@ -1,12 +1,10 @@ function setup = spm_cfg_eeg_opmsetup % Configuration file for M/EEG OPM set up %__________________________________________________________________________ -% Copyright (C) 2008-2016 Wellcome Trust Centre for Neuroimaging - -% $Id: spm_cfg_eeg_opmsetup.m 7207 2017-11-09 16:30:05Z gareth $ -% Tim Tierney's original code modified by Gareth Barnes - +% Copyright (C) 2017-2018 Wellcome Trust Centre for Neuroimaging +% Tim Tierney, Gareth Barnes +% $Id: spm_cfg_eeg_opmsetup.m 7429 2018-09-28 09:29:20Z spm $ R = cfg_files; @@ -120,13 +118,14 @@ setup.prog = @opmsetup; setup.vout = @vout_opmsetup; + +%========================================================================== +%-function out = opmsetup(job) %========================================================================== function out = opmsetup(job) - - - -%%%%%%%%%%%%%%%%%%%%% READ IN RECORDING SET UP FILE %%%%%%%%%%%%%%%%%%%%%% +%-READ IN RECORDING SET UP FILE +%========================================================================== % this table has defines which OPM sensor names go to which ADC channels % format: adchan\t opmname\n % use some default like XX for no channel connected, or delete redundant @@ -136,16 +135,18 @@ matLabel = readtable(recordingsetup,'ReadVariableNames',0,'Delimiter','\t'); %% This links the recorded data to sensor names adcind=matLabel{:,1}; fprintf('\nFound a total of %d listed connections\n',length(adcind)); -adcOPMs=table2array(matLabel(:,2)); %% OPM names attached to the ADC channels +adcOPMs=table2array(matLabel(:,2)); % OPM names attached to the ADC channels timeind=strmatch('Time',adcOPMs,'exact'); -if isempty(timeind), +if isempty(timeind) warning('No time channel found, assuming it is channel 1'); else fprintf('\n Found time channel on %d',timeind); -end; -%%%%%%%%%%%%% READ IN SCANNER-CAST SPECIFYING FILE %%%%%%%%%%%%%%%%%%% -%% gives where the slots are in native space and which OPM channels were in these slots -%% each slot has position and orienttation columns 1:3 columns 4:6 plus a column 7 +end + +%-READ IN SCANNER-CAST SPECIFYING FILE +%========================================================================== +% gives where the slots are in native space and which OPM channels were in these slots +% each slot has position and orienttation columns 1:3 columns 4:6 plus a column 7 castsetup=cell2mat(job.C); fprintf('\n Reading scanner cast file %s ', castsetup); cast = readtable(castsetup,'ReadVariableNames',0,'Delimiter','\t'); %% this is a scanner-cast specific file @@ -157,47 +158,48 @@ fprintf('\n Names '); -%%% NOW READ IN OPM2SLOT FILE- this is the file that will change most often -%%% and links the opms to specific slots in scanner-cast -%%% format : opmname\t slotname\n -%%% use REF in 2nc column to define a reference channel (i.e. used but not in cast) +%-NOW READ IN OPM2SLOT FILE +%========================================================================== +% this is the file that will change most often +% and links the opms to specific slots in scanner-cast +% format : opmname\t slotname\n +% use REF in 2nc column to define a reference channel (i.e. used but not in cast) opm2slotfile=cell2mat(job.RC); opm2slot = readtable(opm2slotfile,'ReadVariableNames',0,'Delimiter','\t'); %% this how the OPM channels were slotted into the cast opm2slot=table2array(opm2slot); fprintf('\n*****************************\n') fprintf('\nOPM\t\tSlot\tADC chan\n') -for f=1:size(opm2slot,1), +for f=1:size(opm2slot,1) fprintf('%s\t\t',opm2slot{f,1}); - if ~(strcmp(opm2slot{f,2},'REF')), + if ~(strcmp(opm2slot{f,2},'REF')) slotind(f)=strmatch(opm2slot{f,2},slotname); %% find the OPM slot name in the scannercastfile (with positions and orientations) - if isempty(slotind), + if isempty(slotind) error('Could not find slot %s in cast file',opm2slot(f,2)); - end; + end fprintf('%s\t\t',slotname{slotind(f)}); else slotind(f)=-1; %% no scanner-cast slot as this is a reference fprintf('REF\t\t'); - end; + end listind=strmatch(opm2slot{f,1},adcOPMs,'exact'); %% find OPM channel in list of ADC connections (in pinout file) - if isempty(listind), + if isempty(listind) error('Could not find OPM %s in OPM to ADC file',opm2slot(f,1)); - end; + end opmadcind(f)=adcind(listind); %% adc channel corresponding to opm opm2slot(f,1) fprintf('%d\n',opmadcind(f)); -end; +end fprintf('\n*****************************\n') megind=find(slotind>0); %% indices of OPM chans in headcast - - -%%%%%%%%%%%% READ IN LABVIEW BINARY FILE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% containing the actual recorded data for OPMs and trigger channels +%-READ IN LABVIEW BINARY FILE +%========================================================================== +% containing the actual recorded data for OPMs and trigger channels labviewfile=cell2mat(job.lbv); [a1,b1,c1]=fileparts(labviewfile); fprintf('\nReading binary file %s', labviewfile); -[time,B]=eb_read_lvm(labviewfile,timeind); +[time,B]=spm_opm_read_lvm(labviewfile,timeind); srate=1./mean(diff(time)); fprintf('\nRead %d samples of %d channels at %3.2fHz sampling rate\n',size(B,1),size(B,2),srate); % every second column is a radial sensor @@ -210,44 +212,47 @@ outbase=sprintf('spm_%s_%s.dat',job.prefix,b1); %% name of output file -%% convert to SPM -if exist(outbase), +%-Convert to SPM +%-------------------------------------------------------------------------- +if exist(outbase) warning('SPM file already exists, OVERWRITING'); D=spm_eeg_load(outbase); D.delete; -end; +end D = spm_opm_convert(megData,outbase,srate,job.convert); -%% set appropriate Channel labels and types +%-Set appropriate Channel labels and types +%-------------------------------------------------------------------------- Nopmchans=size(opm2slot,1); D = chanlabels(D,1:Nopmchans,opm2slot(:,1)); D = chantype(D,megind,'MEG'); D = chantype(D,find(slotind==-1),'Ref'); D=chantype(D,length(opmadcind)+1:length(opmadcind)+length(alltrigs),'Trig') -for f=1:length(alltrigs), +for f=1:length(alltrigs) D=chanlabels(D,length(opmadcind)+1:length(opmadcind)+length(alltrigs),sprintf('tr%d',alltrigs(f))); -end; +end save(D); -%% slot positions and orientation into MEG object +%-Slot positions and orientation into MEG object +%========================================================================== % this table maps positions/orientations to headcast numbers and sensor % labels if length(megind)~=size(posor,1) error('File size mismatch'); -end; +end figure; hold on; grad.label = opm2slot(megind,1); %% names of the MEG channels in headcast megpos=[];meglabels=[]; -for f=1:length(megind), %% megind indexes into slotind which gives the index into posor +for f=1:length(megind) %% megind indexes into slotind which gives the index into posor castind=slotind(megind(f)); grad.coilpos(f,:) = posor(castind,1:3); grad.coilori(f,:) = posor(castind,4:6); %plot3(grad.coilpos(f,1),grad.coilpos(f,2),grad.coilpos(f,3),'r.'); %text(grad.coilpos(f,1),grad.coilpos(f,2),grad.coilpos(f,3),grad.label{f}); -end; +end grad.tra = eye(numel(grad.label)); @@ -259,17 +264,18 @@ D = sensors(D, 'MEG', grad); -%% ORIENT SENSORS IN 2D BASED ON MEAN ORIENTATION +%-ORIENT SENSORS IN 2D BASED ON MEAN ORIENTATION +%========================================================================== n1=mean(grad.coilori); n1= n1./sqrt(dot(n1,n1)); t1=cross(n1,[0 0 1]); t2=cross(t1,n1); -for f=1:length(megind), +for f=1:length(megind) pos2d(f,1)=dot(grad.coilpos(f,:),t1); pos2d(f,2)=dot(grad.coilpos(f,:),t2); plot(pos2d(f,1),pos2d(f,2),'r.'); text(pos2d(f,1),pos2d(f,2),grad.label{f}); -end; +end S=[]; %% SET SENSOR 2D POS S.D=D; @@ -287,9 +293,10 @@ D.save; -%%%%%%%%%%%%%% NOW COREGISTER - easiest to do this here as the opms and -%%%%%%%%%%%%%% head are defined in same native mri space -%% working from native to native space so fiducials should give identity transform +%-NOW COREGISTER +%========================================================================== +% easiest to do this here as the opms and head are defined in same native mri space +% working from native to native space so fiducials should give identity transform newfid=[]; newfid.fid.label = {'nas', 'lpa', 'rpa'}'; @@ -298,7 +305,9 @@ mri_fids=newfid.fid.pnt; %%% the MRI fiducials will be the same as the MEG as everything is defined in native space for OPMs D = fiducials(D, newfid); save(D); -%%%%% now write out a bids format file to be read by coreg procedure + +%-now write out a bids format file to be read by coreg procedure +%-------------------------------------------------------------------------- [a1,b1,c1]=fileparts(cell2mat(job.M)); json_fid = []; @@ -334,13 +343,14 @@ -%% bandpass filter +%-bandpass filter +%-------------------------------------------------------------------------- fprintf('\n Filtering %d to %d Hz \n', job.band(1), job.band(2)); S = []; S.D = D; S.type = 'butterworth'; S.band = 'bandpass'; -S.freq = job.band; +S.freq = [job.band(1) job.band(2)]; S.dir = 'twopass'; S.order = 2; nD = spm_eeg_filter(S); @@ -352,20 +362,20 @@ ev=[];cond={}; -figure; %% ASSUME ONE BIT PER TRIG CHANNEL FOR NOW AND THRESHOLD AT 2* std deviation +figure; % ASSUME ONE BIT PER TRIG CHANNEL FOR NOW AND THRESHOLD AT 2* std deviation -for f=1:length(alltrigs), +for f=1:length(alltrigs) subplot(length(alltrigs),1,f); thresh=std((B(:,alltrigs(f))))*2; trigs=find(diff(B(:,alltrigs(f))>thresh)==1)+1; plot(D.time,B(:,alltrigs(f))',D.time,ones(size(D.time))*thresh,':',D.time(trigs),ones(size(trigs))*thresh*2,'r*'); legend(num2str(alltrigs(f)),'thresh','events'); xlabel('time');ylabel('adc value') title('trigger'); - for j=1:length(trigs), + for j=1:length(trigs) cond{j+length(ev)}=num2str(alltrigs(f)); - end; + end ev = [ev; trigs]; %% trigger samples -end; +end fprintf('\n Epoching'); offsettime=job.epwin(1)/1000; @@ -375,7 +385,7 @@ begsample=ev+offsetsamples; offset=offsetsamples.*ones(size(begsample)); endsample=begsample+durationsamples; -%% should be in format : begin sample, end sample, offset +% should be in format : begin sample, end sample, offset trl = round([begsample endsample offset]); S = []; S.D = nD; @@ -386,7 +396,8 @@ eD = spm_eeg_epochs(S); -%% reference noise cancellation +%-Reference noise cancellation +%-------------------------------------------------------------------------- refInd = selectchannels(eD,'Ref'); %%spm_opm_denoise(D,refD,derivative,gs,update,prefix) fprintf('\n Reference noise cancellation with flags %d %d', job.ncan(1),job.ncan(2)); @@ -402,6 +413,9 @@ fprintf('\n Finished setup'); +%========================================================================== +%-function dep = vout_opmsetup(job) +%========================================================================== function dep = vout_opmsetup(job) % return dependencies % Output is always in field "D", no matter how job is structured diff --git a/config/spm_cfg_opm_create.m b/config/spm_cfg_opm_create.m new file mode 100644 index 00000000..af4d29ba --- /dev/null +++ b/config/spm_cfg_opm_create.m @@ -0,0 +1,337 @@ +function create = spm_cfg_opm_create +% configuration file for creating OPM objects +%__________________________________________________________________________ +% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging + +% Tim Tierney +% $Id: spm_cfg_opm_create.m 7429 2018-09-28 09:29:20Z spm $ + +%-------------------------------------------------------------------------- +% Output Directory +%-------------------------------------------------------------------------- +outdir = cfg_files; +outdir.tag = 'outdir'; +outdir.name = 'Output directory'; +outdir.val{1} = {''}; +outdir.help = {'Files produced by this function will be written into this output directory. If no directory is given, images will be written to current working directory.'}; +outdir.filter = 'dir'; +outdir.ufilter = '.*'; +outdir.num = [0 1]; +%-------------------------------------------------------------------------- +% Output filename +%-------------------------------------------------------------------------- +fname = cfg_entry; +fname.tag = 'fname'; +fname.name = 'File name (.dat)'; +fname.help = {'File name, e.g. OPM.dat. If left empty this will be the name by default'}; +fname.strtype = 's'; +fname.num = [1,Inf]; +fname.val = {'OPM.dat'}; + +fifo = cfg_branch; +fifo.tag = 'fifo'; +fifo.name = 'File management'; +fifo.help = {'The output files and directories'}; +fifo.val = {outdir,fname}; +%-------------------------------------------------------------------------- +% Data +%-------------------------------------------------------------------------- +data = cfg_files; +data.tag = 'data'; +data.name = 'Labview .mat file'; +data.filter = '.*.mat$'; +data.num = [1 1]; +data.help = {'Select the labview.mat file. If left empty an empty dataset will be created according to the simulation parameters'}; +data.val = {{''}}; +%-------------------------------------------------------------------------- +% Sampling Frequency +%-------------------------------------------------------------------------- +fs = cfg_entry; +fs.tag = 'fs'; +fs.name = 'Sampling Frequency'; +fs.help = {'The sampling frequency of the data in Hz'}; +fs.strtype = 'r'; +fs.num = [1,1]; +fs.val = {1200}; +%-------------------------------------------------------------------------- +% Scale Factor +%-------------------------------------------------------------------------- +scale = cfg_entry; +scale.tag = 'scale'; +scale.name = 'Scale Factor'; +scale.help = {'Scale factor (multiplied by data)to convert to units of fT'}; +scale.strtype = 'r'; +scale.num = [1,1]; +scale.val = {1e6/2.7}; + +dataset = cfg_branch; +dataset.tag = 'dataset'; +dataset.name = 'Dataset'; +dataset.help = {'Provide basic information for dataset'}; +dataset.val = {data,fs,scale}; + +%-------------------------------------------------------------------------- +% Pinout +%-------------------------------------------------------------------------- +pinout = cfg_files; +pinout.tag = 'pinout'; +pinout.name = 'Pinout File'; +pinout.filter = '.*.txt'; +pinout.num = [1 Inf]; +pinout.help = {'Select the tab separated pinout .txt file.'}; +pinout.val = {{''}}; +%-------------------------------------------------------------------------- +% OPM 2 Cast File +%-------------------------------------------------------------------------- +sensorsUsed = cfg_files; +sensorsUsed.tag = 'sensorsUsed'; +sensorsUsed.name = 'OPM to Cast File'; +sensorsUsed.filter = '.*.txt'; +sensorsUsed.num = [1 Inf]; +sensorsUsed.help = {'Select the tab separated OPM to cast .txt file.(This is Scannercast and experiment specific)'}; +sensorsUsed.val = {{''}}; +%-------------------------------------------------------------------------- +% Pos file +%-------------------------------------------------------------------------- +pos = cfg_files; +pos.tag = 'pos'; +pos.name = 'Scannercast specific position file '; +pos.filter = '.*.txt'; +pos.num = [1 Inf]; +pos.help = {'Select the tab separated position .txt file .'}; +pos.val={{''}}; + +sens = cfg_branch; +sens.tag = 'sens'; +sens.name = 'Sensor Information'; +sens.help = {'Provide custom .txt files to describe sensor types and positions'}; +sens.val = {pinout,sensorsUsed,pos}; + +%-------------------------------------------------------------------------- +% sMRI +%-------------------------------------------------------------------------- +sMRI = cfg_files; +sMRI.tag = 'sMRI'; +sMRI.name = 'Individual structural image'; +sMRI.filter = 'image'; +sMRI.ufilter = '.*'; +sMRI.num = [1 1]; +sMRI.help = {'Select the subject''s structural image. Leave emptyp to create an OPM object without a forward model'}; +sMRI.val = {{''}}; +%-------------------------------------------------------------------------- +% Meshes +%-------------------------------------------------------------------------- +cortex = cfg_files; +cortex.tag = 'cortex'; +cortex.name = 'Custom cortical mesh'; +cortex.filter = 'mesh'; +cortex.ufilter = '.*'; +cortex.num = [0 1]; +cortex.help = {'Select the subject''s cortical mesh. Leave empty for default'}; +cortex.val = {{''}}; + +iskull = cfg_files; +iskull.tag = 'iskull'; +iskull.name = 'Custom inner skull mesh'; +iskull.filter = 'mesh'; +iskull.ufilter = '.*'; +iskull.num = [0 1]; +iskull.help = {'Select the subject''s inner skull mesh. Leave empty for default'}; +iskull.val = {{''}}; + +oskull = cfg_files; +oskull.tag = 'oskull'; +oskull.name = 'Custom outer skull mesh'; +oskull.filter = 'mesh'; +oskull.ufilter = '.*'; +oskull.num = [0 1]; +oskull.help = {'Select the subject''s outer skull mesh. Leave empty for default'}; +oskull.val = {{''}}; + +scalp = cfg_files; +scalp.tag = 'scalp'; +scalp.name = 'Custom scalp mesh'; +scalp.filter = 'mesh'; +scalp.ufilter = '.*'; +scalp.num = [0 1]; +scalp.help = {'Select the subject''s scalp mesh. Leave empty for default'}; +scalp.val = {{''}}; + +custom = cfg_branch; +custom.tag = 'custom'; +custom.name = 'Custom meshes'; +custom.help = {'Provide custom individual meshes as GIfTI files'}; +custom.val = {cortex, iskull, oskull, scalp}; + +meshres = cfg_menu; +meshres.tag = 'meshres'; +meshres.name = 'Mesh resolution'; +meshres.help = {'Specify the resolution of the cortical mesh'}; +meshres.labels = {'coarse', 'normal', 'fine'}; +meshres.values = {1, 2, 3}; +meshres.val = {2}; + +meshing = cfg_branch; +meshing.tag = 'meshing'; +meshing.name = 'Meshes'; +meshing.help = {'Create head meshes for building the head model'}; +meshing.val = {sMRI,custom, meshres}; + + +%-------------------------------------------------------------------------- +% Volume Conducter +%-------------------------------------------------------------------------- +voltype = cfg_menu; +voltype.tag = 'voltype'; +voltype.name = 'MEG head model'; +voltype.help = {'Select the head model type to use for MEG (if present)'}; +voltype.labels = {'Single Sphere', 'MEG Local Spheres', 'Single Shell'}; +voltype.values = {'Single Sphere', 'MEG Local Spheres', 'Single Shell'}; +voltype.val = {'Single Shell'}; + + +%-------------------------------------------------------------------------- +% simulation parameters +%-------------------------------------------------------------------------- + +wholehead = cfg_entry; +wholehead.tag = 'wholehead'; +wholehead.name = 'Sensor Coverage'; +wholehead.help = {'If value is set to 1 then sensors will be placed all over scalp surface. '}; +wholehead.strtype = 'r'; +wholehead.num = [1,1]; +wholehead.val = {1}; + +space = cfg_entry; +space.tag = 'space'; +space.name = 'Desired Space between Sensors'; +space.help = {'This is only an approximation, units are mm'}; +space.strtype = 'r'; +space.num = [1,1]; +space.val = {25}; + +offset = cfg_entry; +offset.tag = 'offset'; +offset.name = 'Sensor Offset'; +offset.help = {'Scalp to sensor Distance'}; +offset.strtype = 'r'; +offset.num = [1,1]; +offset.val = {6.5}; + +nSamples = cfg_entry; +nSamples.tag = 'nSamples'; +nSamples.name = 'Number of samples'; +nSamples.help = {''}; +nSamples.strtype = 'r'; +nSamples.num = [1,1]; +nSamples.val = {1000}; + +lead = cfg_entry; +lead.tag = 'lead'; +lead.name = 'Compute Lead Field'; +lead.help = {'If value is set to 1 then a lead field will be compted and saved'}; +lead.strtype = 'r'; +lead.num = [1,1]; +lead.val = {0}; + +simulation = cfg_branch; +simulation.tag = 'simulation'; +simulation.name = 'Simulation Parameters'; +simulation.help = {'Parameters for simulating OPM data. will be ignored if a dataset has alread been supplied'}; +simulation.val = {wholehead,space,offset,nSamples,lead}; + +%-------------------------------------------------------------------------- +% simulation parameters +%-------------------------------------------------------------------------- +create = cfg_exbranch; +create.tag = 'create'; +create.name = 'Create OPM object'; +create.val = {fifo,dataset,sens,meshing,voltype,simulation}; +create.help = {'Create/simulate OPM data. All arguments for this function are optional. It allows for either a conversion of raw data to valid MEG object or for the simulation of OPM data on a template brain or an individual brain with customisable sensor configurations.'}'; +create.prog = @opm_create; +create.vout = @vout_opm_create; +create.modality = {'EEG'}; + + +%========================================================================== +function out = opm_create(job) +% construct the S struct + +% datset parameters +S=[]; +S.data=job.dataset.data{1}; +S.fs=job.dataset.fs; +S.scale= job.dataset.scale; + +% sensor information +S.pinout =job.sens.pinout{1}; +S.pos=job.sens.pos{1}; +S.sensorsUsed= job.sens.sensorsUsed{1}; + +% meshses +S.cortex = job.meshing.custom.cortex; +S.iskull = job.meshing.custom.iskull; +S.oskull = job.meshing.custom.oskull; +S.scalp = job.meshing.custom.scalp; +S.meshres = job.meshing.meshres; +S.sMRI = job.meshing.sMRI{1}; +S.voltype = job.voltype; + +% simulation +S.lead= job.simulation.lead; +S.nSamples = job.simulation.nSamples; +S.offset = job.simulation.offset; +S.space = job.simulation.space; +S.wholehead = job.simulation.wholehead; + +% set filename(will be udpate later if necessary) +S.fname = fullfile(job.fifo.outdir{1},job.fifo.fname); + +% check if field should be removed +argumentFields = fields(S); +numFields = length(argumentFields); +notUsed = zeros(numFields,1); + +for i = 1:numFields + notUsed(i) = iscell(S.(argumentFields{i})); +end + +% remove the unused fields +fieldsToRemove = {argumentFields{boolean(notUsed),1}}; +S = rmfield(S,fieldsToRemove); + + +% check if dataset has been provided, load and rename. +if(isfield(S, 'data')) + load(job.dataset.data{1}); + S.data= data.B'; + trigs = (size(data.decimalTrigs,2)+size(data.binaryTrigs,2))>0; + if(trigs) + S.trig= [data.decimalTrigs,data.binaryTrigs]'; + end + [a,b,c]=fileparts(job.dataset.data{1}); + outfile= fullfile(a,['SPM_',b,'.dat']); + S.fname = outfile; +end + +% run the main function +out.D= spm_opm_create(S); +out.Dfname = {fullfile(out.D.path, out.D.fname)}; + + +%========================================================================== +function dep = vout_opm_create(job) +% return dependencies +dep = cfg_dep; +dep.sname = 'OPM Data'; +% reference field "D" from output +dep.src_output = substruct('.','D'); +% this can be entered into any evaluated input +dep.tgt_spec = cfg_findspec({{'strtype','e'}}); + +dep(2) = cfg_dep; +dep(2).sname = 'OPM Datafile'; +% reference field "Dfname" from output +dep(2).src_output = substruct('.','Dfname'); +% this can be entered into any file selector +dep(2).tgt_spec = cfg_findspec({{'filter','mat'}}); diff --git a/config/spm_cfg_opm_epoch_trigger.m b/config/spm_cfg_opm_epoch_trigger.m new file mode 100644 index 00000000..95e2f1ac --- /dev/null +++ b/config/spm_cfg_opm_epoch_trigger.m @@ -0,0 +1,92 @@ +function epoch = spm_cfg_opm_epoch_trigger +% configuration file for epoching OPM data +%__________________________________________________________________________ +% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging + +% Tim Tierney +% $Id: spm_cfg_opm_epoch_trigger.m 7429 2018-09-28 09:29:20Z spm $ + +%-------------------------------------------------------------------------- +% Output Directory +%-------------------------------------------------------------------------- +D = cfg_files; +D.tag = 'D'; +D.name = 'File Name'; +D.filter = 'mat'; +D.num = [1 1]; +D.help = {'Select the M/EEG mat file.'}; + +%-------------------------------------------------------------------------- +% Output filename +%-------------------------------------------------------------------------- +timewin = cfg_entry; +timewin.tag = 'timewin'; +timewin.name = 'Time window'; +timewin.help = {'Time window around trigger which to epoch(ms). e.g [-200,300]'}; +timewin.strtype = 'r'; +timewin.num = [Inf,2]; + +%-------------------------------------------------------------------------- +% Condition Labels +%-------------------------------------------------------------------------- +condLabels = cfg_entry; +condLabels.tag = 'condLabels'; +condLabels.name = 'Condition Labels'; +condLabels.help = {'Labels of conditions. Enter each label on a new line. If left empty the default behaviout is to label conditions accrding to number(e.g. Cond1,Cond2,...)'}; +condLabels.strtype = 's+'; +condLabels.num = [1,100]; +condLabels.val = {{''}}; + + +%-------------------------------------------------------------------------- +% simulation parameters +%-------------------------------------------------------------------------- +epoch = cfg_exbranch; +epoch.tag = 'epoch'; +epoch.name = 'Epoch M/EEG object on Trigger'; +epoch.val = {D,timewin,condLabels}; +epoch.help = {'Epoch M/EEG data at the rise of every trigger in the dataset'}'; +epoch.prog = @epoch_trigger; +epoch.vout = @vout_epoch_trigger; +epoch.modality = {'EEG'}; + + +%========================================================================== +function out = epoch_trigger(job) +% construct the S struct + +% datset parameters +S=[]; +S.D= spm_eeg_load(job.D{1}); +S.timewin= job.timewin; +S.condLabels = job.condLabels; + +% check if condLabels is empty +defaultLabels = strcmp(S.condLabels{1},''); + +% remove the unused fields +if(defaultLabels) +S = rmfield(S,'condLabels'); +end + +% run the main function +out.D= spm_opm_epoch_trigger(S); +out.Dfname = {fullfile(out.D.path, out.D.fname)}; + + +%========================================================================== +function dep = vout_epoch_trigger(job) +% return dependencies +dep = cfg_dep; +dep.sname = 'Epoched Data'; +% reference field "D" from output +dep.src_output = substruct('.','D'); +% this can be entered into any evaluated input +dep.tgt_spec = cfg_findspec({{'strtype','e'}}); + +dep(2) = cfg_dep; +dep(2).sname = 'Epoched Datafile'; +% reference field "Dfname" from output +dep(2).src_output = substruct('.','Dfname'); +% this can be entered into any file selector +dep(2).tgt_spec = cfg_findspec({{'filter','mat'}}); diff --git a/config/spm_cfg_opm_read_lvm.m b/config/spm_cfg_opm_read_lvm.m new file mode 100644 index 00000000..cd08c34f --- /dev/null +++ b/config/spm_cfg_opm_read_lvm.m @@ -0,0 +1,106 @@ +function labview = spm_cfg_opm_read_lvm +% configuration file for reading lab view file +%__________________________________________________________________________ +% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging + +% Tim Tierney +% $Id: spm_cfg_opm_read_lvm.m 7429 2018-09-28 09:29:20Z spm $ + +%-------------------------------------------------------------------------- +% labview file +%-------------------------------------------------------------------------- +filename = cfg_files; +filename.tag = 'filename'; +filename.name = 'File Name'; +filename.filter = '(.lvm|.zip)'; +filename.num = [1 1]; +filename.help = {'Select the (zipped) lvm file.'}; + + +%-------------------------------------------------------------------------- +% headerlength +%-------------------------------------------------------------------------- +headerlength = cfg_entry; +headerlength.tag = 'headerlength'; +headerlength.name = 'No. of header lines'; +headerlength.help = {'The number of lines of text containing header information'}; +headerlength.strtype = 'r'; +headerlength.num = [1,1]; +headerlength.val = {23}; + + + +%-------------------------------------------------------------------------- +% timeind +%-------------------------------------------------------------------------- +timeind = cfg_entry; +timeind.tag = 'timeind'; +timeind.name = 'Time Index'; +timeind.help = {'The column number of the time variable'}; +timeind.strtype = 'r'; +timeind.num = [1,1]; +timeind.val = {1}; + +%-------------------------------------------------------------------------- +% Decimal Triggers +%-------------------------------------------------------------------------- +decimalTriggerInds = cfg_entry; +decimalTriggerInds.tag = 'decimalTriggerInds'; +decimalTriggerInds.name = 'Index of Decimal Triggers'; +decimalTriggerInds.help = {'Column numbers of channels that should be interpreted as decimal triggers. '}; +decimalTriggerInds.strtype = 'r'; +decimalTriggerInds.num = [0,0]; +decimalTriggerInds.val = {74:81}; + +%-------------------------------------------------------------------------- +% Binary Triggers +%-------------------------------------------------------------------------- +binaryTriggerInds = cfg_entry; +binaryTriggerInds.tag = 'binaryTriggerInds'; +binaryTriggerInds.name = 'Index of Binary Triggers'; +binaryTriggerInds.help = {'Column numbers of channels that should be interpreted as binary triggers'}; +binaryTriggerInds.strtype = 'r'; +binaryTriggerInds.num = [0,0]; +binaryTriggerInds.val = {[]}; + +%-------------------------------------------------------------------------- +% Trigger Threshld +%-------------------------------------------------------------------------- +trigThresh = cfg_entry; +trigThresh.tag = 'trigThresh'; +trigThresh.name = 'Trigger Threshold'; +trigThresh.help = {'Threshold to apply to triggers. Currently the default has units of Volts.'}; +trigThresh.strtype = 'r'; +trigThresh.num = [1,1]; +trigThresh.val = {4}; + +%-------------------------------------------------------------------------- +% read +%-------------------------------------------------------------------------- +labview = cfg_exbranch; +labview.tag = 'labview'; +labview.name = 'Read LabView Files'; +labview.val = {filename,headerlength,timeind,decimalTriggerInds,binaryTriggerInds,trigThresh}; +labview.help = {'Reading LabView data'}'; +labview.prog = @lbv_read; +labview.vout = @vout_lbv_read; +labview.modality = {'EEG'}; + + +%========================================================================== +function labview = lbv_read(job) +data = spm_opm_read_lvm(job); + +outfile = spm_file(job.filename{1},'ext','.mat'); +save(outfile,'data'); + +labview.data = {outfile}; + + +%========================================================================== +function dep = vout_lbv_read(job) +% return dependencies +dep = cfg_dep; +dep.sname = 'Prepared Labview Data'; +dep.src_output = substruct('.','data'); +dep.tgt_spec = cfg_findspec({{'filter','mat'}}); diff --git a/config/spm_cfg_opm_synth_gradiometer.m b/config/spm_cfg_opm_synth_gradiometer.m new file mode 100644 index 00000000..6b47c63f --- /dev/null +++ b/config/spm_cfg_opm_synth_gradiometer.m @@ -0,0 +1,84 @@ +function denoise = spm_cfg_opm_synth_gradiometer +% configuration file for performing synthetic gradiometery on OPM data +%__________________________________________________________________________ +% Copyright (C) 2018 Wellcome Trust Centre for Neuroimaging + +% Tim Tierney +% $Id: spm_cfg_opm_synth_gradiometer.m 7429 2018-09-28 09:29:20Z spm $ + +%-------------------------------------------------------------------------- +% Input Dataset +%-------------------------------------------------------------------------- +D = cfg_files; +D.tag = 'D'; +D.name = 'File Name'; +D.filter = 'mat'; +D.num = [1 1]; +D.help = {'Select the M/EEG mat file.'}; + +%-------------------------------------------------------------------------- +% Confounds +%-------------------------------------------------------------------------- +confounds = cfg_entry; +confounds.tag = 'confounds'; +confounds.name = 'Confounds'; +confounds.help = {'Labels of channel types to use for denoising. Will default to REF to use reference sensors for denoising'}; +confounds.strtype = 's+'; +confounds.num = [1,100]; +confounds.val = {{'REF'}}; +%-------------------------------------------------------------------------- +% derivatives +%-------------------------------------------------------------------------- +derivative = cfg_menu; +derivative.tag = 'derivative'; +derivative.name = 'Derivatives'; +derivative.help = {'Boolean to specify whether Derivatices should be used for denoising. Default is true'}; +derivative.labels = {'TRUE', 'FALSE'}; +derivative.values = {1, 0}; +derivative.val = {1}; + + +%-------------------------------------------------------------------------- +% simulation parameters +%-------------------------------------------------------------------------- +denoise = cfg_exbranch; +denoise.tag = 'denoise'; +denoise.name = 'Synthetic Gradiometery'; +denoise.val = {D,confounds,derivative}; +denoise.help = {'Denoise will regress all channels of the selected type(s) from the input dataset. Optionally the derivatives of the selected type(s) can be used as well. This funciton wil automatically regress on a trial by trial basis or accross the whole sesison based on whether or not the dataset has been epoched.'}'; +denoise.prog = @synth_gradiometer; +denoise.vout = @vout_synth_gradiometer; +denoise.modality = {'EEG'}; + + +%========================================================================== +function out = synth_gradiometer(job) +% construct the S struct + +% datset parameters +S=[]; +S.D= spm_eeg_load(job.D{1}); +S.confounds= job.confounds; +S.derivative = job.derivative; + +% run the main function +out.D= spm_opm_synth_gradiometer(S); +out.Dfname = {fullfile(out.D.path, out.D.fname)}; + + +%========================================================================== +function dep = vout_synth_gradiometer(job) +% return dependencies +dep = cfg_dep; +dep.sname = 'Denoised Data'; +% reference field "D" from output +dep.src_output = substruct('.','D'); +% this can be entered into any evaluated input +dep.tgt_spec = cfg_findspec({{'strtype','e'}}); + +dep(2) = cfg_dep; +dep(2).sname = 'Denoised Datafile'; +% reference field "Dfname" from output +dep(2).src_output = substruct('.','Dfname'); +% this can be entered into any file selector +dep(2).tgt_spec = cfg_findspec({{'filter','mat'}}); diff --git a/config/spm_cfg_results.m b/config/spm_cfg_results.m index cc81c73c..e3d4eb1a 100644 --- a/config/spm_cfg_results.m +++ b/config/spm_cfg_results.m @@ -1,9 +1,9 @@ function results = spm_cfg_results % SPM Configuration file for Results Report %__________________________________________________________________________ -% Copyright (C) 2005-2016 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2005-2018 Wellcome Trust Centre for Neuroimaging -% $Id: spm_cfg_results.m 6952 2016-11-25 16:03:13Z guillaume $ +% $Id: spm_cfg_results.m 7268 2018-02-27 16:48:13Z guillaume $ %-------------------------------------------------------------------------- @@ -214,6 +214,37 @@ basename.strtype = 's'; basename.num = [1 Inf]; +%-------------------------------------------------------------------------- +% background Background image +%-------------------------------------------------------------------------- +background = cfg_files; +background.tag = 'background'; +background.name = 'Background image'; +background.help = {'Background image.'}; +background.filter = {'image'}; +background.ufilter = '.*'; +background.num = [1 1]; + +%-------------------------------------------------------------------------- +% orientation Image orientation +%-------------------------------------------------------------------------- +orientation = cfg_menu; +orientation.tag = 'orientation'; +orientation.name = 'Image orientation'; +orientation.help = {'Image orientation.'}; +orientation.labels = {'Axial','Coronal','Sagittal'}'; +orientation.values = {'axial','coronal','sagittal'}; + +%-------------------------------------------------------------------------- +% Slices Slices +%-------------------------------------------------------------------------- +slices = cfg_entry; +slices.tag = 'slices'; +slices.name = 'Slices'; +slices.help = {'Slices to display (mm).'}; +slices.strtype = 'r'; +slices.num = [1 Inf]; + %-------------------------------------------------------------------------- % nsubj Number of subjects %-------------------------------------------------------------------------- @@ -330,6 +361,13 @@ exports{end}.val = { true }; exports{end}.help = {exports{end}.name}; end + +exports{end+1} = cfg_branch; +exports{end}.tag = 'montage'; +exports{end}.name = 'Montage'; +exports{end}.val = { background orientation slices }; +exports{end}.help = {'Display montage.'}; + exports{end+1} = cfg_branch; exports{end}.tag = 'nidm'; exports{end}.name = 'NIDM (Neuroimaging Data Model)'; diff --git a/config/spm_cfg_tissue_volumes.m b/config/spm_cfg_tissue_volumes.m index 4c768777..81139c6f 100644 --- a/config/spm_cfg_tissue_volumes.m +++ b/config/spm_cfg_tissue_volumes.m @@ -6,7 +6,7 @@ % Copyright (C) 2013-2016 Wellcome Trust Centre for Neuroimaging % Ged Ridgway -% $Id: spm_cfg_tissue_volumes.m 6952 2016-11-25 16:03:13Z guillaume $ +% $Id: spm_cfg_tissue_volumes.m 7453 2018-10-18 15:42:33Z christophe $ mat = cfg_files; @@ -21,12 +21,12 @@ T = cfg_entry; T.tag = 'tmax'; -T.name = 'Maximum tissue class'; +T.name = 'Maximum tissue class index'; T.strtype = 'n'; T.num = [1 1]; T.val = {3}; T.help = { - ['Specify the maximum tissue class, T, where tissues 1:T will be ' ... + ['Specify the maximum tissue class index, T, where tissues [1:T] will be ' ... 'measured.'] ['The default of 3 corresponds to GM, WM and CSF for the ' ... 'default tissue prior probability maps ''TPM.nii,1'' to ''TPM.nii,3'''] diff --git a/config/spm_make_standalone.m b/config/spm_make_standalone.m index 4a28864a..fea3f4a3 100644 --- a/config/spm_make_standalone.m +++ b/config/spm_make_standalone.m @@ -27,7 +27,7 @@ function spm_make_standalone(outdir, gateway, contentsver) % Copyright (C) 2010-2017 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_make_standalone.m 7091 2017-06-05 14:46:00Z guillaume $ +% $Id: spm_make_standalone.m 7483 2018-11-12 13:19:31Z guillaume $ %-Check startup.m @@ -97,12 +97,16 @@ function spm_make_standalone(outdir, gateway, contentsver) %========================================================================== %-Compilation %========================================================================== -opts = {'-p',fullfile(matlabroot,'toolbox','signal')}; -if ~exist(opts{2},'dir'), opts = {}; end +Nopts = {'-p',fullfile(matlabroot,'toolbox','signal')}; +if ~exist(Nopts{2},'dir'), Nopts = {}; end +Ropts = {'-R','-singleCompThread'} ; +if spm_check_version('matlab','8.4') >= 0 + Ropts = [Ropts, {'-R','-softwareopengl'}]; +end mcc('-m', '-C', '-v',... '-o',lower(spm('Ver')),... '-d',outdir,... - '-N',opts{:},... - '-R','-singleCompThread',... + '-N',Nopts{:},... + Ropts{:},... '-a',spm('Dir'),... gateway); diff --git a/config/spm_run_fmri_est.m b/config/spm_run_fmri_est.m index 5258ec38..22cc63d7 100644 --- a/config/spm_run_fmri_est.m +++ b/config/spm_run_fmri_est.m @@ -10,13 +10,15 @@ %__________________________________________________________________________ % Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging -% $Id: spm_run_fmri_est.m 7057 2017-04-13 16:45:49Z guillaume $ +% $Id: spm_run_fmri_est.m 7354 2018-06-22 10:44:22Z guillaume $ %-Load SPM.mat file %-------------------------------------------------------------------------- -SPM = []; -load(job.spmmat{:}); +load(job.spmmat{1},'SPM'); +if ~exist('SPM','var') + error('The MAT-file does not contain an SPM variable.'); +end out.spmmat = job.spmmat; %-Move to the directory where the SPM.mat file is diff --git a/config/spm_run_norm.m b/config/spm_run_norm.m index 634ed3be..e5774ca5 100644 --- a/config/spm_run_norm.m +++ b/config/spm_run_norm.m @@ -7,9 +7,9 @@ % Output: % out - computation results, usually a struct variable. %__________________________________________________________________________ -% Copyright (C) 2005-2013 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2005-2018 Wellcome Trust Centre for Neuroimaging -% $Id: spm_run_norm.m 6578 2015-10-15 15:22:12Z volkmar $ +% $Id: spm_run_norm.m 7406 2018-08-21 17:29:53Z john $ for i=1:numel(job.subj) @@ -38,7 +38,7 @@ end if isfield(job,'woptions'), - out(i).files = spm_file(job.subj(i).resample, 'prefix','w'); + out(i).files = spm_file(job.subj(i).resample, 'prefix',job.woptions.prefix); end end %========================================================================== @@ -97,6 +97,8 @@ function write_norm(job) Nii = nifti(defs.comp{1}.def); vx = sqrt(sum(Nii.mat(1:3,1:3).^2)); + if det(Nii.mat(1:3,1:3))<0, vx(1) = -vx(1); end + o = Nii.mat\[0 0 0 1]'; o = o(1:3)'; dm = size(Nii.dat); diff --git a/config/spm_run_results.m b/config/spm_run_results.m index 2a550baf..19f90ad7 100644 --- a/config/spm_run_results.m +++ b/config/spm_run_results.m @@ -7,10 +7,10 @@ % Output: % out - computation results, usually a struct variable. %__________________________________________________________________________ -% Copyright (C) 2008-2016 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2008-2018 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_run_results.m 6896 2016-10-03 16:53:31Z guillaume $ +% $Id: spm_run_results.m 7268 2018-02-27 16:48:13Z guillaume $ cspec = job.conspec; @@ -145,6 +145,19 @@ else cmd = 'winopen(''%s'')'; end fprintf('Saving results to:\n %s\n',spm_file(ofile,'link',cmd)); + case 'montage' + % see myslover() in spm_results_ui.m + so = slover; + so.img.vol = spm_vol(char(job.export{i}.montage.background)); + so.img.prop = 1; + so = add_spm(so, xSPM); + so.transform = job.export{i}.montage.orientation; + so = fill_defaults(so); + so.slices = job.export{i}.montage.slices; + so.figure = spm_figure('GetWin', 'SliceOverlay'); + so = paint(so); + %spm_print('',so.figure); + case 'nidm' opts = struct('mod',job.export{i}.nidm.modality, ... 'space',job.export{i}.nidm.refspace,... diff --git a/config/spm_run_tissue_volumes.m b/config/spm_run_tissue_volumes.m index 680da45e..5aff489e 100644 --- a/config/spm_run_tissue_volumes.m +++ b/config/spm_run_tissue_volumes.m @@ -3,10 +3,11 @@ % % See also: spm_cfg_tissue_volumes, spm_summarise %__________________________________________________________________________ -% Copyright (C) 2013 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2013-2018 Wellcome Trust Centre for Neuroimaging % Ged Ridgway -% $Id: spm_run_tissue_volumes.m 5800 2013-12-10 18:33:15Z guillaume $ +% $Id: spm_run_tissue_volumes.m 7460 2018-10-29 15:55:12Z john $ + switch lower(cmd) %---------------------------------------------------------------------- @@ -43,13 +44,9 @@ tc = false(Kb, 4); % look for existing mwc files - fnm = res.image.fname; - if ~exist(fnm, 'file') - error('Original image no longer found at:\n%s\n', fnm) - end mwc = cell(T, 1); for t = 1:T - mwc{t} = spm_file(fnm, 'prefix', ['mwc' num2str(t)],'ext','nii'); + mwc{t} = spm_file(res.image(1).fname, 'prefix', ['mwc' num2str(t)],'ext','nii'); if ~exist(mwc{t}, 'file') tc(t, 4) = true; % i.e. need to produce this mwc end @@ -57,6 +54,11 @@ % produce mwc files if required if any(tc(:, 4)) + for f = 1:numel(res.image) + if ~exist(res.image(f).fname, 'file') + error('Original image no longer found at:\n%s\n', res.image(f).fname) + end + end spm_preproc_write8(res, tc); end diff --git a/external/bemcp/bem_Cii_cog.mexw32 b/external/bemcp/bem_Cii_cog.mexw32 index 8fad70b0..a2fe7eca 100755 Binary files a/external/bemcp/bem_Cii_cog.mexw32 and b/external/bemcp/bem_Cii_cog.mexw32 differ diff --git a/external/bemcp/bem_Cii_cog.mexw64 b/external/bemcp/bem_Cii_cog.mexw64 index 104d6771..010ddc29 100755 Binary files a/external/bemcp/bem_Cii_cog.mexw64 and b/external/bemcp/bem_Cii_cog.mexw64 differ diff --git a/external/bemcp/bem_Cii_cst.mexw32 b/external/bemcp/bem_Cii_cst.mexw32 index 533c6cb2..e5cf1b15 100755 Binary files a/external/bemcp/bem_Cii_cst.mexw32 and b/external/bemcp/bem_Cii_cst.mexw32 differ diff --git a/external/bemcp/bem_Cii_cst.mexw64 b/external/bemcp/bem_Cii_cst.mexw64 index 0040f1bc..8a506bcd 100755 Binary files a/external/bemcp/bem_Cii_cst.mexw64 and b/external/bemcp/bem_Cii_cst.mexw64 differ diff --git a/external/bemcp/bem_Cii_lin.mexw32 b/external/bemcp/bem_Cii_lin.mexw32 index 17bdfee0..284a7a77 100755 Binary files a/external/bemcp/bem_Cii_lin.mexw32 and b/external/bemcp/bem_Cii_lin.mexw32 differ diff --git a/external/bemcp/bem_Cii_lin.mexw64 b/external/bemcp/bem_Cii_lin.mexw64 index 7afee250..bfc84e23 100755 Binary files a/external/bemcp/bem_Cii_lin.mexw64 and b/external/bemcp/bem_Cii_lin.mexw64 differ diff --git a/external/bemcp/bem_Cij_cog.mexw32 b/external/bemcp/bem_Cij_cog.mexw32 index f22deeaf..38a452ef 100755 Binary files a/external/bemcp/bem_Cij_cog.mexw32 and b/external/bemcp/bem_Cij_cog.mexw32 differ diff --git a/external/bemcp/bem_Cij_cog.mexw64 b/external/bemcp/bem_Cij_cog.mexw64 index 7e811dfc..fbab9a2e 100755 Binary files a/external/bemcp/bem_Cij_cog.mexw64 and b/external/bemcp/bem_Cij_cog.mexw64 differ diff --git a/external/bemcp/bem_Cij_cst.mexw32 b/external/bemcp/bem_Cij_cst.mexw32 index faed79f8..8aee0ebf 100755 Binary files a/external/bemcp/bem_Cij_cst.mexw32 and b/external/bemcp/bem_Cij_cst.mexw32 differ diff --git a/external/bemcp/bem_Cij_cst.mexw64 b/external/bemcp/bem_Cij_cst.mexw64 index 573d4957..fc128faf 100755 Binary files a/external/bemcp/bem_Cij_cst.mexw64 and b/external/bemcp/bem_Cij_cst.mexw64 differ diff --git a/external/bemcp/bem_Cij_lin.mexw32 b/external/bemcp/bem_Cij_lin.mexw32 index d921bbdf..d23c90af 100755 Binary files a/external/bemcp/bem_Cij_lin.mexw32 and b/external/bemcp/bem_Cij_lin.mexw32 differ diff --git a/external/bemcp/bem_Cij_lin.mexw64 b/external/bemcp/bem_Cij_lin.mexw64 index 40bcb77d..212a5008 100755 Binary files a/external/bemcp/bem_Cij_lin.mexw64 and b/external/bemcp/bem_Cij_lin.mexw64 differ diff --git a/external/bemcp/bem_Gi_cog.mexw32 b/external/bemcp/bem_Gi_cog.mexw32 index 7a782aea..e39e59b3 100755 Binary files a/external/bemcp/bem_Gi_cog.mexw32 and b/external/bemcp/bem_Gi_cog.mexw32 differ diff --git a/external/bemcp/bem_Gi_cog.mexw64 b/external/bemcp/bem_Gi_cog.mexw64 index b8cee4c7..3f317ae1 100755 Binary files a/external/bemcp/bem_Gi_cog.mexw64 and b/external/bemcp/bem_Gi_cog.mexw64 differ diff --git a/external/bemcp/bem_Gi_vert.mexw32 b/external/bemcp/bem_Gi_vert.mexw32 index 3608939a..06b35c7d 100755 Binary files a/external/bemcp/bem_Gi_vert.mexw32 and b/external/bemcp/bem_Gi_vert.mexw32 differ diff --git a/external/bemcp/bem_Gi_vert.mexw64 b/external/bemcp/bem_Gi_vert.mexw64 index 587a21d2..645cdbf0 100755 Binary files a/external/bemcp/bem_Gi_vert.mexw64 and b/external/bemcp/bem_Gi_vert.mexw64 differ diff --git a/external/fieldtrip/compat/incorrect/README b/external/fieldtrip/compat/incorrect/README new file mode 100644 index 00000000..80af7cb0 --- /dev/null +++ b/external/fieldtrip/compat/incorrect/README @@ -0,0 +1,4 @@ +This is a directory that should never be added to your MATLAB path. +It only serves to check whether your path is setup incorrectly. + +See also http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path diff --git a/external/fieldtrip/compat/matlablt2010b/README b/external/fieldtrip/compat/matlablt2010b/README new file mode 100644 index 00000000..eb715fb3 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2010b/README @@ -0,0 +1,12 @@ +This directory contains some functions that allow FieldTrip to run +on older MATLAB versions. These functions emulate the functionality +that is present in the latest MATLAB version. + +Each of these directories is called matlabltXXXXX, where "lt" stands +for "less than" and XXXXX is the MATLAB release. If you are using +MATLAB release N, then you should only add the directories N+1, +N+2, ... to your path. In general this will be done automatically +by the ft_defaults function. + +See also http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path + diff --git a/external/fieldtrip/compat/matlablt2010b/iscolumn.m b/external/fieldtrip/compat/matlablt2010b/iscolumn.m new file mode 100644 index 00000000..7c5025b8 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2010b/iscolumn.m @@ -0,0 +1,10 @@ +function tf = iscolumn(x) + +% This is a compatibility directory that should only be added to the path on +% MATLAB versions prior to 2010b. +% +% iscolumn is not present in older versions. + +tf = length(size(x))==2 && size(x,2)==1; + +end % function diff --git a/external/fieldtrip/compat/matlablt2010b/ismatrix.m b/external/fieldtrip/compat/matlablt2010b/ismatrix.m new file mode 100644 index 00000000..c1ba3876 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2010b/ismatrix.m @@ -0,0 +1,13 @@ +function status = ismatrix(x) + +% This is a compatibility directory that should only be added to the path on +% MATLAB versions prior to 2010b. +% +% ismatrix is not present in older versions. + +siz = size(x); +status = numel(siz)==2 && siz(1)>=0 && siz(2)>=0; + +end % function + + diff --git a/external/fieldtrip/compat/matlablt2010b/isrow.m b/external/fieldtrip/compat/matlablt2010b/isrow.m new file mode 100644 index 00000000..838a8117 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2010b/isrow.m @@ -0,0 +1,10 @@ +function tf = isrow(x) + +% This is a compatibility directory that should only be added to the path on +% MATLAB versions prior to 2010b. +% +% isrow is not present in older version. + +tf = length(size(x))==2 && size(x,1)==1; + +end % function diff --git a/external/fieldtrip/compat/matlablt2011b/README b/external/fieldtrip/compat/matlablt2011b/README new file mode 100644 index 00000000..a371a10f --- /dev/null +++ b/external/fieldtrip/compat/matlablt2011b/README @@ -0,0 +1,11 @@ +This directory contains some functions that allow FieldTrip to run +on older MATLAB versions. These functions emulate the functionality +that is present in the latest MATLAB version. + +Each of these directories is called matlabltXXXXX, where "lt" stands +for "less than" and XXXXX is the MATLAB release. If you are using +MATLAB release N, then you should only add the directories N+1, +N+2, ... to your path. In general this will be done automatically +by the ft_defaults function. + +See also http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path diff --git a/external/fieldtrip/compat/matlablt2011b/narginchk.m b/external/fieldtrip/compat/matlablt2011b/narginchk.m new file mode 100644 index 00000000..671efe14 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2011b/narginchk.m @@ -0,0 +1,11 @@ +function narginchk(min,max) + +% This is a compatibility directory that should only be added to the path on +% MATLAB versions prior to 2011b. +% +% narginchk is not present in older versions. + +n = evalin('caller', 'nargin'); +assert(n>=min, 'not enough input arguments') +assert(n<=max, 'too many input arguments') + diff --git a/external/fieldtrip/compat/matlablt2012a/README b/external/fieldtrip/compat/matlablt2012a/README new file mode 100644 index 00000000..a371a10f --- /dev/null +++ b/external/fieldtrip/compat/matlablt2012a/README @@ -0,0 +1,11 @@ +This directory contains some functions that allow FieldTrip to run +on older MATLAB versions. These functions emulate the functionality +that is present in the latest MATLAB version. + +Each of these directories is called matlabltXXXXX, where "lt" stands +for "less than" and XXXXX is the MATLAB release. If you are using +MATLAB release N, then you should only add the directories N+1, +N+2, ... to your path. In general this will be done automatically +by the ft_defaults function. + +See also http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path diff --git a/external/fieldtrip/compat/matlablt2012a/isequaln.m b/external/fieldtrip/compat/matlablt2012a/isequaln.m new file mode 100644 index 00000000..fe84a031 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2012a/isequaln.m @@ -0,0 +1,11 @@ +function tf = isequaln(varargin) + +% This is a compatibility directory that should only be added to the path on +% MATLAB versions prior to 2012a. +% +% isequalwithequalnans is deprecated in newer MATLAB versions, and +% will be removed, so we strive to only use isequaln in the code. + +tf = isequalwithequalnans(varargin{:}); + +end % function diff --git a/external/fieldtrip/compat/matlablt2013b/README b/external/fieldtrip/compat/matlablt2013b/README new file mode 100644 index 00000000..a371a10f --- /dev/null +++ b/external/fieldtrip/compat/matlablt2013b/README @@ -0,0 +1,11 @@ +This directory contains some functions that allow FieldTrip to run +on older MATLAB versions. These functions emulate the functionality +that is present in the latest MATLAB version. + +Each of these directories is called matlabltXXXXX, where "lt" stands +for "less than" and XXXXX is the MATLAB release. If you are using +MATLAB release N, then you should only add the directories N+1, +N+2, ... to your path. In general this will be done automatically +by the ft_defaults function. + +See also http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path diff --git a/external/fieldtrip/compat/matlablt2013b/istable.m b/external/fieldtrip/compat/matlablt2013b/istable.m new file mode 100644 index 00000000..47245118 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2013b/istable.m @@ -0,0 +1,7 @@ +function s = istable(varargin) + +% ISTABLE is a drop-in replacement for the same function that was +% introduced in MATLAB R2013b. In versions prior to 2013b this function +% returns false. + +s = false; diff --git a/external/fieldtrip/compat/matlablt2016b/README b/external/fieldtrip/compat/matlablt2016b/README new file mode 100644 index 00000000..a371a10f --- /dev/null +++ b/external/fieldtrip/compat/matlablt2016b/README @@ -0,0 +1,11 @@ +This directory contains some functions that allow FieldTrip to run +on older MATLAB versions. These functions emulate the functionality +that is present in the latest MATLAB version. + +Each of these directories is called matlabltXXXXX, where "lt" stands +for "less than" and XXXXX is the MATLAB release. If you are using +MATLAB release N, then you should only add the directories N+1, +N+2, ... to your path. In general this will be done automatically +by the ft_defaults function. + +See also http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path diff --git a/external/fieldtrip/compat/matlablt2016b/contains.m b/external/fieldtrip/compat/matlablt2016b/contains.m new file mode 100644 index 00000000..450a83ad --- /dev/null +++ b/external/fieldtrip/compat/matlablt2016b/contains.m @@ -0,0 +1,37 @@ +function tf = contains(s, pattern, str, boolean) + +%CONTAINS True if text contains a pattern. +% TF = contains(S,PATTERN) returns true if any element of string array S +% contains PATTERN. TF is the same size as S. +% +% This is a compatibility function that should only be added to the path on +% MATLAB versions prior to 2016b. + +if ~ischar(s) && ~iscellstr(s) + error('the input should be either a char-array or a cell-array with chars'); +end + +if nargin<4 + boolean = false; +end +if nargin<3 + str = 'IgnoreCase'; +end +if ~strcmpi(str, 'ignorecase') + error('incorrect third input argument, can only be ''IgnoreCase'''); +end +if ~islogical(boolean) + error('fourth input argument should be a logical scalar'); +end + +if ~iscellstr(s) + s = {s}; +end + +if boolean + s = lower(s); + pattern = lower(pattern); +end + +tf = ~cellfun(@isempty, strfind(s, pattern)); + diff --git a/external/fieldtrip/compat/matlablt2016b/endsWith.m b/external/fieldtrip/compat/matlablt2016b/endsWith.m new file mode 100644 index 00000000..cfae0b07 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2016b/endsWith.m @@ -0,0 +1,42 @@ +function tf = endsWith(s, pattern, str, boolean) + +%ENDSWITH True if text starts with pattern. +% TF = endsWith(S,PATTERN) returns true if any element of string array S +% starts with PATTERN. TF is the same size as S. +% +% This is a compatibility function that should only be added to the path on +% MATLAB versions prior to 2016b. + +if ~ischar(s) && ~iscellstr(s) + error('the input should be either a char-array or a cell array with chars'); +end + +if nargin<4 + boolean = false; +end +if nargin<3 + str = 'IgnoreCase'; +end +if ~strcmpi(str, 'ignorecase') + error('incorrect third input argument, can only be ''IgnoreCase'''); +end +if ~islogical(boolean) + error('fourth input argument should be a logical scalar'); +end + +% the final comparison is done on the start of the string, hence flip all of them +pattern = fliplr(pattern); +if iscell(s) + s = cellfun(@fliplr, s); +else + s = fliplr(s); +end + +% compare the start of the string +if boolean + tf = strncmpi(s, pattern, numel(pattern)); +else + tf = strncmp(s, pattern, numel(pattern)); +end + + diff --git a/external/fieldtrip/compat/matlablt2016b/newline.m b/external/fieldtrip/compat/matlablt2016b/newline.m new file mode 100644 index 00000000..7aa78112 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2016b/newline.m @@ -0,0 +1,10 @@ +function C = newline + +% NEWLINE Create newline character +% C = newline creates the character representing a newline. +% newline is equivalent to char(10) or sprintf('\n'). +% +% This is a compatibility function that should only be added to the path on +% MATLAB versions prior to 2016b. + +C = char(10); diff --git a/external/fieldtrip/compat/matlablt2016b/pad.m b/external/fieldtrip/compat/matlablt2016b/pad.m new file mode 100644 index 00000000..ded27ede --- /dev/null +++ b/external/fieldtrip/compat/matlablt2016b/pad.m @@ -0,0 +1,51 @@ +function s = pad(s, n, side, c) + +% PAD adds leading or trailing characters, such as spaces, to the left or +% right of an existing string. +% +% This is a compatibility function that should only be added to the path on +% MATLAB versions prior to 2016b. + +if nargin<4 || isempty(c) + c = ' '; +end + +if nargin<3 || isempty(side) + side = 'right'; +end + +if nargin<2 || isempty(n) + if iscell(s) + n = max(cellfun(@length, s(:))); + else + n = length(s); + end +end + +if iscell(s) + % use recursion to deal with cell-arrays + for i=1:numel(s) + s{i} = pad(s{i}, n, side, c); + end + +else + % this is where the actual work happens + assert(size(s,1)<2); + assert(ischar(s)); + assert(numel(c)==1); + assert(ischar(c)); + + if length(s)>=n + return + else + c = repmat(c, 1, n-length(s)); + switch (side) + case 'left' + s = [c s]; + case 'right' + s = [s c]; + otherwise + error('unsupported side') + end % switch + end +end diff --git a/external/fieldtrip/compat/matlablt2016b/startsWith.m b/external/fieldtrip/compat/matlablt2016b/startsWith.m new file mode 100644 index 00000000..590121a7 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2016b/startsWith.m @@ -0,0 +1,32 @@ +function tf = startsWith(s, pattern, str, boolean) + +%STARTSWITH True if text starts with pattern. +% TF = startsWith(S,PATTERN) returns true if any element of string array S +% starts with PATTERN. TF is the same size as S. +% +% This is a compatibility function that should only be added to the path on +% MATLAB versions prior to 2016b. + +if ~ischar(s) && ~iscellstr(s) + error('the input should be either a char-array or a cell array with chars'); +end + +if nargin<4 + boolean = false; +end +if nargin<3 + str = 'IgnoreCase'; +end +if ~strcmpi(str, 'ignorecase') + error('incorrect third input argument, can only be ''IgnoreCase'''); +end +if ~islogical(boolean) + error('fourth input argument should be a logical scalar'); +end + +if boolean + tf = strncmpi(s, pattern, numel(pattern)); +else + tf = strncmp(s, pattern, numel(pattern)); +end + diff --git a/external/fieldtrip/compat/matlablt2017b/README b/external/fieldtrip/compat/matlablt2017b/README new file mode 100644 index 00000000..a371a10f --- /dev/null +++ b/external/fieldtrip/compat/matlablt2017b/README @@ -0,0 +1,11 @@ +This directory contains some functions that allow FieldTrip to run +on older MATLAB versions. These functions emulate the functionality +that is present in the latest MATLAB version. + +Each of these directories is called matlabltXXXXX, where "lt" stands +for "less than" and XXXXX is the MATLAB release. If you are using +MATLAB release N, then you should only add the directories N+1, +N+2, ... to your path. In general this will be done automatically +by the ft_defaults function. + +See also http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path diff --git a/external/fieldtrip/compat/matlablt2017b/isfile.m b/external/fieldtrip/compat/matlablt2017b/isfile.m new file mode 100644 index 00000000..47d95ee0 --- /dev/null +++ b/external/fieldtrip/compat/matlablt2017b/isfile.m @@ -0,0 +1,11 @@ +function tf = isfile(input) + +%ISFILE Determine if the input points to a file +% TF = ISFILE(INPUT) returns true if INPUT points to a file and false otherwise. +% +% This is a compatibility function that should only be added to the path on +% MATLAB versions prior to 2017b. Note that currently this function only allows +% for a single string in the input, as opposed to the isfile function from MATLAB, which allows for multiple inputs + +tf = ismember(exist(input),[2 3 4 5 6]); + diff --git a/external/fieldtrip/compat/matlablt2017b/isfolder.m b/external/fieldtrip/compat/matlablt2017b/isfolder.m new file mode 100644 index 00000000..9ace9d0a --- /dev/null +++ b/external/fieldtrip/compat/matlablt2017b/isfolder.m @@ -0,0 +1,10 @@ +function tf = isfolder(dirpath) + +%ISFOLDER Determine if the input path points to a folder +% TF = ISFOLDER(PATH) returns true if PATH points to a folder and false otherwise. +% +% This is a compatibility function that should only be added to the path on +% MATLAB versions prior to 2017b. + +tf = exist(dirpath,'dir') == 7; + diff --git a/external/fieldtrip/connectivity/README b/external/fieldtrip/connectivity/README index ad9f1321..6895b453 100644 --- a/external/fieldtrip/connectivity/README +++ b/external/fieldtrip/connectivity/README @@ -1,11 +1,21 @@ This is the FieldTrip CONNECTIVITY module. It contains functions for the computation of various connectivity metrics. -For more information please visit http://www.ru.nl/neuroimaging/fieldtrip - -The FieldTrip software is free but copyrighted software, distributed -under the terms of the GNU General Public Licence as published by -the Free Software Foundation (either version 2, or at your option -any later version). See the file COPYING for more details. +For more information please visit http://www.fieldtriptoolbox.org/development/modules +------------------------------------------------------------------------------- Copyright (C) 2009-2011, Donders Institute for Brain, Cognition and Behaviour, The Netherlands (DCCN, DCC, DCN) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +% +% $Id$ + +powflag = ft_getopt(varargin, 'powflag', 0); +realflag = ft_getopt(varargin, 'realflag', 0); +trunc = ft_getopt(varargin, 'powflag', 0); + +ind = find(sum(isfinite(C))>0); + +xorig = x; +yorig = y; +x = intersect(ind, x); +y = intersect(ind, y); + +% use the approach as specified by Borga et al 1992: a unified approach to PCA, PLS, MLR and CCA +% that is: solve the generalized eigenvalue problem eig(inv(B)*A) with A = [O Cxy; Cyx O], and B = [Cxx O; O Cyy]; +indx = zeros(length(ind), 1); +indy = zeros(length(ind), 1); +indx(1:length(x)) = 1; +indy(length(x)+1:end) = 1; + +Aorig = [indx*indy' + indy*indx'].*C([x y],[x y]); +Borig = [indx*indx' + indy*indy'].*C([x y],[x y]); + +if realflag + A = real(Aorig); + B = real(Borig); +else + A = Aorig; + B = Borig; +end +% if A and B are rank deficient this could lead to non-finite eigenvalues +% [w, p] = eig(A,B); +[ua, sa, va] = svds(C(x,x), rank(C(x,x))); +[ub, sb, vb] = svds(C(y,y), rank(C(y,y))); +U = [ua' zeros(size(ua,2),size(ub,1)); zeros(size(ub,2),size(ua,1)) ub']; +[w, p] = eig(U*A*U', U*B*U'); + +[srt, ind] = sort(diag(abs(p)), 'descend'); +w = w(:, ind); +p = p(ind, ind); + +% eigenvalues come in pairs, with 180-degree ambiguity +nump = ceil(size(p,1)/2); +% wx = zeros(length(xorig)); wx(find(ismember(xorig,x)), 1:nump) = w(1:length(x), 1:2:end); +% wy = zeros(length(yorig)); wy(find(ismember(yorig,y)), 1:nump) = w(length(x)+1:end, 1:2:end); +wx = zeros(length(xorig)); +wy = zeros(length(yorig)); +px = abs(p(1:2:end, 1:2:end)); +py = abs(p(1:2:end, 1:2:end)); + +if powflag + powx = wx'*B(x,x)*wx; + powy = wy'*B(y,y)*wy; +end + +if false + Cxx = C(x,x); + Cyy = C(y,y); + Cxy = C(x,y); + Cyx = C(y,x); + + [wx, px] = eig(inv(Cxx)*Cxy*inv(Cyy)*Cyx); + [wy, py] = eig(inv(Cyy)*Cyx*inv(Cxx)*Cxy); + + [srtx,indx] = sort(diag(px), 'descend'); + [srty,indy] = sort(diag(py), 'descend'); + + px = px(indx,indx); + wx = wx(:, indx); + py = py(indy,indy); + wy = wy(:, indy); + + if powflag + powx = wx'*Cxx*wx; + powy = wy'*Cyy*wy; + end +end + +if trunc>0 && trunc<1 + px = px.*double(px>trunc); + py = py.*double(py>trunc); +elseif trunc>=1 + px(trunc+1:end,trunc+1:end) = 0; + py(trunc+1:end,trunc+1:end) = 0; +end diff --git a/external/fieldtrip/connectivity/ft_connectivity_corr.m b/external/fieldtrip/connectivity/ft_connectivity_corr.m index 2889421c..effb6804 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_corr.m +++ b/external/fieldtrip/connectivity/ft_connectivity_corr.m @@ -1,25 +1,39 @@ function [c, v, outcnt] = ft_connectivity_corr(input, varargin) -% FT_CONNECTIVITY_CORR computes correlation, coherence or a related -% quantity from a data-matrix containing a covariance or cross-spectral -% density. +% FT_CONNECTIVITY_CORR computes correlation, coherence or a related quantity from a +% data-matrix containing a covariance or cross-spectral density. It implements the +% methods as described in the following papers: +% +% Coherence: Rosenberg et al, The Fourier approach to the identification of +% functional coupling between neuronal spike trains. Prog Biophys Molec +% Biol 1989; 53; 1-31 +% +% Partial coherence: Rosenberg et al, Identification of patterns of +% neuronal connectivity - partial spectra, partial coherence, and neuronal +% interactions. J. Neurosci. Methods, 1998; 83; 57-72 +% +% Phase locking value: Lachaux et al, Measuring phase sychrony in brain +% signals. Human Brain Mapping, 1999; 8; 194-208. +% +% Imaginary part of coherency: Nolte et al, Identifying true brain +% interaction from EEG data using the imaginary part of coherence. Clinical +% Neurophysiology, 2004; 115; 2292-2307 % % Use as -% [c, v, n] = ft_connectivity_corr(input, varargin) +% [c, v, n] = ft_connectivity_corr(input, ...) % -% The input data input should be an array organized as: +% The input data should be an array organized as % Repetitions x Channel x Channel (x Frequency) (x Time) % or % Repetitions x Channelcombination (x Frequency) (x Time) % -% If the input already contains an average, the first dimension should be -% singleton. Furthermore, the input data can be complex-valued cross -% spectral densities, or real-valued covariance estimates. If the former is -% the case, the output will be coherence (or a derived metric), if the -% latter is the case, the output will be the correlation coefficient. -% -% Additional input arguments come as key-value pairs: +% If the input already contains an average, the first dimension should be singleton. +% Furthermore, the input data can be complex-valued cross spectral densities, or +% real-valued covariance estimates. If the former is the case, the output will be +% coherence (or a derived metric), if the latter is the case, the output will be the +% correlation coefficient. % +% Additional optional input arguments come as key-value pairs: % hasjack = 0 or 1 specifying whether the Repetitions represent % leave-one-out samples % complex = 'abs', 'angle', 'real', 'imag', 'complex', 'logabs' for @@ -49,23 +63,6 @@ % which only can be computed if the data contains leave-one-out samples, % and n is the number of repetitions in the input data. % -% It implements the methods as described in the following papers: -% -% Coherence: Rosenberg et al, The Fourier approach to the identification of -% functional coupling between neuronal spike trains. Prog Biophys Molec -% Biol 1989; 53; 1-31 -% -% Partial coherence: Rosenberg et al, Identification of patterns of -% neuronal connectivity - partial spectra, partial coherence, and neuronal -% interactions. J. Neurosci. Methods, 1998; 83; 57-72 -% -% Phase locking value: Lachaux et al, Measuring phase sychrony in brain -% signals. Human Brain Mapping, 1999; 8; 194-208. -% -% Imaginary part of coherency: Nolte et al, Identifying true brain -% interaction from EEG data using the imaginary part of coherence. Clinical -% Neurophysiology, 2004; 115; 2292-2307 -% % See also FT_CONNECTIVITYANALYSIS % Copyright (C) 2009-2010 Donders Institute, Jan-Mathijs Schoffelen @@ -107,7 +104,7 @@ siz = [size(input) 1]; % do partialisation if necessary -if ~isempty(pchanindx), +if ~isempty(pchanindx) % partial spectra are computed as in Rosenberg JR et al (1998) J.Neuroscience Methods, equation 38 chan = allchanindx; @@ -138,7 +135,7 @@ end % compute the metric -if (length(strfind(dimord, 'chan'))~=2 || ~isempty(strfind(dimord, 'pos'))) && ~isempty(powindx), +if (length(strfind(dimord, 'chan'))~=2 || contains(dimord, 'pos')) && ~isempty(powindx) % crossterms are not described with chan_chan_therest, but are linearly indexed outsum = zeros(siz(2:end)); @@ -161,7 +158,7 @@ end ft_progress('close'); -elseif length(strfind(dimord, 'chan'))==2 || length(strfind(dimord, 'pos'))==2, +elseif length(strfind(dimord, 'chan'))==2 || length(strfind(dimord, 'pos'))==2 % crossterms are described by chan_chan_therest outsum = zeros(siz(2:end)); outssq = zeros(siz(2:end)); @@ -202,7 +199,7 @@ c = outsum./outcnt; % correct the variance estimate for the under-estimation introduced by the jackknifing -if n>1, +if n>1 if hasjack %bias = (n1-1).^2; % added this for nan support marvin bias = (outcnt-1).^2; diff --git a/external/fieldtrip/connectivity/ft_connectivity_csd2transfer.m b/external/fieldtrip/connectivity/ft_connectivity_csd2transfer.m index 82702c76..a5e8940a 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_csd2transfer.m +++ b/external/fieldtrip/connectivity/ft_connectivity_csd2transfer.m @@ -1,19 +1,18 @@ function [output] = ft_connectivity_csd2transfer(freq, varargin) -% FT_CONNECTIVITY_CSD2TRANSFER computes the transfer-function from frequency -% domain data using the Wilson-Burg algorithm. The transfer function can be -% used for the computation of directional measures of connectivity, such as -% granger causality, partial directed coherence, or directed transfer functions +% FT_CONNECTIVITY_CSD2TRANSFER computes the transfer-function from frequency domain +% data using the Wilson-Burg algorithm. The transfer function can be used for the +% computation of directional measures of connectivity, such as Granger causality, +% partial directed coherence, or directed transfer functions. % % Use as -% [output] = ft_connectivity_csd2transfer(freq, varargin) +% [output] = ft_connectivity_csd2transfer(freq, ...) % -% Where freq is a data structure containing frequency domain data containing -% the cross-spectral density computed between all pairs of channels, thus -% containing a 'dimord' of 'chan_chan_freq(_time)'. -% -% Configurable options come in key-value pairs: +% The input variable freq should be a FieldTrip data structure containing frequency +% domain data containing the cross-spectral density computed between all pairs of +% channels, thus containing a 'dimord' of 'chan_chan_freq(_time)'. % +% Additional optional input arguments come as key-value pairs: % numiteration = scalar value (default: 100) the number of iterations % channelcmb = Nx2 cell-array listing the channel pairs for the spectral % factorization. If not defined or empty (default), a @@ -21,28 +20,31 @@ % a multiple pairwise factorization is done. % tol = scalar value (default: 1e-18) tolerance limit truncating % the iterations -% sfmethod = 'multivariate', or 'bivariate' +% sfmethod = 'multivariate', or 'bivariate' % stabilityfix = false, or true. zigzag-reduction by means of tapering of the % intermediate time domain representation when computing the % plusoperator % -% The code for the Wilson-Burg algorithm has been very generously provided by -% Dr. Mukesh Dhamala, and Prof. Mingzhou Ding and his group, and has been -% adjusted for efficiency. -% -% If you use this code for studying directed interactions, please cite from +% The code for the Wilson-Burg algorithm has been very generously provided by Dr. +% Mukesh Dhamala, and Prof. Mingzhou Ding and his group, and has been adjusted for +% efficiency. If you use this code for studying directed interactions, please cite % the following references: -% -M.Dhamala, R.Rangarajan, M.Ding, Physical Review Letters 100, 018701 (2008) -% -M.Dhamala, R.Rangarajan, M.Ding, Neuroimage 41, 354 (2008) +% - M.Dhamala, R.Rangarajan, M.Ding, Physical Review Letters 100, 018701 (2008). +% - M.Dhamala, R.Rangarajan, M.Ding, Neuroimage 41, 354 (2008). +% +% See also FT_CONNECTIVITYANALYSIS -% Undocumented options: +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % +% Undocumented options: % block % blockindx % svd % conditional % init % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Copyright (C) 2009-2017, Jan-Mathijs Schoffelen % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org @@ -63,22 +65,20 @@ % % $Id$ -sfmethod = ft_getopt(varargin, 'sfmethod', 'multivariate'); -numiteration = ft_getopt(varargin, 'numiteration', 100); -fb = ft_getopt(varargin, 'feedback', 'none'); +sfmethod = ft_getopt(varargin, 'sfmethod', 'multivariate'); +numiteration = ft_getopt(varargin, 'numiteration', 100); +fb = ft_getopt(varargin, 'feedback', 'none'); checkconvergence = ft_getopt(varargin, 'checkconvergence', true); -init = ft_getopt(varargin, 'init', 'chol'); - -channelcmb = ft_getopt(varargin, 'channelcmb', {}); -channeltriplet = ft_getopt(varargin, 'channeltriplet', {}); -block = ft_getopt(varargin, 'block', []); -tol = ft_getopt(varargin, 'tol', 1e-18); +init = ft_getopt(varargin, 'init', 'chol'); +channelcmb = ft_getopt(varargin, 'channelcmb', {}); +channeltriplet = ft_getopt(varargin, 'channeltriplet', {}); +block = ft_getopt(varargin, 'block', []); +tol = ft_getopt(varargin, 'tol', 1e-18); -dosvd = istrue(ft_getopt(varargin, 'svd', 'no')); -doconditional = istrue(ft_getopt(varargin, 'conditional', 0)); -stabilityfix = istrue(ft_getopt(varargin, 'stabilityfix', false)); - -doblock = isstruct(block) || doconditional; +dosvd = istrue(ft_getopt(varargin, 'svd', 'no')); +doconditional = istrue(ft_getopt(varargin, 'conditional', 0)); +stabilityfix = istrue(ft_getopt(varargin, 'stabilityfix', false)); +doblock = isstruct(block) || doconditional; if doconditional && isempty(block) % create the default block struct-array @@ -97,7 +97,7 @@ end hasrpt = ~isempty(strfind(freq.dimord, 'rpt')); -if hasrpt, +if hasrpt nrpt = numel(freq.cumtapcnt); else nrpt = 1; @@ -121,13 +121,13 @@ end % create an Ntriplet x 3 index matrix, to be used below - [~,i2] = match_str(channeltriplet(:,1),freq.label); + [dum,i2] = match_str(channeltriplet(:,1),freq.label); cmbindx(:,1) = i2; - [~,i2] = match_str(channeltriplet(:,2),freq.label); + [dum,i2] = match_str(channeltriplet(:,2),freq.label); cmbindx(:,2) = i2; - [~,i2] = match_str(channeltriplet(:,3),freq.label); + [dum,i2] = match_str(channeltriplet(:,3),freq.label); cmbindx(:,3) = i2; - + elseif strcmp(sfmethod, 'bivariate_conditional') % this method requires a non-empty channelcmb {Nx2} with a corresponding % channelcnd{Nx1}. The elements in channelcnd can be cell-arrays @@ -159,7 +159,7 @@ ok = true(size(tmp,1),1); tmpindx = zeros(size(tmp)); for k = 1:size(tmpindx,1) - [~, tmpindx(k,:)] = match_str(tmp(k,:)', utmp); + [dum, tmpindx(k,:)] = match_str(tmp(k,:)', utmp); tmptmpindx = tmpindx(k,:); tmptmpindx = tmptmpindx([1 2 3;1 3 2;2 1 3;2 3 1;3 1 2;3 2 1]); if ~isempty(intersect(tmpindx(1:(k-1),:), tmptmpindx, 'rows')) @@ -169,16 +169,16 @@ end end channeltriplet = channeltriplet(ok,:); -% -% tmp = cell(0,3); -% while ~isempty(channeltriplet) -% tmp = cat(1, tmp, channeltriplet(end,:)); -% channeltriplet(strcmp(channeltriplet(:,1),channeltriplet{end,1})&... -% strcmp(channeltriplet(:,2),channeltriplet{end,2})&... -% strcmp(channeltriplet(:,3),channeltriplet{end,3}),:) = []; -% end -% channeltriplet = tmp; -% + % + % tmp = cell(0,3); + % while ~isempty(channeltriplet) + % tmp = cat(1, tmp, channeltriplet(end,:)); + % channeltriplet(strcmp(channeltriplet(:,1),channeltriplet{end,1})&... + % strcmp(channeltriplet(:,2),channeltriplet{end,2})&... + % strcmp(channeltriplet(:,3),channeltriplet{end,3}),:) = []; + % end + % channeltriplet = tmp; + % end if ~isempty(channelcmb) && numel(freq.label)>1 && strncmp(sfmethod, 'bivariate', 9) @@ -192,7 +192,7 @@ if ~isempty(block) % sanity check 1 - if ~isstruct(block) + if ~isstruct(block) ft_error('block should be a struct-array'); end % sanity check 2 @@ -201,15 +201,15 @@ end end -if isfield(freq, 'time'), +if isfield(freq, 'time') ntim = numel(freq.time); else ntim = 1; end siz = size(freq.crsspctrm); -if ntim==1, - siz = [siz 1]; %add dummy dimensionality for time axis +if ntim==1 + siz = [siz 1]; % add dummy dimensionality for time axis end if strcmp(sfmethod, 'bivariate') @@ -221,7 +221,7 @@ elseif strcmp(sfmethod, 'bivariate_conditional') % no text elseif strcmp(sfmethod, 'multivariate') - fprintf('computing multivariate non-parametric spectral factorization on %d channels\n', numel(freq.label)); + fprintf('computing multivariate non-parametric spectral factorization on %d channels\n', numel(freq.label)); elseif strcmp(sfmethod, 'trivariate') fprintf('computing tripletwise non-parametric spectral factorization\n'); else @@ -232,7 +232,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % the actual computations start here %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -if strcmp(sfmethod, 'multivariate') && nrpt==1 && ~doconditional, +if strcmp(sfmethod, 'multivariate') && nrpt==1 && ~doconditional %%%%%%%%%%%%%%%%%%%%%%%%%%%% % standard code % multivariate decomposition @@ -245,7 +245,7 @@ % only do decomposition once for m = 1:ntim tmp = freq.crsspctrm(:,:,:,m); - + % do SVD to avoid zigzags due to numerical issues if dosvd dat = sum(tmp,3); @@ -255,13 +255,13 @@ end end - if any(isnan(tmp(:))), + if any(isnan(tmp(:))) Htmp = nan; Ztmp = nan; Stmp = nan; else [Htmp, Ztmp, Stmp] = sfactorization_wilson(tmp, freq.freq, ... - numiteration, tol, fb, init, checkconvergence, stabilityfix); + numiteration, tol, fb, init, checkconvergence, stabilityfix); end % undo SVD @@ -278,12 +278,12 @@ S(:,:,:,m) = Stmp; end -elseif strcmp(sfmethod, 'multivariate') && nrpt==1 && doblock, +elseif strcmp(sfmethod, 'multivariate') && nrpt==1 && doblock %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % blockwise multivariate stuff %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - if ntim>1, + if ntim>1 ft_error('blockwise factorization of tfrs is not yet possible'); end @@ -296,7 +296,7 @@ end if doconditional - % multiple factorizations are needed: + % multiple factorizations are needed: % -all blocks together % -all pairs of blocks ( % -all leave-one-out blocks @@ -355,7 +355,7 @@ end [Htmp, Ztmp, Stmp] = sfactorization_wilson(Stmp, freq.freq, ... - numiteration, tol, fb, init, checkconvergence, stabilityfix); + numiteration, tol, fb, init, checkconvergence, stabilityfix); % undo PCA if dopca @@ -365,7 +365,7 @@ end Ztmp = u*Ztmp*u'; end - + siz = [size(Htmp) 1]; siz = [siz(1)*siz(2) siz(3:end)]; Htmp = reshape(Htmp, siz); siz = [size(Ztmp) 1]; siz = [siz(1)*siz(2) siz(3:end)]; @@ -377,12 +377,12 @@ cmbtmp = cell(siz(1), 2); [tmpindx(:,1), tmpindx(:,2)] = ind2sub(sqrt(siz(1))*[1 1],1:siz(1)); for kk = 1:size(cmbtmp,1) - cmbtmp{kk,1} = [freq.label{sel(tmpindx(kk,1))},'[',cat(2,freq.label{sel}),']']; - cmbtmp{kk,2} = [freq.label{sel(tmpindx(kk,2))},'[',cat(2,freq.label{sel}),']']; + cmbtmp{kk,1} = [freq.label{sel(tmpindx(kk,1))},'[',strjoin(freq.label(sel),','),']']; + cmbtmp{kk,2} = [freq.label{sel(tmpindx(kk,2))},'[',strjoin(freq.label(sel),','),']']; end %concatenate - if k == 1, + if k == 1 H = Htmp; Z = Ztmp; S = Stmp; @@ -392,14 +392,14 @@ Z = cat(1,Z,Ztmp); S = cat(1,S,Stmp); labelcmb = cat(1,labelcmb,cmbtmp); - end + end end - if strcmp(freq.dimord(1:9), 'chan_chan'), + if strcmp(freq.dimord(1:9), 'chan_chan') freq.dimord = ['chancmb_',freq.dimord(strfind(freq.dimord,'freq'):end)]; end -elseif strcmp(sfmethod, 'multivariate') && nrpt>1 && ~doblock, +elseif strcmp(sfmethod, 'multivariate') && nrpt>1 && ~doblock %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % multiple repetitions, loop over repetitions % multivariate decomposition @@ -412,15 +412,15 @@ for m = 1:ntim tmp = reshape(freq.crsspctrm(k,:,:,:,m), siz(2:end-1)); [Htmp, Ztmp, Stmp] = sfactorization_wilson(tmp, freq.freq, ... - numiteration, tol, fb, init, checkconvergence, stabilityfix); + numiteration, tol, fb, init, checkconvergence, stabilityfix); H(k,:,:,:,m) = Htmp; Z(k,:,:,m) = Ztmp; S(k,:,:,:,m) = Stmp; - end + end end - + elseif strcmp(sfmethod, 'multivariate') && nrpt>1 && doblock && ~doconditional - % error + % error ft_error('single trial estimates and blockwise factorisation is not yet implemented'); elseif strcmp(sfmethod, 'bivariate') @@ -433,7 +433,7 @@ cmbindx = zeros(size(channelcmb)); ok = true(size(cmbindx,1), 1); for k = 1:size(cmbindx,1) - [~, cmbindx(k,:)] = match_str(channelcmb(k,:)', freq.label); + [dum, cmbindx(k,:)] = match_str(channelcmb(k,:)', freq.label); if ~isempty(intersect(cmbindx(1:(k-1),:), cmbindx(k,:), 'rows')) ok(k) = false; elseif cmbindx(k,1)==cmbindx(k,2) @@ -447,11 +447,11 @@ channelcmb = channelcmb(ok,:); %do multiple 2x2 factorization efficiently - if ntim>1, + if ntim>1 for kk = 1:ntim [Htmp, Ztmp, Stmp] = sfactorization_wilson2x2(freq.crsspctrm(:,:,:,kk), ... - freq.freq, numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); - if kk==1, + freq.freq, numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); + if kk==1 H = Htmp; Z = Ztmp; S = Stmp; @@ -477,24 +477,24 @@ for k = 1:numel(begchunk) fprintf('computing factorization of chunck %d/%d\n', k, numel(begchunk)); [Htmp, Ztmp, Stmp] = sfactorization_wilson2x2(freq.crsspctrm, freq.freq, ... - numiteration, tol, cmbindx(begchunk(k):endchunk(k),:), fb, init, checkconvergence, stabilityfix); - + numiteration, tol, cmbindx(begchunk(k):endchunk(k),:), fb, init, checkconvergence, stabilityfix); + begix = (k-1)*nperchunk*4+1; endix = min(k*nperchunk*4, size(cmbindx,1)*4); H(begix:endix, :) = Htmp; S(begix:endix, :) = Stmp; Z(begix:endix, :) = Ztmp; - + end else [H, Z, S] = sfactorization_wilson2x2(freq.crsspctrm, freq.freq, ... - numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); + numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); end end labelcmb = cell(size(cmbindx,1)*4, 2); for k = 1:size(cmbindx,1) - duplet = strcat(channelcmb{k,:}); + duplet = sprintf('%s,%s',channelcmb{k,1},channelcmb{k,2}); indx = (k-1)*4 + (1:4); labelcmb{indx(1),1} = [channelcmb{k,1},'[',duplet,']']; labelcmb{indx(1),2} = [channelcmb{k,1},'[',duplet,']']; @@ -533,7 +533,7 @@ ok = true(size(tmp,1),1); tmpindx = zeros(size(tmp)); for k = 1:size(tmpindx,1) - [~, tmpindx(k,:)] = match_str(tmp(k,:)', utmp); + [dum, tmpindx(k,:)] = match_str(tmp(k,:)', utmp); if ~isempty(intersect(tmpindx(1:(k-1),:), [tmpindx(k,:);tmpindx(k,[2 1])], 'rows')) ok(k) = false; elseif tmpindx(k,1)==tmpindx(k,2) @@ -558,11 +558,11 @@ %FIXME remove auto-combinations and double occurrences %do multiple 3x3 factorization efficiently - if ntim>1, + if ntim>1 for kk = 1:ntim [Htmp, Ztmp, Stmp] = sfactorization_wilson3x3(freq.crsspctrm(:,:,:,kk), ... - freq.freq, numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); - if kk==1, + freq.freq, numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); + if kk==1 H = Htmp; Z = Ztmp; S = Stmp; @@ -588,24 +588,24 @@ for k = 1:numel(begchunk) fprintf('computing factorization of chunck %d/%d\n', k, numel(begchunk)); [Htmp, Ztmp, Stmp] = sfactorization_wilson3x3(freq.crsspctrm, freq.freq, ... - numiteration, tol, cmbindx(begchunk(k):endchunk(k),:), fb, init, checkconvergence, stabilityfix); - + numiteration, tol, cmbindx(begchunk(k):endchunk(k),:), fb, init, checkconvergence, stabilityfix); + begix = (k-1)*nperchunk*9+1; endix = min(k*nperchunk*9, size(cmbindx,1)*9); H(begix:endix, :) = Htmp; S(begix:endix, :) = Stmp; Z(begix:endix, :) = Ztmp; - + end else [H, Z, S] = sfactorization_wilson3x3(freq.crsspctrm, freq.freq, ... - numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); + numiteration, tol, cmbindx, fb, init, checkconvergence, stabilityfix); end end labelcmb = cell(size(cmbindx,1)*9, 2); for k = 1:size(cmbindx,1) - triplet = strcat(channeltriplet{k,:}); + triplet = sprintf('%s,%s,%s',channeltriplet{k,1},channeltriplet{k,2},channeltriplet{k,3}); indx = (k-1)*9 + (1:9); labelcmb{indx(1),1} = [channeltriplet{k,1},'[',triplet,']']; @@ -627,10 +627,10 @@ labelcmb{indx(9),1} = [channeltriplet{k,3},'[',triplet,']']; labelcmb{indx(9),2} = [channeltriplet{k,3},'[',triplet,']']; end -elseif strcmp(sfmethod, 'bivariate') && nrpt>1, - % error +elseif strcmp(sfmethod, 'bivariate') && nrpt>1 + % error ft_error('single trial estimates and linear combination indexing is not implemented'); - + end %%%%%%%%%%%%%%%%%%%%%%%%% @@ -643,7 +643,7 @@ if strcmp(sfmethod, 'multivariate') output.dimord = freq.dimord; else - if strncmp(freq.dimord, 'chan_chan', 9), + if strncmp(freq.dimord, 'chan_chan', 9) freq.dimord = ['chancmb_',freq.dimord(strfind(freq.dimord,'freq'):end)]; end output.dimord = freq.dimord; diff --git a/external/fieldtrip/connectivity/ft_connectivity_dtf.m b/external/fieldtrip/connectivity/ft_connectivity_dtf.m index 05195ea8..c364d14b 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_dtf.m +++ b/external/fieldtrip/connectivity/ft_connectivity_dtf.m @@ -1,20 +1,20 @@ function [dtf, dtfvar, n] = ft_connectivity_dtf(input, varargin) -% FT_CONNECTIVITY_DTF computes directed transfer function. -% +% FT_CONNECTIVITY_DTF computes the directed transfer function. +% % Use as -% [d, v, n] = ft_connectivity_dtf(h, key1, value1, ...) +% [d, v, n] = ft_connectivity_dtf(h, ...) % -% Input arguments: -% h = spectral transfer matrix, Nrpt x Nchan x Nchan x Nfreq (x Ntime), -% Nrpt can be 1. +% The input data h should be a spectral transfer matrix organized as +% Nrpt x Nchan x Nchan x Nfreq (x Ntime), +% where Nrpt can be 1. % -% additional options need to be specified as key-value pairs and are: +% Additional optional input arguments come as key-value pairs: % 'hasjack' = 0 (default) is a boolean specifying whether the input % contains leave-one-outs, required for correct variance % estimate. % 'feedback' = string, determining verbosity (default = 'none'), see FT_PROGRESS -% 'crsspctrm' = matrix containing the cross-spectral density. If this +% 'crsspctrm' = matrix containing the cross-spectral density. If this % matrix is defined, the function % returns the ddtf, which requires an estimation of partial % coherence from this matrix. @@ -29,10 +29,12 @@ % v = variance of d across observations. % n = number of observations. % -% Typically, nrpt should be 1 (where the spectral transfer matrix is -% computed across observations. When nrpt>1 and hasjack is true the input -% is assumed to contain the leave-one-out estimates of H, thus a more -% reliable estimate of the relevant quantities. +% Typically, nrpt should be 1 (where the spectral transfer matrix is computed across +% observations. When nrpt>1 and hasjack is true the input is assumed to contain the +% leave-one-out estimates of H, thus a more reliable estimate of the relevant +% quantities. +% +% See also FT_CONNECTIVITYANALYSIS % Copyright (C) 2009-2017, Jan-Mathijs Schoffelen % @@ -121,8 +123,8 @@ end dtf = outsum./n; -if n>1, %FIXME this is strictly only true for jackknife, otherwise other bias is needed - if hasjack, +if n>1 % FIXME this is strictly only true for jackknife, otherwise other bias is needed + if hasjack bias = (n - 1).^2; else bias = 1; diff --git a/external/fieldtrip/connectivity/ft_connectivity_granger.m b/external/fieldtrip/connectivity/ft_connectivity_granger.m index 4441fb53..74b7d0b5 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_granger.m +++ b/external/fieldtrip/connectivity/ft_connectivity_granger.m @@ -1,18 +1,20 @@ function [granger, v, n] = ft_connectivity_granger(H, Z, S, varargin) -% FT_CONNECTIVITY_GRANGER computes spectrally resolved granger causality. -% +% FT_CONNECTIVITY_GRANGER computes spectrally resolved granger causality. This +% implementation is loosely based on the code used in Brovelli, et. al., PNAS 101, +% 9849-9854 (2004). +% % Use as -% [GRANGER, V, N] = FT_CONNECTIVITY_GRANGER(H, Z, S, key1, value1, ...) +% [GRANGER, V, N] = FT_CONNECTIVITY_GRANGER(H, Z, S, ...) % -% where -% H is the spectral transfer matrix, Nrpt x Nchan x Nchan x Nfreq (x Ntime), -% or Nrpt x Nchancmb x Nfreq (x Ntime). Nrpt can be 1. -% Z is the covariance matrix of the noise, Nrpt x Nchan x Nchan (x Ntime), -% or Nrpt x Nchancmb (x Ntime). -% S is the cross-spectral density matrix, same dimensionality as H +% The input data should be +% H = spectral transfer matrix, Nrpt x Nchan x Nchan x Nfreq (x Ntime), +% or Nrpt x Nchancmb x Nfreq (x Ntime). Nrpt can be 1. +% Z = the covariance matrix of the noise, Nrpt x Nchan x Nchan (x Ntime), +% or Nrpt x Nchancmb (x Ntime). +% S = the cross-spectral density matrix with the same dimensionality as H. % -% additional options need to be specified as key-value pairs and are: +% Additional optional input arguments come as key-value pairs: % 'dimord' = required string specifying how to interpret the input data % supported values are 'rpt_chan_chan_freq(_time) and % 'rpt_chan_freq(_time), 'rpt_pos_pos_XXX' and 'rpt_pos_XXX' @@ -22,11 +24,11 @@ % estimate % 'powindx' = is a variable determining the exact computation, see below % -% If the inputdata is such that the channel-pairs are linearly indexed, -% granger causality is computed per quadruplet of consecutive entries, -% where the convention is as follows: +% If the inputdata is such that the channel-pairs are linearly indexed, granger +% causality is computed per quadruplet of consecutive entries, where the convention +% is as follows: % -% H(:, (k-1)*4 + 1, :, :, :) -> 'chan1-chan1' +% H(:, (k-1)*4 + 1, :, :, :) -> 'chan1-chan1' % H(:, (k-1)*4 + 2, :, :, :) -> 'chan1->chan2' % H(:, (k-1)*4 + 3, :, :, :) -> 'chan2->chan1' % H(:, (k-1)*4 + 4, :, :, :) -> 'chan2->chan2' @@ -34,16 +36,18 @@ % The same holds for the Z and S matrices. % % Pairwise block-granger causality can be computed when the inputdata has -% dimensionality Nchan x Nchan. In that case powindx should be specified, -% as a 1x2 cell-array indexing the individual channels that go into each -% 'block'. +% dimensionality Nchan x Nchan. In that case powindx should be specified, as a 1x2 +% cell-array indexing the individual channels that go into each 'block'. +% +% See also FT_CONNECTIVITYANALYSIS +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % Undocumented option: powindx can be a struct. In that case, blockwise % conditional granger can be computed. % -% The code is loosely based on the code used in: -% Brovelli, et. al., PNAS 101, 9849-9854 (2004). -% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Copyright (C) 2009-2017, Jan-Mathijs Schoffelen % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org @@ -71,7 +75,7 @@ %FIXME speed up code and check siz = size(H); -if numel(siz)==4, +if numel(siz)==4 siz(5) = 1; end n = siz(1); @@ -82,385 +86,389 @@ % crossterms are described by chan_chan_therest issquare = length(strfind(dimord, 'chan'))==2 || length(strfind(dimord, 'pos'))==2; - -switch method -case 'granger' - if issquare && isempty(powindx) - %%%%%%%%%%%%%%%%%%%%%%%%%%%% - % data are chan_chan_therest - %%%%%%%%%%%%%%%%%%%%%%%%%%%% +switch method + case 'granger' - for kk = 1:n - for ii = 1:Nc - for jj = 1:Nc - if ii ~=jj, - zc = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]); - zc = repmat(zc,[1 1 1 siz(4) 1]); - numer = reshape(abs(S(kk,ii,ii,:,:)),[1 1 siz(4:end)]); - denom = reshape(abs(S(kk,ii,ii,:,:)-zc.*abs(H(kk,ii,jj,:,:)).^2),[1 1 siz(4:end)]); - outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + log(numer./denom); - outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + (log(numer./denom)).^2; + if issquare && isempty(powindx) + %%%%%%%%%%%%%%%%%%%%%%%%%%%% + % data are chan_chan_therest + %%%%%%%%%%%%%%%%%%%%%%%%%%%% + + for kk = 1:n + for ii = 1:Nc + for jj = 1:Nc + if ii ~=jj + zc = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]); + zc = repmat(zc,[1 1 1 siz(4) 1]); + numer = reshape(abs(S(kk,ii,ii,:,:)),[1 1 siz(4:end)]); + denom = reshape(abs(S(kk,ii,ii,:,:)-zc.*abs(H(kk,ii,jj,:,:)).^2),[1 1 siz(4:end)]); + outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + log(numer./denom); + outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + (log(numer./denom)).^2; + end end + outsum(ii,ii,:,:) = 0;%self-granger set to zero end - outsum(ii,ii,:,:) = 0;%self-granger set to zero end - end - - elseif ~issquare && isempty(powindx) - %%%%%%%%%%%%%%%%%%%%%%%%%% - %data are linearly indexed - %%%%%%%%%%%%%%%%%%%%%%%%%% - - for j = 1:n - for k = 1:Nc - %FIXME powindx is not used here anymore - %iauto1 = sum(powindx==powindx(k,1),2)==2; - %iauto2 = sum(powindx==powindx(k,2),2)==2; - %icross1 = k; - %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2; - - % The following is based on hard-coded assumptions: which is fair - % to do if the order of the labelcmb is according to the output of - % ft_connectivity_csd2transfer - if mod(k-1, 4)==0 - continue; % auto granger set to 0 - %iauto1=k;iauto2=k;icross1=k;icross2=k; - elseif mod(k-1, 4)==1 - iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1; - elseif mod(k-1, 4)==2 - iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1; - elseif mod(k-1, 4)==3 - continue; % auto granger set to 0 - %iauto1=k;iauto2=k;icross1=k;icross2=k; + + elseif ~issquare && isempty(powindx) + %%%%%%%%%%%%%%%%%%%%%%%%%% + %data are linearly indexed + %%%%%%%%%%%%%%%%%%%%%%%%%% + + for j = 1:n + for k = 1:Nc + %FIXME powindx is not used here anymore + %iauto1 = sum(powindx==powindx(k,1),2)==2; + %iauto2 = sum(powindx==powindx(k,2),2)==2; + %icross1 = k; + %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2; + + % The following is based on hard-coded assumptions: which is fair + % to do if the order of the labelcmb is according to the output of + % ft_connectivity_csd2transfer + if mod(k-1, 4)==0 + continue; % auto granger set to 0 + %iauto1=k;iauto2=k;icross1=k;icross2=k; + elseif mod(k-1, 4)==1 + iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1; + elseif mod(k-1, 4)==2 + iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1; + elseif mod(k-1, 4)==3 + continue; % auto granger set to 0 + %iauto1=k;iauto2=k;icross1=k;icross2=k; + end + + zc = Z(j,iauto2,:,:) - Z(j,icross1,:,:).^2./Z(j,iauto1,:,:); + numer = abs(S(j,iauto1,:,:)); + denom = abs(S(j,iauto1,:,:)-zc(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2); + outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]); + outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]); end - - zc = Z(j,iauto2,:,:) - Z(j,icross1,:,:).^2./Z(j,iauto1,:,:); - numer = abs(S(j,iauto1,:,:)); - denom = abs(S(j,iauto1,:,:)-zc(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2); - outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]); - outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]); end - end - - elseif issquare && iscell(powindx) - %%%%%%%%%%%%%%%%%%% - % blockwise granger - %%%%%%%%%%%%%%%%%%% - - % H = transfer function nchan x nchan x nfreq - % Z = noise covariance nchan x nchan - % S = crosspectrum nchan x nchan x nfreq - % powindx{k} is a list of indices for block k - - nblock = numel(powindx); - n = size(H,1); - nfreq = size(H,4); - - outsum = zeros(nblock,nblock,nfreq); - outssq = zeros(nblock,nblock,nfreq); - - for k = 1:nblock - for m = (k+1):nblock - indx = [powindx{k}(:);powindx{m}(:)]; - n1 = numel(powindx{k}); - n2 = numel(powindx{m}); - ntot = n1+n2; - indx1 = 1:n1; - indx2 = (n1+1):ntot; - - for kk = 1:n - tmpZ = reshape(Z(kk,indx,indx), [ntot ntot]); + + elseif issquare && iscell(powindx) + %%%%%%%%%%%%%%%%%%% + % blockwise granger + %%%%%%%%%%%%%%%%%%% + + % H = transfer function nchan x nchan x nfreq + % Z = noise covariance nchan x nchan + % S = crosspectrum nchan x nchan x nfreq + % powindx{k} is a list of indices for block k + + nblock = numel(powindx); + n = size(H,1); + nfreq = size(H,4); + + outsum = zeros(nblock,nblock,nfreq); + outssq = zeros(nblock,nblock,nfreq); + + for k = 1:nblock + for m = (k+1):nblock + indx = [powindx{k}(:);powindx{m}(:)]; + indx = cat(1,indx,setdiff((1:size(Z,2))',indx)); + n1 = numel(powindx{k}); + n2 = numel(powindx{m}); + ntot = numel(indx); + indx1 = 1:n1; + indx1_ = (1:ntot)'; indx1_(indx1) = []; + indx2 = n1+(1:n2); + indx12_ = (1:ntot)'; indx12_([indx1(:);indx2(:)]) = []; - % projection matrix for block2 -> block1 - P1 = [eye(n1) zeros(n1,n2); - -tmpZ(indx2,indx1)/tmpZ(indx1,indx1) eye(n2)]; - - % projection matrix for block1 -> block2 - P2 = [ eye(n1) -tmpZ(indx1,indx2)/tmpZ(indx2,indx2); - zeros(n2,n1) eye(n2)]; - - % invert only once - for jj = 1:nfreq - % post multiply transfer matrix with the inverse of the projection matrix - % this is equivalent to time domain pre multiplication with P - Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]); - Zj = tmpZ;%(:,:); - H1 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P1; - H2 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P2; - num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components - num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components - denom1 = abs(det(H1(indx1,indx1)*Zj(indx1,indx1)*H1(indx1,indx1)')); - denom2 = abs(det(H2(indx2,indx2)*Zj(indx2,indx2)*H2(indx2,indx2)')); - %rH1 = real(H1(indx1,indx1)); - %rH2 = real(H2(indx2,indx2)); - %iH1 = imag(H1(indx1,indx1)); - %iH2 = imag(H2(indx2,indx2)); - %h1 = rH1*Zj(indx1,indx1)*rH1' + iH1*Zj(indx1,indx1)*iH1'; - %h2 = rH2*Zj(indx2,indx2)*rH2' + iH2*Zj(indx2,indx2)*iH2'; - %denom1 = abs(det(h1)); - %denom2 = abs(det(h2)); - - outsum(m,k,jj) = log( num1./denom1 ) + outsum(m,k,jj); - outsum(k,m,jj) = log( num2./denom2 ) + outsum(k,m,jj); - outssq(m,k,jj) = log( num1./denom1 ).^2 + outssq(m,k,jj); - outssq(k,m,jj) = log( num2./denom2 ).^2 + outssq(k,m,jj); + for kk = 1:n + tmpZ = reshape(Z(kk,indx,indx), [ntot ntot]); + + % projection matrix for therest+block2 -> block1 + P1 = [eye(n1) zeros(n1,ntot-n1); + -tmpZ(indx1_,indx1)/tmpZ(indx1,indx1) eye(ntot-n1)]; + + % projection matrix for therest+block1 -> block2 + P2 = [ eye(n1) -tmpZ(indx1,indx2)/tmpZ(indx2,indx2) zeros(n1,ntot-n1-n2); + zeros(n2,n1) eye(n2) zeros(n2, ntot-n1-n2); + zeros(ntot-n1-n2,n1) -tmpZ(indx12_,indx2)/tmpZ(indx2,indx2) eye(ntot-n1-n2)]; + + % invert only once + for jj = 1:nfreq + % post multiply transfer matrix with the inverse of the projection matrix + % this is equivalent to time domain pre multiplication with P + Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]); + Zj = tmpZ;%(:,:); + H1 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P1; + H2 = reshape(H(kk,indx,indx,jj), [ntot ntot])/P2; + num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components + num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components + denom1 = abs(det(H1(indx1,indx1)*Zj(indx1,indx1)*H1(indx1,indx1)')); + denom2 = abs(det(H2(indx2,indx2)*Zj(indx2,indx2)*H2(indx2,indx2)')); + %rH1 = real(H1(indx1,indx1)); + %rH2 = real(H2(indx2,indx2)); + %iH1 = imag(H1(indx1,indx1)); + %iH2 = imag(H2(indx2,indx2)); + %h1 = rH1*Zj(indx1,indx1)*rH1' + iH1*Zj(indx1,indx1)*iH1'; + %h2 = rH2*Zj(indx2,indx2)*rH2' + iH2*Zj(indx2,indx2)*iH2'; + %denom1 = abs(det(h1)); + %denom2 = abs(det(h2)); + + outsum(m,k,jj) = log( num1./denom1 ) + outsum(m,k,jj); + outsum(k,m,jj) = log( num2./denom2 ) + outsum(k,m,jj); + outssq(m,k,jj) = log( num1./denom1 ).^2 + outssq(m,k,jj); + outssq(k,m,jj) = log( num2./denom2 ).^2 + outssq(k,m,jj); + end end + end + end + + elseif ~issquare && isstruct(powindx) && isfield(powindx, 'n') + %%%%%%%%%%%%%%%%%%%%%% + %blockwise conditional + %%%%%%%%%%%%%%%%%%%%%% + + n = size(H,1); + ncmb = size(H,2); + nfreq = size(H,3); + ncnd = size(powindx.cmbindx,1); + + outsum = zeros(ncnd, nfreq); + outssq = zeros(ncnd, nfreq); + for k = 1:n + tmpS = reshape(S, [ncmb nfreq]); + tmpH = reshape(H, [ncmb nfreq]); + tmpZ = reshape(Z, [ncmb 1]); + tmp = blockwise_conditionalgranger(tmpS,tmpH,tmpZ,powindx.cmbindx,powindx.n); + outsum = outsum + tmp; + outssq = outssq + tmp.^2; end - end - - elseif ~issquare && isstruct(powindx) && isfield(powindx, 'n') - %%%%%%%%%%%%%%%%%%%%%% - %blockwise conditional - %%%%%%%%%%%%%%%%%%%%%% - - n = size(H,1); - ncmb = size(H,2); - nfreq = size(H,3); - ncnd = size(powindx.cmbindx,1); - - outsum = zeros(ncnd, nfreq); - outssq = zeros(ncnd, nfreq); - for k = 1:n - tmpS = reshape(S, [ncmb nfreq]); - tmpH = reshape(H, [ncmb nfreq]); - tmpZ = reshape(Z, [ncmb 1]); - tmp = blockwise_conditionalgranger(tmpS,tmpH,tmpZ,powindx.cmbindx,powindx.n); - outsum = outsum + tmp; - outssq = outssq + tmp.^2; - end - - elseif ~issquare && isstruct(powindx) - %%%%%%%%%%%%%%%%%%%%%% - %triplet conditional - %%%%%%%%%%%%%%%%%%%%%% - - % decode from the powindx struct which rows in the data correspond with - % the triplets, and which correspond with the duplets - ublockindx = unique(powindx.blockindx); - nperblock = zeros(size(ublockindx)); - for k = 1:numel(ublockindx) - nperblock(k,1) = sum(powindx.blockindx==ublockindx(k)); - end - if ~all(ismember(nperblock,[4 9])) - error('the data should be a mixture of trivariate and bivariate decompositions'); - end - indx_triplets = ismember(powindx.blockindx, ublockindx(nperblock==9)); ntriplets = sum(indx_triplets)./9; - indx_duplets = ismember(powindx.blockindx, ublockindx(nperblock==4)); nduplets = sum(indx_duplets)./4; - - % this assumes well-behaved powindx.cmbindx - cmbindx2 = reshape(powindx.cmbindx(indx_duplets, 1), 2, [])'; - cmbindx3 = reshape(powindx.cmbindx(indx_triplets,1), 3, [])'; - - cmbindx2 = cmbindx2(1:2:end,:); - cmbindx3 = cmbindx3(1:3:end,:); - - cmbindx = powindx.outindx; - - n = size(H,1); - siz = size(H); - - outsum = zeros(size(cmbindx,1), size(H,3)); - outssq = outsum; + elseif ~issquare && isstruct(powindx) + %%%%%%%%%%%%%%%%%%%%%% + %triplet conditional + %%%%%%%%%%%%%%%%%%%%%% + + % decode from the powindx struct which rows in the data correspond with + % the triplets, and which correspond with the duplets + ublockindx = unique(powindx.blockindx); + nperblock = zeros(size(ublockindx)); + for k = 1:numel(ublockindx) + nperblock(k,1) = sum(powindx.blockindx==ublockindx(k)); + end + if ~all(ismember(nperblock,[4 9])) + error('the data should be a mixture of trivariate and bivariate decompositions'); + end + indx_triplets = ismember(powindx.blockindx, ublockindx(nperblock==9)); ntriplets = sum(indx_triplets)./9; + indx_duplets = ismember(powindx.blockindx, ublockindx(nperblock==4)); nduplets = sum(indx_duplets)./4; + + % this assumes well-behaved powindx.cmbindx + cmbindx2 = reshape(powindx.cmbindx(indx_duplets, 1), 2, [])'; + cmbindx3 = reshape(powindx.cmbindx(indx_triplets,1), 3, [])'; + + cmbindx2 = cmbindx2(1:2:end,:); + cmbindx3 = cmbindx3(1:3:end,:); + + cmbindx = powindx.outindx; + + n = size(H,1); + siz = size(H); + + outsum = zeros(size(cmbindx,1), size(H,3)); + outssq = outsum; + + % call the low-level function + for k = 1:n + H3 = reshape(H(k,indx_triplets,:,:), [3 3 ntriplets siz(3:end)]); + Z3 = reshape(Z(k,indx_triplets), [3 3 ntriplets]); + + H2 = reshape(H(k,indx_duplets,:,:), [2 2 nduplets siz(3:end)]); + Z2 = reshape(Z(k,indx_duplets), [2 2 nduplets]); - % call the low-level function - for k = 1:n - H3 = reshape(H(k,indx_triplets,:,:), [3 3 ntriplets siz(3:end)]); - Z3 = reshape(Z(k,indx_triplets), [3 3 ntriplets]); + tmp = triplet_conditionalgranger(H3,Z3,cmbindx3,H2,Z2,cmbindx2,cmbindx); + + outsum = outsum + tmp; + outssq = outssq + tmp.^2; + end + - H2 = reshape(H(k,indx_duplets,:,:), [2 2 nduplets siz(3:end)]); - Z2 = reshape(Z(k,indx_duplets), [2 2 nduplets]); - tmp = triplet_conditionalgranger(H3,Z3,cmbindx3,H2,Z2,cmbindx2,cmbindx); - - outsum = outsum + tmp; - outssq = outssq + tmp.^2; end + case 'instantaneous' - - end - -case 'instantaneous' - - if issquare && isempty(powindx), - % data are chan_chan_therest - for kk = 1:n - for ii = 1:Nc - for jj = 1:Nc - if ii ~=jj, - zc1 = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]); - zc2 = reshape(Z(kk,ii,ii,:) - Z(kk,jj,ii,:).^2./Z(kk,jj,jj,:),[1 1 1 1 siz(5)]); - zc1 = repmat(zc1,[1 1 1 siz(4) 1]); - zc2 = repmat(zc2,[1 1 1 siz(4) 1]); - term1 = abs(S(kk,ii,ii,:,:)) - zc1.*abs(H(kk,ii,jj,:,:)).^2; - term2 = abs(S(kk,jj,jj,:,:)) - zc2.*abs(H(kk,jj,ii,:,:)).^2; - numer = term1.*term2; - denom = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:)); - outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom), [1 1 siz(4:end)]); - outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]); + if issquare && isempty(powindx) + % data are chan_chan_therest + for kk = 1:n + for ii = 1:Nc + for jj = 1:Nc + if ii ~=jj + zc1 = reshape(Z(kk,jj,jj,:) - Z(kk,ii,jj,:).^2./Z(kk,ii,ii,:),[1 1 1 1 siz(5)]); + zc2 = reshape(Z(kk,ii,ii,:) - Z(kk,jj,ii,:).^2./Z(kk,jj,jj,:),[1 1 1 1 siz(5)]); + zc1 = repmat(zc1,[1 1 1 siz(4) 1]); + zc2 = repmat(zc2,[1 1 1 siz(4) 1]); + term1 = abs(S(kk,ii,ii,:,:)) - zc1.*abs(H(kk,ii,jj,:,:)).^2; + term2 = abs(S(kk,jj,jj,:,:)) - zc2.*abs(H(kk,jj,ii,:,:)).^2; + numer = term1.*term2; + denom = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:)); + outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom), [1 1 siz(4:end)]); + outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]); + end end + outsum(ii,ii,:,:) = 0;%self-granger set to zero end - outsum(ii,ii,:,:) = 0;%self-granger set to zero end - end - elseif ~issquare && isempty(powindx) - % data are linearly indexed - for j = 1:n - for k = 1:Nc - %iauto1 = sum(powindx==powindx(k,1),2)==2; - %iauto2 = sum(powindx==powindx(k,2),2)==2; - %icross1 = k; - %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2; - if mod(k-1, 4)==0 - continue; % auto granger set to 0 - %iauto1=k;iauto2=k;icross1=k;icross2=k; - elseif mod(k-1, 4)==1 - iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1; - elseif mod(k-1, 4)==2 - iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1; - elseif mod(k-1, 4)==3 - continue; % auto granger set to 0 - %iauto1=k;iauto2=k;icross1=k;icross2=k; + elseif ~issquare && isempty(powindx) + % data are linearly indexed + for j = 1:n + for k = 1:Nc + %iauto1 = sum(powindx==powindx(k,1),2)==2; + %iauto2 = sum(powindx==powindx(k,2),2)==2; + %icross1 = k; + %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2; + if mod(k-1, 4)==0 + continue; % auto granger set to 0 + %iauto1=k;iauto2=k;icross1=k;icross2=k; + elseif mod(k-1, 4)==1 + iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1; + elseif mod(k-1, 4)==2 + iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1; + elseif mod(k-1, 4)==3 + continue; % auto granger set to 0 + %iauto1=k;iauto2=k;icross1=k;icross2=k; + end + + zc1 = Z(j,iauto1,:, :) - Z(j,icross2,:, :).^2./Z(j,iauto2,:, :); + zc2 = Z(j,iauto2,:, :) - Z(j,icross1,:, :).^2./Z(j,iauto1,:, :); + term1 = abs(S(j,iauto2,:,:)) - zc1(:,:,ones(1,size(H,3)),:).*abs(H(j,icross2,:,:)).^2; + term2 = abs(S(j,iauto1,:,:)) - zc2(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2; + numer = term1.*term2; + denom = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:)); + + outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]); + outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]); end - - zc1 = Z(j,iauto1,:, :) - Z(j,icross2,:, :).^2./Z(j,iauto2,:, :); - zc2 = Z(j,iauto2,:, :) - Z(j,icross1,:, :).^2./Z(j,iauto1,:, :); - term1 = abs(S(j,iauto2,:,:)) - zc1(:,:,ones(1,size(H,3)),:).*abs(H(j,icross2,:,:)).^2; - term2 = abs(S(j,iauto1,:,:)) - zc2(:,:,ones(1,size(H,3)),:).*abs(H(j,icross1,:,:)).^2; - numer = term1.*term2; - denom = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:)); - - outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]); - outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]); end + elseif issquare && iscell(powindx) + % blockwise granger + % H = transfer function nchan x nchan x nfreq + % Z = noise covariance nchan x nchan + % S = crosspectrum nchan x nchan x nfreq + % powindx{1} is a list of indices for block1 + % powindx{2} is a list of indices for block2 + ft_error('instantaneous causality is not implemented for blockwise factorizations'); + elseif isstruct(powindx) + %blockwise conditional + ft_error('blockwise conditional instantaneous causality is not implemented'); + else + ft_error('not implemented'); end - elseif issquare && iscell(powindx) - % blockwise granger - % H = transfer function nchan x nchan x nfreq - % Z = noise covariance nchan x nchan - % S = crosspectrum nchan x nchan x nfreq - % powindx{1} is a list of indices for block1 - % powindx{2} is a list of indices for block2 - ft_error('instantaneous causality is not implemented for blockwise factorizations'); - elseif isstruct(powindx) - %blockwise conditional - ft_error('blockwise conditional instantaneous causality is not implemented'); - else - ft_error('not implemented'); - end - -case 'total' - - if issquare && isempty(powindx), - % data are chan_chan_therest - for kk = 1:n - for ii = 1:Nc - for jj = 1:Nc - if ii ~=jj, - numer = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:)); - denom = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:)); - outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom), [1 1 siz(4:end)]); - outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]); + + case 'total' + + if issquare && isempty(powindx) + % data are chan_chan_therest + for kk = 1:n + for ii = 1:Nc + for jj = 1:Nc + if ii ~=jj + numer = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:)); + denom = abs(S(kk,ii,ii,:,:).*S(kk,jj,jj,:,:) - S(kk,ii,jj,:,:).*S(kk,jj,ii,:,:)); + outsum(jj,ii,:,:) = outsum(jj,ii,:,:) + reshape(log(numer./denom), [1 1 siz(4:end)]); + outssq(jj,ii,:,:) = outssq(jj,ii,:,:) + reshape((log(numer./denom)).^2, [1 1 siz(4:end)]); + end end + outsum(ii,ii,:,:) = 0;%self-granger set to zero end - outsum(ii,ii,:,:) = 0;%self-granger set to zero end - end - elseif ~issquare && isempty(powindx) - % data are linearly indexed - for j = 1:n - for k = 1:Nc - %iauto1 = sum(powindx==powindx(k,1),2)==2; - %iauto2 = sum(powindx==powindx(k,2),2)==2; - %icross1 = k; - %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2; - if mod(k-1, 4)==0 - continue; % auto granger set to 0 - %iauto1=k;iauto2=k;icross1=k;icross2=k; - elseif mod(k-1, 4)==1 - iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1; - elseif mod(k-1, 4)==2 - iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1; - elseif mod(k-1, 4)==3 - continue; % auto granger set to 0 - %iauto1=k;iauto2=k;icross1=k;icross2=k; + elseif ~issquare && isempty(powindx) + % data are linearly indexed + for j = 1:n + for k = 1:Nc + %iauto1 = sum(powindx==powindx(k,1),2)==2; + %iauto2 = sum(powindx==powindx(k,2),2)==2; + %icross1 = k; + %icross2 = sum(powindx==powindx(ones(Nc,1)*k,[2 1]),2)==2; + if mod(k-1, 4)==0 + continue; % auto granger set to 0 + %iauto1=k;iauto2=k;icross1=k;icross2=k; + elseif mod(k-1, 4)==1 + iauto1=k+2;iauto2=k-1;icross1=k;icross2=k+1; + elseif mod(k-1, 4)==2 + iauto1=k-2;iauto2=k+1;icross1=k;icross2=k-1; + elseif mod(k-1, 4)==3 + continue; % auto granger set to 0 + %iauto1=k;iauto2=k;icross1=k;icross2=k; + end + + numer = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:)); + denom = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:)); + outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]); + outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]); end - - numer = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:)); - denom = abs(S(j,iauto1,:,:).*S(j,iauto2,:,:) - S(j,icross1,:,:).*S(j,icross2,:,:)); - outsum(icross2,:,:) = outsum(icross2,:,:) + reshape(log(numer./denom), [1 siz(3:end)]); - outssq(icross2,:,:) = outssq(icross2,:,:) + reshape((log(numer./denom)).^2, [1 siz(3:end)]); end - end - elseif issquare && iscell(powindx) - % blockwise total interdependence - - % S = crosspectrum nchan x nchan x nfreq - % powindx{k} is a list of indices for block k - - nblock = numel(powindx); - n = size(S,1); - nfreq = size(S,4); - - outsum = zeros(nblock,nblock,nfreq); - outssq = zeros(nblock,nblock,nfreq); - - for k = 1:nblock - for m = (k+1):nblock - indx = [powindx{k}(:);powindx{m}(:)]; - n1 = numel(powindx{k}); - n2 = numel(powindx{m}); - ntot = n1+n2; - indx1 = 1:n1; - indx2 = (n1+1):ntot; - - for kk = 1:n - for jj = 1:nfreq - Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]); - num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components - num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components - num = num1.*num2; - denom = abs(det(Sj)); - - outsum(m,k,jj) = log( num./denom ) + outsum(m,k,jj); - outsum(k,m,jj) = log( num./denom ) + outsum(k,m,jj); - outssq(m,k,jj) = log( num./denom ).^2 + outssq(m,k,jj); - outssq(k,m,jj) = log( num./denom ).^2 + outssq(k,m,jj); + elseif issquare && iscell(powindx) + % blockwise total interdependence + + % S = crosspectrum nchan x nchan x nfreq + % powindx{k} is a list of indices for block k + + nblock = numel(powindx); + n = size(S,1); + nfreq = size(S,4); + + outsum = zeros(nblock,nblock,nfreq); + outssq = zeros(nblock,nblock,nfreq); + + for k = 1:nblock + for m = (k+1):nblock + indx = [powindx{k}(:);powindx{m}(:)]; + n1 = numel(powindx{k}); + n2 = numel(powindx{m}); + ntot = n1+n2; + indx1 = 1:n1; + indx2 = (n1+1):ntot; + + for kk = 1:n + for jj = 1:nfreq + Sj = reshape(S(kk,indx,indx,jj), [ntot ntot]); + num1 = abs(det(Sj(indx1,indx1))); % numerical round off leads to tiny imaginary components + num2 = abs(det(Sj(indx2,indx2))); % numerical round off leads to tiny imaginary components + num = num1.*num2; + denom = abs(det(Sj)); + + outsum(m,k,jj) = log( num./denom ) + outsum(m,k,jj); + outsum(k,m,jj) = log( num./denom ) + outsum(k,m,jj); + outssq(m,k,jj) = log( num./denom ).^2 + outssq(m,k,jj); + outssq(k,m,jj) = log( num./denom ).^2 + outssq(k,m,jj); + end end + end - end + + elseif issquare && isstruct(powindx) + %blockwise conditional + ft_error('blockwise conditional total interdependence is not implemented'); end - elseif issquare && isstruct(powindx) - %blockwise conditional - ft_error('blockwise conditional total interdependence is not implemented'); - end - -case 'iis' - ft_warning('THIS IS EXPERIMENTAL CODE, USE AT YOUR OWN RISK!'); - % this is experimental - if ~issquare && isempty(powindx) - A = transfer2coeffs(shiftdim(H),(0:size(H,3)-1)); - ncmb = size(A,1)./4; - iis = coeffs2iis(reshape(A,[2 2 ncmb size(A,2)]),reshape(Z,[2 2 ncmb])); - iis = repmat(iis(:),[1 4])'; - outsum = iis(:); - outssq = nan(size(outsum)); - else - ft_error('iis can only be computed when the input contains sets of bivariate factorizations'); - end - -otherwise - ft_error('unsupported output requested'); + case 'iis' + ft_warning('THIS IS EXPERIMENTAL CODE, USE AT YOUR OWN RISK!'); + % this is experimental + if ~issquare && isempty(powindx) + A = transfer2coeffs(shiftdim(H),(0:size(H,3)-1)); + ncmb = size(A,1)./4; + iis = coeffs2iis(reshape(A,[2 2 ncmb size(A,2)]),reshape(Z,[2 2 ncmb])); + iis = repmat(iis(:),[1 4])'; + outsum = iis(:); + outssq = nan(size(outsum)); + else + ft_error('iis can only be computed when the input contains sets of bivariate factorizations'); + end + + otherwise + ft_error('unsupported output requested'); end granger = outsum./n; -if n>1, +if n>1 if hasjack bias = (n-1).^2; else diff --git a/external/fieldtrip/connectivity/ft_connectivity_laggedcoherence.m b/external/fieldtrip/connectivity/ft_connectivity_laggedcoherence.m index 4677aff1..859ed704 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_laggedcoherence.m +++ b/external/fieldtrip/connectivity/ft_connectivity_laggedcoherence.m @@ -1,83 +1,84 @@ function [lcoh] = ft_connectivity_laggedcoherence(cfg,freqout) -% FT_CONNECTIVITY_LAGGEDCOHERENCE performs time-resolved coherence analysis -% of oscillatory activity only, both within and between recording sites. +% FT_CONNECTIVITY_LAGGEDCOHERENCE performs time-resolved coherence analysis of +% oscillatory activity only, both within and between recording sites. This implements +% the method described in Fransen, Anne M. M, Van Ede, Freek, Maris, Eric (2015) +% Identifying oscillations on the basis of rhythmicity. NeuroImage 118: 256-267. % % Use as -% lcoh=ft_connectivityanalysis(cfg,freqout) with cfg.method='laggedcoherence'; -% or as lcoh=ft_connectivity_laggedcoherence(cfg,freqout) +% lcoh = ft_connectivityanalysis(cfg, freq) +% with cfg.method='laggedcoherence' or as +% lcoh = ft_connectivity_laggedcoherence(cfg, freq) % -% The input data should be organised in a structure as obtained from -% the FT_FREQANALYSIS function (freqout), such that freqout contains the -% fields 'fourierspctrm' and 'time'. -% The timepoints must be chosen such that the desired cfg.lag/cfg.foi -% (lag in s) is an integer multiple of the time resolution in freqout. +% The input data should be organised in a structure as obtained from FT_FREQANALYSIS +% and should contain the fields 'fourierspctrm' and 'time'. The timepoints must be +% chosen such that the desired cfg.lag/cfg.foi (lag in s) is an integer multiple of +% the time resolution in freqout. % -% At the moment, this function must be called separately for each frequency -% of interest. To analyse multiple frequencies, we advise the use of a -% forloop such as this: -% cfg_F = []; -% cfg_LC = []; -% cfg_F.method = 'wavelet'; -% cfg_F.output = 'fourier'; -% cfg_F.width = 3; -% cfg_F.keeptrials = 'yes'; -% cfg_LC.lag = cfg_F.width; -% cfg_LC.method = 'laggedcoherence'; -% foi = 1:1:100; -% fs = data.fsample; -% for counter = 1:length(foi); -% cfg_F.foi = foi(counter); -% cfg_LC.foi = foi(counter); -% width = cfg_F.width/cfg_F.foi; -% cfg_F.toi = data.time{1}(1) + ceil(fs*width/2)/fs : ... %from: -% cfg_LC.lag/cfg_F.foi : ... %in steps of size: -% data.time{1}(end) - ceil(fs*width/2)/fs; %to: -% freqout = ft_freqanalysis(cfg_F,data); -% lcoh(counter) = ft_connectivityanalysis(cfg_LC,freqout); -% end +% This function must be called separately for each frequency of interest. To analyse +% multiple frequencies, we advise the use of a for loop like this: +% cfg_F = []; +% cfg_F.method = 'wavelet'; +% cfg_F.output = 'fourier'; +% cfg_F.width = 3; +% cfg_F.keeptrials = 'yes'; +% cfg_LC = []; +% cfg_LC.lag = cfg_F.width; +% cfg_LC.method = 'laggedcoherence'; +% foi = 1:1:100; +% fs = data.fsample; +% for counter = 1:length(foi); +% cfg_F.foi = foi(counter); +% cfg_LC.foi = foi(counter); +% width = cfg_F.width/cfg_F.foi; +% cfg_F.toi = data.time{1}(1) + ceil(fs*width/2)/fs : ... %from: +% cfg_LC.lag/cfg_F.foi : ... %in steps of size: +% data.time{1}(end) - ceil(fs*width/2)/fs; %to: +% freqout = ft_freqanalysis(cfg_F,data); +% lcoh(counter) = ft_connectivityanalysis(cfg_LC,freqout); +% end % -% The configuration for ft_connectivity_laggedcoherence should contain: -% cfg.foi = frequency of interest (default=freqout.freq(1)) -% cfg.lag = the number of periods between the onset of the -% time window used for phase estimate 1 and the onset of the time -% window for phase estimate 2 (the default cfg.lag is set to match the -% time resolution in freqout). We recommend users to choose cfg.lag such -% that it is larger or equal to the width of the wavelet used for each -% Fourier transform in ft_freqanalysis -% cfg.output = 'lcoh', or 'csd' (default='lcoh'). When the output -% is set to 'csd', one can specify the channel combinations between -% which to compute the lagged cross-spectra in cfg.channelcmb. +% The configuration structure cfg should contain: +% cfg.foi = frequency of interest (default=freqout.freq(1)) +% cfg.lag = the number of periods between the onset of the time window +% used for phase estimate 1 and the onset of the time window +% for phase estimate 2 (the default cfg.lag is set to match the +% time resolution in freqout). We recommend users to choose +% cfg.lag such that it is larger or equal to the width of the +% wavelet used for each Fourier transform in ft_freqanalysis +% cfg.output = 'lcoh', or 'csd' (default='lcoh'). When the output +% is set to 'csd', one can specify the channel combinations +% between which to compute the lagged cross-spectra in +% cfg.channelcmb % -% To calculate lagged coherence values from the cross-spectra, do: -% abs(lcoh.laggedcrsspctrm)./sqrt(lcoh.powspctrm1.*lcoh.powspctrm2) -% where lcoh.powspctrm1 denotes power in the channels of the first -% column of cfg.channelcmb, and lcoh.powspctrm2 does the same for the -% channels in the second column of cfg.channelcmb. Note that the power -% is calculated for the same time windows that are used for calculating -% the lagged cross-spectra. +% To calculate lagged coherence values from the cross-spectra, do: +% abs(lcoh.laggedcrsspctrm)./sqrt(lcoh.powspctrm1.*lcoh.powspctrm2) +% where lcoh.powspctrm1 denotes power in the channels of the first +% column of cfg.channelcmb, and lcoh.powspctrm2 does the same for the +% channels in the second column of cfg.channelcmb. Note that the power +% is calculated for the same time windows that are used for calculating +% the lagged cross-spectra. % % Optional settings: -% cfg.timeresolved = 'yes' or 'no' (default='no'). If set to yes, lagged -% coherence is calculated separately for each pair of timepoints that -% is separated by cfg.lag -% cfg.nlags = The lags in lcoh are the set of (1:1:nlags)*lag -% (default: cfg.nlag=1). Note that if cfg.timeresolved=='yes', then -% cfg.nlags must be set to 1. -% cfg.channel = Nx1 cell-array with selection of channels -% (default = 'all'), see FT_CHANNELSELECTION for details -% cfg.channelcmb = Mx2 cell-array with selection of channel pairs, -% (default={'all','all'}), see FT_CHANNELCOMBINATION for details. -% cfg.autocmb = 'yes' or 'no' (default='no'). Adds all auto- -% combinations of cfg.channel to cfg.channelcmb -% cfg.trialsets = cell array with per cell the set of trials over -% which lcoh is calculated. Each cell must contain 'all' or a 1xN -% vector of trial indices. Default={'all'}. Note that this differs -% from the required format of cfg.trials in e.g. ft_connectivityanalysis. +% cfg.timeresolved = 'yes' or 'no' (default='no'). If set to yes, lagged +% coherence is calculated separately for each pair of timepoints +% that is separated by cfg.lag +% cfg.nlags = the lags in lcoh are the set of (1:1:nlags)*lag +% (default = 1). Note that if cfg.timeresolved=='yes', then +% cfg.nlags must be set to 1. +% cfg.channel = Nx1 cell-array with selection of channels +% (default = 'all'), see FT_CHANNELSELECTION for details +% cfg.channelcmb = Mx2 cell-array with channel pairs, (default={'all','all'}), +% see FT_CHANNELCOMBINATION for details +% cfg.autocmb = 'yes' or 'no' (default='no'). Adds all auto- +% combinations of cfg.channel to cfg.channelcmb +% cfg.trialsets = cell array with per cell the set of trials over +% which lcoh is calculated. Each cell must contain 'all' or a +% 1xN vector of trial indices. Default={'all'}. Note that this +% differs from the required format of cfg.trials in e.g. +% ft_connectivityanalysis. % -% When this measure is used for your publication, please cite: -% Fransen, Anne M. M, Van Ede, Freek, Maris, Eric (2015) Identifying -% oscillations on the basis of rhythmicity. NeuroImage 118: 256-267. +% See also FT_CONNECTIVITYANALYSIS % Copyright (C) 2015, Anne M.M. Fransen, DCN % @@ -121,15 +122,15 @@ if ~iscell(cfg.trialsets); cfg.trialsets={cfg.trialsets}; end % adjust cfg.foi to nearest possibility in freqout.freq, and only allow 1 foi -[dummy indexf] = min(abs(freqout.freq - cfg.foi(1))); -cfg.foi = freqout.freq(indexf); +[dummy, indexf] = min(abs(freqout.freq - cfg.foi(1))); +cfg.foi = freqout.freq(indexf); % check if the input data is valid for this function freqout = ft_checkdata(freqout, 'datatype', {'freq'}, 'feedback', cfg.feedback,'dimord',{'rpttap_chan_freq_time','rpt_chan_freq_time'}); if ~isfield(freqout,'fourierspctrm') - ft_error('ft_connectivity_laggedcoherence requires frequency data with a fourierspctrm.'); + ft_error('ft_connectivity_laggedcoherence requires frequency data with a fourierspctrm.'); elseif size(freqout.fourierspctrm,4)==1 - ft_error('ft_connectivity_laggedcoherence requires frequency data with a time axis') + ft_error('ft_connectivity_laggedcoherence requires frequency data with a time axis') end % ensure that cfg.lag is present @@ -144,49 +145,49 @@ % select trials of interest for each trialset for counter=1:length(cfg.trialsets) - if strcmp(cfg.trialsets{counter},'all') - cfg.trialsets{counter} = 1:size(freqout.fourierspctrm,1); - end - if size(cfg.trialsets{counter},1)==1 - cfg.trialsets{counter}=cfg.trialsets{counter}'; - end + if strcmp(cfg.trialsets{counter},'all') + cfg.trialsets{counter} = 1:size(freqout.fourierspctrm,1); + end + if size(cfg.trialsets{counter},1)==1 + cfg.trialsets{counter}=cfg.trialsets{counter}'; + end end % some proper error handling if numel(freqout.label)==0 - ft_error('no channels were selected'); + ft_error('no channels were selected'); end if ~(strcmp(cfg.output,'lcoh') || strcmp(cfg.output,'csd')) - ft_error('cfg.output must be either ''lcoh'' or ''csd'''); + ft_error('cfg.output must be either ''lcoh'' or ''csd'''); end index = cellfun('isclass',cfg.trialsets,'char'); if sum(index)>0 - index2 = cellfun(@(x) strcmp(x,'all'),cfg.trialsets(index),'UniformOutput',true); - if any(index2==0) - ft_error('each cell of cfg.trialsets must contain either a 1xN vector, or ''all'''); - end - cfg.trialsets(index) = {1:size(freqout.fourierspctrm,1)}; + index2 = cellfun(@(x) strcmp(x,'all'),cfg.trialsets(index),'UniformOutput',true); + if any(index2==0) + ft_error('each cell of cfg.trialsets must contain either a 1xN vector, or ''all'''); + end + cfg.trialsets(index) = {1:size(freqout.fourierspctrm,1)}; end clear tmpcfg index index2 % prepare cfg.channelcmb csdflag = strcmp(cfg.output,'csd'); if ~csdflag - %only autocombinations - cfg.channelcmb = [cfg.channel,cfg.channel]; + %only autocombinations + cfg.channelcmb = [cfg.channel,cfg.channel]; else - if ~isfield(cfg, 'channelcmb') - %set the default for the channelcombination - cfg.channelcmb = {'all' 'all'}; - end - cfg.channelcmb = ft_channelcombination(cfg.channelcmb,freqout.label,cfg.autocmb); + if ~isfield(cfg, 'channelcmb') + %set the default for the channelcombination + cfg.channelcmb = {'all' 'all'}; + end + cfg.channelcmb = ft_channelcombination(cfg.channelcmb,freqout.label,cfg.autocmb); end % determine the corresponding indices of all channel combinations -[dummy,chancmbind(:,1)] = match_str(cfg.channelcmb(:,1), cfg.channel); -[dummy,chancmbind(:,2)] = match_str(cfg.channelcmb(:,2), cfg.channel); +[dummy, chancmbind(:,1)] = match_str(cfg.channelcmb(:,1), cfg.channel); +[dummy, chancmbind(:,2)] = match_str(cfg.channelcmb(:,2), cfg.channel); clear dummy if csdflag - assert(length(unique(chancmbind(:)))>1, 'CSD output requires multiple channels'); + assert(length(unique(chancmbind(:)))>1, 'CSD output requires multiple channels'); end %initiate some variables @@ -199,279 +200,277 @@ % switch between time-resolved and non-time-resolved calculation of lagged coherence switch cfg.timeresolved - case 'no' - %initiate some variables - laggedcps = complex(zeros(nchancmb,cfg.nlags,nrep)); - power = complex(zeros(nchancmb,cfg.nlags,2,nrep)); - hasdata = false(nrep,cfg.nlags); - nsmplslaggedcps = zeros(cfg.nlags,nrep); - lagwidth = zeros(1,cfg.nlags); - - for lagindx=1:cfg.nlags - % select all pairs of timepoints with relative lag cfg.lag (identified with precision cfg.precision) - lagwidth(lagindx) = cfg.lag*cyclelength*lagindx; - lagwidth(lagindx) = dtim(find(abs(dtim-lagwidth(lagindx))==min(abs(dtim(:)-lagwidth(lagindx))),1)); - [t1 t2] = find(dtim==lagwidth(lagindx)); - - % calculate laggedcrossproducts and power per channelcmb and trial - for trialindx=1:nrep - % get the spectrum for this trial and frequency - fcs1 = complex(zeros(size(freqout.fourierspctrm,2),length(t1))); - fcs2 = fcs1; % fcs1 and fcs2 have dimord chan_time - fcs1(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t1); - fcs2(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t2); - colswithnans = any(isnan(fcs1)|isnan(fcs2),1); - fcs1(:,colswithnans) = []; fcs2(:,colswithnans) = []; - % sum laggedcrossproducts and power over all timepoints - for tcounter=1:length(t1) - laggedcrossproduct = fcs1(chancmbind(:,1),tcounter).*conj(fcs2(chancmbind(:,2),tcounter)); - laggedcps(:,lagindx,trialindx) = laggedcps(:,lagindx,trialindx)+ laggedcrossproduct; - power(:,lagindx,1,trialindx) = power(:,lagindx,1,trialindx)+ abs(fcs1(chancmbind(:,1),tcounter)).^2; - power(:,lagindx,2,trialindx) = power(:,lagindx,2,trialindx)+ abs(fcs2(chancmbind(:,2),tcounter)).^2; - hasdata(trialindx,lagindx) = true; - nsmplslaggedcps(lagindx,trialindx) = nsmplslaggedcps(lagindx,trialindx)+1; - end - end + case 'no' + %initiate some variables + laggedcps = complex(zeros(nchancmb,cfg.nlags,nrep)); + power = complex(zeros(nchancmb,cfg.nlags,2,nrep)); + hasdata = false(nrep,cfg.nlags); + nsmplslaggedcps = zeros(cfg.nlags,nrep); + lagwidth = zeros(1,cfg.nlags); + + for lagindx=1:cfg.nlags + % select all pairs of timepoints with relative lag cfg.lag (identified with precision cfg.precision) + lagwidth(lagindx) = cfg.lag*cyclelength*lagindx; + lagwidth(lagindx) = dtim(find(abs(dtim-lagwidth(lagindx))==min(abs(dtim(:)-lagwidth(lagindx))),1)); + [t1, t2] = find(dtim==lagwidth(lagindx)); + + % calculate laggedcrossproducts and power per channelcmb and trial + for trialindx=1:nrep + % get the spectrum for this trial and frequency + fcs1 = complex(zeros(size(freqout.fourierspctrm,2),length(t1))); + fcs2 = fcs1; % fcs1 and fcs2 have dimord chan_time + fcs1(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t1); + fcs2(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t2); + colswithnans = any(isnan(fcs1)|isnan(fcs2),1); + fcs1(:,colswithnans) = []; fcs2(:,colswithnans) = []; + % sum laggedcrossproducts and power over all timepoints + for tcounter=1:length(t1) + laggedcrossproduct = fcs1(chancmbind(:,1),tcounter).*conj(fcs2(chancmbind(:,2),tcounter)); + laggedcps(:,lagindx,trialindx) = laggedcps(:,lagindx,trialindx)+ laggedcrossproduct; + power(:,lagindx,1,trialindx) = power(:,lagindx,1,trialindx)+ abs(fcs1(chancmbind(:,1),tcounter)).^2; + power(:,lagindx,2,trialindx) = power(:,lagindx,2,trialindx)+ abs(fcs2(chancmbind(:,2),tcounter)).^2; + hasdata(trialindx,lagindx) = true; + nsmplslaggedcps(lagindx,trialindx) = nsmplslaggedcps(lagindx,trialindx)+1; end - - % calculate lagged coherence - if strcmp('lcoh',cfg.output) - laggedcoh = complex(nan(ntrialsets,nchancmb,cfg.nlags)); - for trialset=1:ntrialsets - % sum laggedcps and laggedpower per trialset - trials = cfg.trialsets{trialset}; - sumlaggedcps = nansum(laggedcps(:,:,trials),3); - sumpower = nansum(power(:,:,:,trials),4); - % normalise the lagged csd - denomcoh = sqrt(sumpower(:,:,1).*sumpower(:,:,2)); - laggedcoh(trialset,:,:) = abs(sumlaggedcps)./denomcoh; - for lagindex=1:cfg.nlags - laggedcoh(trialset,:,sum(hasdata(trials,lagindx),1)==0) = NaN; - end - end - % make output structure - lcoh = []; - lcoh.laggedcoh = laggedcoh; - lcoh.label = freqout.label; - lcoh.freq = cfg.foi; - lcoh.lag = lagwidth*cfg.foi; - if length(cfg.trialsets)==1 - lcoh.laggedcoh = shiftdim(lcoh.laggedcoh,1); - if length(lcoh.lag)==1 - lcoh.dimord = 'chan'; - else - lcoh.dimord = 'chan_rep'; - end - else - if all(cellfun(@length,cfg.trialsets)==1) - trials=cell2mat(cfg.trialsets); - if isfield(freqout,'trialinfo') - lcoh.trialinfo=freqout.trialinfo(trials,:); - end - else - if isfield(freqout,'trialinfo') - for counter=1:ntrialsets - lcoh.trialinfos{counter}=freqout.trialinfo(cfg.trialsets{counter},:); - end - end - lcoh.trialsets = cfg.trialsets; - end - if length(lcoh.lag)==1 - lcoh.dimord = 'rep_chan'; - else - lcoh.dimord = 'rep_chan_rep'; - end - end - - % calculate lagged crossspectra and corresponding power spectra - elseif csdflag - laggedcrsspctrm = complex(zeros(ntrialsets,nchancmb,cfg.nlags)); - powspctrm1 = complex(zeros(ntrialsets,nchancmb,cfg.nlags)); - powspctrm2 = complex(zeros(ntrialsets,nchancmb,cfg.nlags)); - for trialset=1:ntrialsets - % sum laggedcps and laggedpower per trialset - trials = cfg.trialsets{trialset}; - sumlaggedcps = nansum(laggedcps(:,:,trials),3); - sumlaggedpower = nansum(power(:,:,:,trials),4); - for lagindx=1:cfg.nlags - sumnsmpls = nansum(nsmplslaggedcps(lagindx,trials),2); - laggedcrsspctrm(trialset,:,lagindx) = sumlaggedcps(:,lagindx)/sumnsmpls; - powspctrm1(trialset,:,lagindx) = sumlaggedpower(:,lagindx,1)/sumnsmpls; - powspctrm2(trialset,:,lagindx) = sumlaggedpower(:,lagindx,2)/sumnsmpls; - if sum(hasdata(trials,lagindx),1)==0 - laggedcrsspctrm(trialset,:,lagindx) = NaN; - end - end - end - % make output structure - lcoh = []; - lcoh.laggedcrsspctrm = laggedcrsspctrm; - lcoh.powspctrm1 = powspctrm1; - lcoh.powspctrm2 = powspctrm2; - lcoh.label = freqout.label; - lcoh.freq = cfg.foi; - lcoh.lag = lagwidth*cfg.foi; - lcoh.labelcmb = cfg.channelcmb; - if length(cfg.trialsets)==1 - lcoh.laggedcrsspctrm = shiftdim(lcoh.laggedcrsspctrm,1); - lcoh.powspctrm1 = shiftdim(lcoh.powspctrm1,1); - lcoh.powspctrm2 = shiftdim(lcoh.powspctrm2,1); - if length(lcoh.lag)==1 - lcoh.dimord = 'chan'; - else - lcoh.dimord = 'chan_rep'; - end - else - if all(cellfun(@length,cfg.trialsets)==1) - trials=cell2mat(cfg.trialsets); - if isfield(freqout,'trialinfo') - lcoh.trialinfo=freqout.trialinfo(trials,:); - end - else - if isfield(freqout,'trialinfo') - for counter=1:ntrialsets - lcoh.trialinfos{counter}=freqout.trialinfo(cfg.trialsets{counter},:); - end - end - lcoh.trialsets = cfg.trialsets; - end - if length(lcoh.lag)==1 - lcoh.dimord = 'rep_chan'; - else - lcoh.dimord = 'rep_chan_rep'; - end - end + end + end + + % calculate lagged coherence + if strcmp('lcoh',cfg.output) + laggedcoh = complex(nan(ntrialsets,nchancmb,cfg.nlags)); + for trialset=1:ntrialsets + % sum laggedcps and laggedpower per trialset + trials = cfg.trialsets{trialset}; + sumlaggedcps = nansum(laggedcps(:,:,trials),3); + sumpower = nansum(power(:,:,:,trials),4); + % normalise the lagged csd + denomcoh = sqrt(sumpower(:,:,1).*sumpower(:,:,2)); + laggedcoh(trialset,:,:) = abs(sumlaggedcps)./denomcoh; + for lagindex=1:cfg.nlags + laggedcoh(trialset,:,sum(hasdata(trials,lagindx),1)==0) = NaN; end - - case 'yes' - % method-specfic checks - if cfg.nlags>1 - ft_error('when calculating timeresolved lcoh, cfg.nlags must be set to 1'); + end + % make output structure + lcoh = []; + lcoh.laggedcoh = laggedcoh; + lcoh.label = freqout.label; + lcoh.freq = cfg.foi; + lcoh.lag = lagwidth*cfg.foi; + if length(cfg.trialsets)==1 + lcoh.laggedcoh = shiftdim(lcoh.laggedcoh,1); + if length(lcoh.lag)==1 + lcoh.dimord = 'chan'; + else + lcoh.dimord = 'chan_rep'; end - - % select pairs of timepoints with relative lag cfg.lag (identified with precision cfg.precision) - lagwidth = cfg.lag*cyclelength; - lagwidth = dtim(find(abs(dtim-lagwidth)==min(abs(dtim(:)-lagwidth)),1)); - [t1 t2] = find(dtim==lagwidth); - - % initiate some variables (dependent on nr of timepoints) - ntoi = length(t1); - laggedcrossproduct = complex(zeros(nchancmb,ntoi,nrep)); - power = complex(zeros(nchancmb,ntoi,2,nrep)); - hasdata = false(nrep,ntoi); - - % calculate laggedcrossproducts and power per channel, time of interest, and trial - for trialindx=1:nrep - % get the spectrum for this trial and frequency - fcs1 = complex(zeros(size(freqout.fourierspctrm,2),length(t1))); - fcs2 = fcs1; % fcs1 and fcs2 have dimord chan_time - fcs1(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t1); - fcs2(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t2); - colswithnans = any(isnan(fcs1)|isnan(fcs2),1); - fcs1(:,colswithnans) = []; - fcs2(:,colswithnans) = []; - % sum laggedcps and laggedpower over all timepoints - for tcounter=1:ntoi - laggedcrossproduct(:,tcounter,trialindx)= fcs1(chancmbind(:,1),tcounter).*conj(fcs2(chancmbind(:,2),tcounter)); - power(:,tcounter,1,trialindx) = abs(fcs1(chancmbind(:,1),tcounter)).^2; - power(:,tcounter,2,trialindx) = abs(fcs2(chancmbind(:,2),tcounter)).^2; - hasdata(trialindx,tcounter) = true; + else + if all(cellfun(@length,cfg.trialsets)==1) + trials=cell2mat(cfg.trialsets); + if isfield(freqout,'trialinfo') + lcoh.trialinfo=freqout.trialinfo(trials,:); + end + else + if isfield(freqout,'trialinfo') + for counter=1:ntrialsets + lcoh.trialinfos{counter}=freqout.trialinfo(cfg.trialsets{counter},:); end + end + lcoh.trialsets = cfg.trialsets; end - - % calculate lagged coherence - if strcmp('lcoh',cfg.output) - ntrialsets = length(cfg.trialsets); - laggedcoh = complex(nan(ntrialsets,nchancmb,ntoi)); - for trialset=1:ntrialsets - % sum laggedcps and laggedpower per trialset - trials = cfg.trialsets{trialset}; - trialsumlaggedcps = nansum(laggedcrossproduct(:,:,trials),3); - trialsumpower = nansum(power(:,:,:,trials),4); - for toii=1:ntoi - sumlaggedcps = trialsumlaggedcps(:,toii); - sumpower = trialsumpower(:,toii,:); - % normalise the power per frequency - denomcoh = sqrt(sumpower(:,:,1).*sumpower(:,:,2)); - laggedcoh(trialset,:,toii) = abs(sumlaggedcps)./denomcoh; - end - end - % make output structure - lcoh = []; - lcoh.laggedcoh = laggedcoh; - lcoh.label = freqout.label; - lcoh.freq = cfg.foi; - lcoh.time = nanmean([freqout.time(t1);freqout.time(t2)],1); - lcoh.lag = (1:1:cfg.nlags)*lagwidth*cfg.foi; - if length(cfg.trialsets)==1 - lcoh.laggedcoh = shiftdim(lcoh.laggedcoh,1); - lcoh.dimord = 'chan_time'; - else - if all(cellfun(@length,cfg.trialsets)==1) - trials=cell2mat(cfg.trialsets); - if isfield(freqout,'trialinfo') - lcoh.trialinfo=freqout.trialinfo(trials,:); - end - else - if isfield(freqout,'trialinfo') - for counter=1:ntrialsets - lcoh.trialinfos{counter} = freqout.trialinfo(cfg.trialsets{counter},:); - end - end - lcoh.trialsets = cfg.trialsets; - end - lcoh.dimord = 'rep_chan_time'; + if length(lcoh.lag)==1 + lcoh.dimord = 'rep_chan'; + else + lcoh.dimord = 'rep_chan_rep'; + end + end + + % calculate lagged crossspectra and corresponding power spectra + elseif csdflag + laggedcrsspctrm = complex(zeros(ntrialsets,nchancmb,cfg.nlags)); + powspctrm1 = complex(zeros(ntrialsets,nchancmb,cfg.nlags)); + powspctrm2 = complex(zeros(ntrialsets,nchancmb,cfg.nlags)); + for trialset=1:ntrialsets + % sum laggedcps and laggedpower per trialset + trials = cfg.trialsets{trialset}; + sumlaggedcps = nansum(laggedcps(:,:,trials),3); + sumlaggedpower = nansum(power(:,:,:,trials),4); + for lagindx=1:cfg.nlags + sumnsmpls = nansum(nsmplslaggedcps(lagindx,trials),2); + laggedcrsspctrm(trialset,:,lagindx) = sumlaggedcps(:,lagindx)/sumnsmpls; + powspctrm1(trialset,:,lagindx) = sumlaggedpower(:,lagindx,1)/sumnsmpls; + powspctrm2(trialset,:,lagindx) = sumlaggedpower(:,lagindx,2)/sumnsmpls; + if sum(hasdata(trials,lagindx),1)==0 + laggedcrsspctrm(trialset,:,lagindx) = NaN; + end + end + end + % make output structure + lcoh = []; + lcoh.laggedcrsspctrm = laggedcrsspctrm; + lcoh.powspctrm1 = powspctrm1; + lcoh.powspctrm2 = powspctrm2; + lcoh.label = freqout.label; + lcoh.freq = cfg.foi; + lcoh.lag = lagwidth*cfg.foi; + lcoh.labelcmb = cfg.channelcmb; + if length(cfg.trialsets)==1 + lcoh.laggedcrsspctrm = shiftdim(lcoh.laggedcrsspctrm,1); + lcoh.powspctrm1 = shiftdim(lcoh.powspctrm1,1); + lcoh.powspctrm2 = shiftdim(lcoh.powspctrm2,1); + if length(lcoh.lag)==1 + lcoh.dimord = 'chan'; + else + lcoh.dimord = 'chan_rep'; + end + else + if all(cellfun(@length,cfg.trialsets)==1) + trials=cell2mat(cfg.trialsets); + if isfield(freqout,'trialinfo') + lcoh.trialinfo=freqout.trialinfo(trials,:); + end + else + if isfield(freqout,'trialinfo') + for counter=1:ntrialsets + lcoh.trialinfos{counter}=freqout.trialinfo(cfg.trialsets{counter},:); end - - % calculate lagged crossspectra and corresponding power spectra - elseif csdflag - laggedcrsspctrm = complex(nan(ntrialsets,nchancmb,ntoi)); - powspctrm1 = complex(nan(ntrialsets,nchancmb,ntoi)); - powspctrm2 = complex(nan(ntrialsets,nchancmb,ntoi)); - for trialset=1:ntrialsets - % sum laggedcps and laggedpower per trialset - trials = cfg.trialsets{trialset}; - sumlaggedcps = nansum(laggedcrossproduct(:,:,trials),3); - sumpower = nansum(power(:,:,:,trials),4); - sumnsmpls = length(trials); - for toii=1:ntoi - laggedcrsspctrm(trialset,:,toii)= sumlaggedcps(:,toii)/sumnsmpls; - powspctrm1(trialset,:,toii) = sumpower(:,toii,1)/sumnsmpls; - powspctrm2(trialset,:,toii) = sumpower(:,toii,2)/sumnsmpls; - end + end + lcoh.trialsets = cfg.trialsets; + end + if length(lcoh.lag)==1 + lcoh.dimord = 'rep_chan'; + else + lcoh.dimord = 'rep_chan_rep'; + end + end + end + + case 'yes' + % method-specfic checks + if cfg.nlags>1 + ft_error('when calculating timeresolved lcoh, cfg.nlags must be set to 1'); + end + + % select pairs of timepoints with relative lag cfg.lag (identified with precision cfg.precision) + lagwidth = cfg.lag*cyclelength; + lagwidth = dtim(find(abs(dtim-lagwidth)==min(abs(dtim(:)-lagwidth)),1)); + [t1, t2] = find(dtim==lagwidth); + + % initiate some variables (dependent on nr of timepoints) + ntoi = length(t1); + laggedcrossproduct = complex(zeros(nchancmb,ntoi,nrep)); + power = complex(zeros(nchancmb,ntoi,2,nrep)); + hasdata = false(nrep,ntoi); + + % calculate laggedcrossproducts and power per channel, time of interest, and trial + for trialindx=1:nrep + % get the spectrum for this trial and frequency + fcs1 = complex(zeros(size(freqout.fourierspctrm,2),length(t1))); + fcs2 = fcs1; % fcs1 and fcs2 have dimord chan_time + fcs1(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t1); + fcs2(:,:) = freqout.fourierspctrm(trialindx,:,indexf,t2); + colswithnans = any(isnan(fcs1)|isnan(fcs2),1); + fcs1(:,colswithnans) = []; + fcs2(:,colswithnans) = []; + % sum laggedcps and laggedpower over all timepoints + for tcounter=1:ntoi + laggedcrossproduct(:,tcounter,trialindx)= fcs1(chancmbind(:,1),tcounter).*conj(fcs2(chancmbind(:,2),tcounter)); + power(:,tcounter,1,trialindx) = abs(fcs1(chancmbind(:,1),tcounter)).^2; + power(:,tcounter,2,trialindx) = abs(fcs2(chancmbind(:,2),tcounter)).^2; + hasdata(trialindx,tcounter) = true; + end + end + + % calculate lagged coherence + if strcmp('lcoh',cfg.output) + ntrialsets = length(cfg.trialsets); + laggedcoh = complex(nan(ntrialsets,nchancmb,ntoi)); + for trialset=1:ntrialsets + % sum laggedcps and laggedpower per trialset + trials = cfg.trialsets{trialset}; + trialsumlaggedcps = nansum(laggedcrossproduct(:,:,trials),3); + trialsumpower = nansum(power(:,:,:,trials),4); + for toii=1:ntoi + sumlaggedcps = trialsumlaggedcps(:,toii); + sumpower = trialsumpower(:,toii,:); + % normalise the power per frequency + denomcoh = sqrt(sumpower(:,:,1).*sumpower(:,:,2)); + laggedcoh(trialset,:,toii) = abs(sumlaggedcps)./denomcoh; + end + end + % make output structure + lcoh = []; + lcoh.laggedcoh = laggedcoh; + lcoh.label = freqout.label; + lcoh.freq = cfg.foi; + lcoh.time = nanmean([freqout.time(t1);freqout.time(t2)],1); + lcoh.lag = (1:1:cfg.nlags)*lagwidth*cfg.foi; + if length(cfg.trialsets)==1 + lcoh.laggedcoh = shiftdim(lcoh.laggedcoh,1); + lcoh.dimord = 'chan_time'; + else + if all(cellfun(@length,cfg.trialsets)==1) + trials=cell2mat(cfg.trialsets); + if isfield(freqout,'trialinfo') + lcoh.trialinfo=freqout.trialinfo(trials,:); + end + else + if isfield(freqout,'trialinfo') + for counter=1:ntrialsets + lcoh.trialinfos{counter} = freqout.trialinfo(cfg.trialsets{counter},:); end - % make output structure - lcoh = []; - lcoh.laggedcrsspctrm = laggedcrsspctrm; - lcoh.powspctrm1 = powspctrm1; - lcoh.powspctrm2 = powspctrm2; - lcoh.label = freqout.label; - lcoh.freq = cfg.foi; - lcoh.time = nanmean([freqout.time(t1);freqout.time(t2)],1); - lcoh.lag = (1:1:cfg.nlags)*lagwidth*cfg.foi; - lcoh.labelcmb = cfg.channelcmb; - if length(cfg.trialsets)==1 - lcoh.laggedcrsspctrm = shiftdim(lcoh.laggedcrsspctrm,1); - lcoh.powspctrm1 = shiftdim(lcoh.powspctrm1,1); - lcoh.powspctrm2 = shiftdim(lcoh.powspctrm2,1); - lcoh.dimord = 'chan_time'; - else - if all(cellfun(@length,cfg.trialsets)==1) - trials=cell2mat(cfg.trialsets); - if isfield(freqout,'trialinfo') - lcoh.trialinfo=freqout.trialinfo(trials,:); - end - else - if isfield(freqout,'trialinfo') - for counter=1:ntrialsets - lcoh.trialinfos{counter} = freqout.trialinfo(cfg.trialsets{counter},:); - end - end - lcoh.trialsets = cfg.trialsets; - end - lcoh.dimord = 'rep_chan_time'; + end + lcoh.trialsets = cfg.trialsets; + end + lcoh.dimord = 'rep_chan_time'; + end + + % calculate lagged crossspectra and corresponding power spectra + elseif csdflag + laggedcrsspctrm = complex(nan(ntrialsets,nchancmb,ntoi)); + powspctrm1 = complex(nan(ntrialsets,nchancmb,ntoi)); + powspctrm2 = complex(nan(ntrialsets,nchancmb,ntoi)); + for trialset=1:ntrialsets + % sum laggedcps and laggedpower per trialset + trials = cfg.trialsets{trialset}; + sumlaggedcps = nansum(laggedcrossproduct(:,:,trials),3); + sumpower = nansum(power(:,:,:,trials),4); + sumnsmpls = length(trials); + for toii=1:ntoi + laggedcrsspctrm(trialset,:,toii)= sumlaggedcps(:,toii)/sumnsmpls; + powspctrm1(trialset,:,toii) = sumpower(:,toii,1)/sumnsmpls; + powspctrm2(trialset,:,toii) = sumpower(:,toii,2)/sumnsmpls; + end + end + % make output structure + lcoh = []; + lcoh.laggedcrsspctrm = laggedcrsspctrm; + lcoh.powspctrm1 = powspctrm1; + lcoh.powspctrm2 = powspctrm2; + lcoh.label = freqout.label; + lcoh.freq = cfg.foi; + lcoh.time = nanmean([freqout.time(t1);freqout.time(t2)],1); + lcoh.lag = (1:1:cfg.nlags)*lagwidth*cfg.foi; + lcoh.labelcmb = cfg.channelcmb; + if length(cfg.trialsets)==1 + lcoh.laggedcrsspctrm = shiftdim(lcoh.laggedcrsspctrm,1); + lcoh.powspctrm1 = shiftdim(lcoh.powspctrm1,1); + lcoh.powspctrm2 = shiftdim(lcoh.powspctrm2,1); + lcoh.dimord = 'chan_time'; + else + if all(cellfun(@length,cfg.trialsets)==1) + trials=cell2mat(cfg.trialsets); + if isfield(freqout,'trialinfo') + lcoh.trialinfo=freqout.trialinfo(trials,:); + end + else + if isfield(freqout,'trialinfo') + for counter=1:ntrialsets + lcoh.trialinfos{counter} = freqout.trialinfo(cfg.trialsets{counter},:); end + end + lcoh.trialsets = cfg.trialsets; end + lcoh.dimord = 'rep_chan_time'; + end + end end -return - diff --git a/external/fieldtrip/connectivity/ft_connectivity_mim.m b/external/fieldtrip/connectivity/ft_connectivity_mim.m new file mode 100644 index 00000000..369c3032 --- /dev/null +++ b/external/fieldtrip/connectivity/ft_connectivity_mim.m @@ -0,0 +1,71 @@ +function [m] = ft_connectivity_mim(input, varargin) + +% FT_CONNECTIVITY_MIM computes the multivariate interaction measure from a +% data-matrix containing the cross-spectral density. This implements the method +% described in Ewald et al., Estimating true brain connectivity from EEG/MEG data +% invariant to linear and static trasformations in sensor space. Neuroimage, 2012; +% 476:488. +% +% Use as +% [m] = hcp_connectivity_mim(input, ...) +% +% The input data should be an array organized as +% Channel x Channel x Frequency +% +% Additional optional input arguments come as key-value pairs: +% indices = 1xN vector with indices of the groups to which the channels belong, +% e.g. [1 1 2 2] for a 2-by-2 connectivity between planar MEG channels. +% +% The output m contains the Channel*Channel connectivity measure. +% +% See also FT_CONNECTIVITYANALYSIS + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +indices = ft_getopt(varargin, 'indices'); + +if isempty(indices) && isequal(size(input), [2 2]) + % simply assume two channels + indx1 = 1; + indx2 = 2; +else + % it should be a vector like [1 1 1 2 2 2] + indx1 = indices==1; + indx2 = indices==2; +end + +cs_aa_re = real(input(indx1,indx1)); +cs_bb_re = real(input(indx2,indx2)); +cs_ab_im = imag(input(indx1,indx2)); + +inv_cs_bb_re = pinv(cs_bb_re); +inv_cs_aa_re = pinv(cs_aa_re); +transp_cs_ab_im = transpose(cs_ab_im); +m = trace(inv_cs_aa_re*cs_ab_im*inv_cs_bb_re*transp_cs_ab_im); % try to speed up by dividing calculation in steps + +% taking the mldivide and mrdivide operators doesn't change the results, but speeds up by a factor of 4 over 1000 iterations (on LM Notebook) +% m = trace(cs_aa_re\cs_ab_im*inv_cs_bb_re*transp_cs_ab_im); + +% % % block_a=cs_aa_re\cs_ab_im; +% % % block_b=cs_bb_re\transpose(cs_ab_im); +% % % m = trace(block_a*block_b); + +% m = trace(cs_aa_re\cs_ab_im*(cs_bb_re\transpose(cs_ab_im))); diff --git a/external/fieldtrip/connectivity/ft_connectivity_mutualinformation.m b/external/fieldtrip/connectivity/ft_connectivity_mutualinformation.m index c7b5da56..3eb5256f 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_mutualinformation.m +++ b/external/fieldtrip/connectivity/ft_connectivity_mutualinformation.m @@ -1,27 +1,28 @@ function output = ft_connectivity_mutualinformation(input, varargin) -% FT_CONNECTIVITY_MUTUALINFORMATION computes mutual information using -% the information breakdown toolbox (ibtb), as described in Magri et al., -% BMC Neuroscience 2009, 1471-2202. The function is a helper function for -% FT_CONNECTIVITYANALYSIS. As a standalone function, it could be used as -% follows: +% FT_CONNECTIVITY_MUTUALINFORMATION computes mutual information using the information +% breakdown toolbox (ibtb), as described in Magri et al., BMC Neuroscience 2009, +% 1471-2202. % -% mi = ft_connectivity_mutualinformation(data, varargin) +% Use as +% mi = ft_connectivity_mutualinformation(data, ...) % -% The input data is a Nchan x Nobservations matrix. +% The input data should be a Nchan x Nobservations matrix. % -% Additional input arguments come as key-value pairs: +% Additional optional input arguments come as key-value pairs: % histmethod = The way that histograms are generated from the data. Possible values % are 'eqpop' (default), 'eqspace', 'ceqspace', 'gseqspace'. % See the help of the 'binr' function in the ibtb toolbox for more information. -% numbin = scalar value. The number of bins used to create the histograms needed for +% numbin = scalar value. The number of bins used to create the histograms needed for % the entropy computations % opts = structure that is passed on to the 'information' function in the ibtb % toolbox. See the help of that function for more information. % refindx = scalar value or 'all'. The channel that is used as 'reference channel'. -% refdata = 1xNobservations vector, as an alternative to the refindx. Refdata takes precedence over refindx +% refdata = 1xNobservations vector, as an alternative to the refindx. Refdata takes precedence over refindx % % The output contains the estimated mutual information between all channels and the reference channel(s). +% +% See also FT_CONNECTIVITYANALYSIS % Copyright (C) 2016 Donders Institute, Jan-Mathijs Schoffelen % @@ -43,29 +44,28 @@ % % $Id$ - + % check whether the required toolbox is available ft_hastoolbox('ibtb', 1); % set some options -histmethod = ft_getopt(varargin, 'histmethod', 'eqpop'); -numbin = ft_getopt(varargin, 'numbin', 10); -refindx = ft_getopt(varargin, 'refindx', 'all'); -refdata = ft_getopt(varargin, 'refdata', []); -lags = ft_getopt(varargin, 'lags', 0); % shift of data w.r.t. reference, in samples +histmethod = ft_getopt(varargin, 'histmethod', 'eqpop'); +numbin = ft_getopt(varargin, 'numbin', 10); +refindx = ft_getopt(varargin, 'refindx', 'all'); +refdata = ft_getopt(varargin, 'refdata', []); +lags = ft_getopt(varargin, 'lags', 0); % shift of data w.r.t. reference, in samples -% set some additional options that pertain to the algorithmic details of the -% mutual information computation -opts = ft_getopt(varargin, 'opts', []); -opts.nt = ft_getopt(opts, 'nt', []); -opts.method = ft_getopt(opts, 'method', 'dr'); -opts.bias = ft_getopt(opts, 'bias', 'pt'); +% set some additional options that pertain to the algorithmic details of the mutual information computation +opts = ft_getopt(varargin, 'opts', []); +opts.nt = ft_getopt(opts, 'nt', []); +opts.method = ft_getopt(opts, 'method', 'dr'); +opts.bias = ft_getopt(opts, 'bias', 'pt'); if ischar(refindx) && strcmp(refindx, 'all') refindx = (1:size(input,1))'; end -if numel(lags)>1 || lags~=0, +if numel(lags)>1 || lags~=0 if numel(refindx)>1, ft_error('with multiple lags, or with a lag~=0 only a single refindx is allowed'); end refdata = input(refindx,:); n = size(refdata,2); @@ -87,9 +87,7 @@ tmp = ft_connectivity_mutualinformation(input, 'refdata', tmpdata, 'opts', opts, 'histmethod', histmethod, 'numbin', numbin); output(:,k) = tmp(1:end-1); end - return; - - + return end if ~isempty(refdata) @@ -97,14 +95,12 @@ refindx = size(input,1); end - % check validity of refindx if length(refindx)~=numel(refindx) % could be channelcmb indexing ft_error('channelcmb indexing is not supported'); end - % get rid of nans in the input notsel = sum(~isfinite(input))>0; input = input(:,~notsel); @@ -117,7 +113,7 @@ % discretize signal1 signal1 = binr(signal1, nsmp, numbin, histmethod); - + for m = setdiff(1:size(input,1),refindx(k)) signal2 = input(m,:); diff --git a/external/fieldtrip/connectivity/ft_connectivity_pdc.m b/external/fieldtrip/connectivity/ft_connectivity_pdc.m index dc78c9f1..9122d280 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_pdc.m +++ b/external/fieldtrip/connectivity/ft_connectivity_pdc.m @@ -1,15 +1,21 @@ function [pdc, pdcvar, n] = ft_connectivity_pdc(input, varargin) -% FT_CONNECTIVITY_PDC computes partial directed coherence. -% +% FT_CONNECTIVITY_PDC computes partial directed coherence. This function implements +% the metrices described in Baccala et al., Biological Cybernetics 2001, 84(6), +% 463-74. and in Baccala et al., 15th Int.Conf.on DSP 2007, 163-66. +% +% The implemented algorithm has been tested against the implementation in the +% SIFT-toolbox. It yields numerically identical results to what is known there as +% 'nPDC' (for PDC) and 'GPDC' for generalized pdc. +% % Use as % [p, v, n] = ft_connectivity_pdc(h, key1, value1, ...) % -% Input arguments: -% H = spectral transfer matrix, Nrpt x Nchan x Nchan x Nfreq (x Ntime), -% Nrpt can be 1. +% The input argument H should be a spectral transfer matrix organized as +% Nrpt x Nchan x Nchan x Nfreq (x Ntime), +% where Nrpt can be 1. % -% additional options need to be specified as key-value pairs and are: +% Additional optional input arguments come as key-value pairs: % 'hasjack' = 0 (default) is a boolean specifying whether the input % contains leave-one-outs, required for correct variance % estimate @@ -33,14 +39,7 @@ % is assumed to contain the leave-one-out estimates of H, thus a more % reliable estimate of the relevant quantities. % -% This function implements the metrices described in: -% - Baccala et al., Biological Cybernetics 2001, 84(6), 463-74. -% - Baccala et al., 15th Int.Conf.on DSP 2007, 163-66. -% -% The implemented algorithm has been tested against the implementation in -% the SIFT-toolbox. It yields numerically identical results to what is -% known in the SIFT-toolbox as 'nPDC' (for PDC) and 'GPDC' for generalized -% pdc. +% See also FT_CONNECTIVITYANALYSIS % Copyright (C) 2009-2017, Jan-Mathijs Schoffelen % @@ -64,7 +63,7 @@ hasjack = ft_getopt(varargin, 'hasjack', 0); feedback = ft_getopt(varargin, 'feedback', 'none'); -invfun = ft_getopt(varargin, 'invfun', 'inv'); +invfun = ft_getopt(varargin, 'invfun', 'inv'); noisecov = ft_getopt(varargin, 'noisecov'); switch invfun @@ -110,11 +109,9 @@ ft_progress(j/n, 'computing metric for replicate %d from %d\n', j, n); invh = reshape(input(j,:,:,:,:), siz(2:end)); - den = sum(abs(invh).^2,1); tmppdc = abs(invh)./sqrt(repmat(den, [siz(2) 1 1 1 1])); - outsum = outsum + tmppdc; outssq = outssq + tmppdc.^2; end @@ -122,7 +119,7 @@ pdc = outsum./n; -if n>1, +if n>1 if hasjack bias = (n-1).^2; else diff --git a/external/fieldtrip/connectivity/ft_connectivity_powcorr_ortho.m b/external/fieldtrip/connectivity/ft_connectivity_powcorr_ortho.m index c41718a3..ae3ddba8 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_powcorr_ortho.m +++ b/external/fieldtrip/connectivity/ft_connectivity_powcorr_ortho.m @@ -1,22 +1,27 @@ function [c] = ft_connectivity_powcorr_ortho(mom, varargin) -% FT_CONNECTIVITY_POWCORR_ORTHO computes power correlation after removing -% the zero-lag contribution on a trial-by-trial basis, according to Hipp's -% Nature Neuroscience paper. +% FT_CONNECTIVITY_POWCORR_ORTHO computes power correlation after removing the +% zero-lag contribution on a trial-by-trial basis. This implements the method +% described in JF Hipp, DJ Hawellek, M Corbetta, M Siegel, AK Engel. Large-scale +% cortical correlation structure of spontaneous oscillatory activity. Nature +% neuroscience 15 (6), 884-890. % % Use as -% c = ft_connectivity_powcorr(mom) -% c = ft_connectivity_powcorr(mom, 'refindx', refindx) +% c = ft_connectivity_powcorr(mom, ...) % -% Where mom is a NchanxNrpt matrix containing the complex-valued amplitude -% and phase information at a given frequency, and the optional key refindx -% specifies the index/indices of the channels that serve as a reference -% channel. (Default is 'all'). +% The input argument mom should be a NchanxNrpt matrix containing the complex-valued +% amplitude and phase information at a given frequency, and the optional key refindx +% specifies the +% +% Additional optional input arguments come as key-value pairs: +% refindx = index/indices of the channels that serve as a reference channel (default is all) % % The output c is a NchanxNrefchan matrix that contain the power correlation % for all channels orthogonalised relative to the reference channel in the first -% Nrefchan columns, and the power correlation for the reference channels +% Nrefchan columns, and the power correlation for the reference channels % orthogonalised relative to the channels in the second Nrefchan columns. +% +% See also FT_CONNECTIVITYANALYSIS % Copyright (C) 2012 Jan-Mathijs Schoffelen % @@ -86,7 +91,7 @@ indx = refindx(k); ref = mom(indx,:); crefnorm = conj(ref./abs(ref)); - + % FIXME the following is probably not correct for ntap>1 pow2 = (abs(imag(ref(N,:).*cmomnorm)).^2)*tra; pow2 = standardise(log10(pow2), 2); @@ -98,8 +103,7 @@ pow2 = standardise(log10(pow2), 2); pow2 = repmat(pow2, [n 1]); c2 = mean(pow1.*pow2, 2); - + c(:,k) = (c1+c2)./2; %c(:,k+numel(refindx)) = c2; end - diff --git a/external/fieldtrip/connectivity/ft_connectivity_ppc.m b/external/fieldtrip/connectivity/ft_connectivity_ppc.m index b778132b..9a1c6af9 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_ppc.m +++ b/external/fieldtrip/connectivity/ft_connectivity_ppc.m @@ -1,41 +1,34 @@ function [c, v, n] = ft_connectivity_ppc(input, varargin) -% FT_CONNECTIVITY_PPC computes pairwise phase consistency or weighted -% pairwise phase consistency from a data-matrix containing a cross-spectral -% density. It implements the method described in: Vinck M, van Wingerden M, -% Womelsdorf T, Fries P, Pennartz CM. The pairwise phase consistency: a -% bias-free measure of rhythmic neuronal synchronization. Neuroimage. 2010 -% May 15;51(1):112-22 +% FT_CONNECTIVITY_PPC computes pairwise phase consistency or weighted pairwise phase +% consistency from a data-matrix containing a cross-spectral density. This implements +% the method described in Vinck M, van Wingerden M, Womelsdorf T, Fries P, Pennartz +% CM. The pairwise phase consistency: a bias-free measure of rhythmic neuronal +% synchronization. Neuroimage. 2010 May 15;51(1):112-22. % % Use as -% [c, v, n] = ft_connectivity_ppc(input, varargin) -% -% The input data input should be organized as: +% [c, v, n] = ft_connectivity_ppc(input, ...) % +% The input data input should be organized as: % Repetitions x Channel x Channel (x Frequency) (x Time) -% % or -% % Repetitions x Channelcombination (x Frequency) (x Time) -% -% The first dimension should contain repetitions and should not contain an -% average already. Also, it should not consist of leave one out averages. % -% Additional input arguments come as key-value pairs: +% The first dimension should contain repetitions and should not contain an average +% already. Also, it should not consist of leave-one-out averages. +% +% Additional optional input arguments come as key-value pairs: +% feedback = 'none', 'text', 'textbar' type of feedback showing progress of computation +% weighted = 1 (or true) or 0 (or false), we compute unweighted ppc or +% weighted ppc, the weighting is according to the magnitude of +% the cross-spectrum % -% feedback 'none', 'text', 'textbar' type of feedback showing progress of -% computation -% weighted 1 (or true) or 0 (or false), we compute unweighted ppc or -% weighted ppc, the weighting is according to the magnitude of -% the cross-spectrum +% The output c contains the ppc, v is a leave-one-out variance estimate which is only +% computed if dojack = 1,and n is the number of repetitions in the input data. % -% The output c contains the ppc, v is a leave-one-out variance estimate -% which is only computed if dojack = 1,and n is the number of repetitions -% in the input data. -% % See also FT_CONNECTIVITYANALYSIS -% Copyright (C) 2011, Martin Vinck +% Copyright (C) 2011, Martin Vinck % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -72,33 +65,33 @@ outssq = nansum(input.*conj(input)); outsumw = nansum(abs(input)); c = (outsum.*conj(outsum) - outssq)./(outsumw.*conj(outsumw) - outssq); % do the pairwise thing in a handy way - end + end c = reshape(c,siz(2:end)); % remove the first singular dimension else c = NaN(siz(2:end)); % for one observation, we should return NaNs - ft_warning('ft_connectivity_ppc:nTrials', 'computation ppc requires >1 trial, returning NaNs') + ft_warning('computation ppc requires >1 trial, returning NaNs') end [leave1outsum, leave1outssq] = deal(0); if dojack && n>2 % n needs to be larger than 2 to get a meaningful variance for k = 1:n % this code works with both formats of input, also if it is 5-D - s = outsum - input(k,:,:,:,:,:,:); % index up to 7-D, this also works for 5-D then. + s = outsum - input(k,:,:,:,:,:,:); % index up to 7-D, this also works for 5-D then. if ~weighted num = s.*conj(s) - (n-2); denom = (n-1)*(n-2); else - sq = outssq - input(k,:,:,:,:,:,:).*conj(input(k,:,:,:,:,:,:)); + sq = outssq - input(k,:,:,:,:,:,:).*conj(input(k,:,:,:,:,:,:)); sw = outsumw - abs(input(k,:,:,:,:,:,:)); num = s.*conj(s) - sq; denom = sw.*conj(sw) - sq; - end + end leave1outsum = leave1outsum + num./denom; - leave1outssq = leave1outssq + (num./denom).^2; - end - % compute the sem here + leave1outssq = leave1outssq + (num./denom).^2; + end + % compute the sem here v = (n-1).^2*(leave1outssq - (leave1outsum.^2)./n)./(n - 1); % 11.5 efron, sqrt and 1/n done in ft_connectivityanalysis - v = reshape(v,siz(2:end)); % remove the first singular dimension + v = reshape(v,siz(2:end)); % remove the first singular dimension elseif dojack && n<=2 v = NaN(siz(2:end)); else diff --git a/external/fieldtrip/connectivity/ft_connectivity_psi.m b/external/fieldtrip/connectivity/ft_connectivity_psi.m index 96765042..5d5918e4 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_psi.m +++ b/external/fieldtrip/connectivity/ft_connectivity_psi.m @@ -6,21 +6,17 @@ % in complex physical systems. Physical Review Letters, 2008; 100; 234101. % % Use as -% [c, v, n] = ft_connectivity_psi(input, varargin) -% -% The input data input should be organized as: +% [c, v, n] = ft_connectivity_psi(input, ...) % +% The input data input should be organized as % Repetitions x Channel x Channel (x Frequency) (x Time) -% % or -% % Repetitions x Channelcombination (x Frequency) (x Time) % % The first dimension should be singleton if the input already contains an -% average -% -% Additional input arguments come as key-value pairs: +% average. % +% Additional optional input arguments come as key-value pairs: % nbin = scalar, half-bandwidth parameter: the number of frequency bins % across which to integrate % hasjack = 0 or 1, specifying whether the repetitions represent @@ -28,8 +24,8 @@ % feedback = 'none', 'text', 'textbar' type of feedback showing progress of % computation % dimord = string, specifying how the input matrix should be interpreted -% powindx -% normalize +% powindx = +% normalize = % % The output p contains the phase slope index, v is a variance estimate % which only can be computed if the data contains leave-one-out samples, @@ -73,7 +69,7 @@ ft_error('input parameters should contain a dimord'); end -if (length(strfind(dimord, 'chan'))~=2 || ~isempty(strfind(dimord, 'pos'))>0) && ~isempty(powindx), +if (length(strfind(dimord, 'chan'))~=2 || contains(dimord, 'pos')>0) && ~isempty(powindx) %crossterms are not described with chan_chan_therest, but are linearly indexed siz = size(input); @@ -97,7 +93,7 @@ end ft_progress('close'); -elseif length(strfind(dimord, 'chan'))==2 || length(strfind(dimord, 'pos'))==2, +elseif length(strfind(dimord, 'chan'))==2 || length(strfind(dimord, 'pos'))==2 %crossterms are described by chan_chan_therest siz = size(input); @@ -130,7 +126,7 @@ n = siz(1); c = outsum./n; -if n>1, +if n>1 n = shiftdim(sum(~isnan(input),1),1); if hasjack bias = (n-1).^2; @@ -142,7 +138,8 @@ v = []; end -%--------------------------------------- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + function [y] = phaseslope(x, n, norm) m = size(x, 1); %total number of frequency bins diff --git a/external/fieldtrip/connectivity/ft_connectivity_wpli.m b/external/fieldtrip/connectivity/ft_connectivity_wpli.m index 7526546f..9ee28ee4 100644 --- a/external/fieldtrip/connectivity/ft_connectivity_wpli.m +++ b/external/fieldtrip/connectivity/ft_connectivity_wpli.m @@ -1,40 +1,35 @@ function [wpli, v, n] = ft_connectivity_wpli(input, varargin) -% FT_CONNECTIVITY_WPLI computes the weighted phase lag index from a -% data-matrix containing a cross-spectral density. It implements the method -% described in: Vinck M, Oostenveld R, van Wingerden M, Battaglia F, -% Pennartz CM. An improved index of phase-synchronization for -% electrophysiological data in the presence of volume-conduction, noise and -% sample-size bias. Neuroimage. 2011 Apr 15;55(4):1548-65. +% FT_CONNECTIVITY_WPLI computes the weighted phase lag index from a data matrix +% containing the cross-spectral density. This implements the method described in +% Vinck M, Oostenveld R, van Wingerden M, Battaglia F, Pennartz CM. An improved index +% of phase-synchronization for electrophysiological data in the presence of +% volume-conduction, noise and sample-size bias. Neuroimage. 2011 Apr +% 15;55(4):1548-65. % % Use as -% [wpi, v, n] = ft_connectivity_wpli(input, varargin) -% -% The input data input should be organized as: +% [wpi, v, n] = ft_connectivity_wpli(input, ...) % +% The input data input should be organized as: % Repetitions x Channel x Channel (x Frequency) (x Time) -% % or -% % Repetitions x Channelcombination (x Frequency) (x Time) -% +% % The first dimension should contain repetitions and should not contain an % average already. Also, it should not consist of leave one out averages. % -% Additional input arguments come as key-value pairs: -% -% dojack 1 or 0, compute a variance estimate, based on leave-one-out -% feedback 'none', 'text', 'textbar' type of feedback showing progress of -% computation -% debias 1 (or true) or 0 (or false), compute debiased wpli or not +% Additional optional input arguments come as key-value pairs: +% dojack = 1 or 0, compute a variance estimate, based on leave-one-out +% feedback = 'none', 'text', 'textbar' type of feedback showing progress of computation +% debias = 1 (or true) or 0 (or false), compute debiased wpli or not % % The output wpli contains the wpli, v is a leave-one-out variance estimate % which is only computed if dojack = 1,and n is the number of repetitions % in the input data. -% +% % See also FT_CONNECTIVITYANALYSIS -% Copyright (C) 2011, Martin Vinck +% Copyright (C) 2011, Martin Vinck % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -62,7 +57,7 @@ n = siz(1); ft_progress('init', feedback, 'computing metric...'); if n>1 - input = imag(input); % make everything imaginary + input = imag(input); % make everything imaginary outsum = nansum(input,1); % compute the sum; this is 1 x size(2:end) outsumW = nansum(abs(input),1); % normalization of the WPLI if debias @@ -70,7 +65,7 @@ wpli = (outsum.^2 - outssq)./(outsumW.^2 - outssq); % do the pairwise thing in a handy way else wpli = outsum./outsumW; % estimator of E(Im(X))/E(|Im(X)|) - end + end wpli = reshape(wpli,siz(2:end)); % remove the first singular dimension else wpli = NaN(siz(2:end)); % for one observation, we should return NaNs @@ -83,23 +78,23 @@ s = outsum - input(k,:,:,:,:,:,:); % works for any array up to 7-D sw = outsumW - abs(input(k,:,:,:,:,:,:)); if debias - sq = outssq - input(k,:,:,:,:,:,:).^2; + sq = outssq - input(k,:,:,:,:,:,:).^2; num = s.^2 - sq; denom = sw.^2 - sq; else num = s; % this is estimator of E(Im(X)) denom = sw; % estimator of E(|Im(X)|) - end + end tmp = num./denom; % avoids doing the division twice tmp(isnan(tmp)) = 0; % added for nan support leave1outsum = leave1outsum + tmp;% added this for nan support - leave1outssq = leave1outssq + tmp.^2; % added this for nan support - end - % compute the sem here + leave1outssq = leave1outssq + tmp.^2; % added this for nan support + end + % compute the sem here n = sum(~isnan(input),1); % this is the actual df when nans are found in the input matrix v = (n-1).^2.*(leave1outssq - (leave1outsum.^2)./n)./(n - 1); % 11.5 efron, sqrt and 1/n done in ft_connectivityanalysis - v = reshape(v,siz(2:end)); % remove the first singular dimension - n = reshape(n,siz(2:end)); + v = reshape(v,siz(2:end)); % remove the first singular dimension + n = reshape(n,siz(2:end)); elseif dojack && n<=2 v = NaN(siz(2:end)); else diff --git a/external/fieldtrip/connectivity/private/defaultId.m b/external/fieldtrip/connectivity/private/defaultId.m index f4e1ee99..e7569675 100644 --- a/external/fieldtrip/connectivity/private/defaultId.m +++ b/external/fieldtrip/connectivity/private/defaultId.m @@ -40,7 +40,9 @@ % remove the non-FieldTrip functions from the path, these should not be part of the default message identifier keep = true(size(stack)); -[v, p] = ft_version; +p = fileparts(mfilename('fullpath')); +% strip away '/utilities/private' where this function is located +p = p(1:end-18); for i=1:numel(stack) keep(i) = strncmp(p, stack(i).file, length(p)); end diff --git a/external/fieldtrip/connectivity/private/ft_getopt.m b/external/fieldtrip/connectivity/private/ft_getopt.m index 0d433115..5f2f387b 100644 --- a/external/fieldtrip/connectivity/private/ft_getopt.m +++ b/external/fieldtrip/connectivity/private/ft_getopt.m @@ -9,7 +9,7 @@ % s = structure or cell-array % key = string % default = any valid MATLAB data type (optional, default = []) -% emptymeaningful = boolean value (optional, default = 0) +% emptymeaningful = boolean value (optional, default = false) % % If the key is present as field in the structure, or as key-value pair in the % cell-array, the corresponding value will be returned. @@ -49,7 +49,7 @@ end if nargin < 4 - emptymeaningful = 0; + emptymeaningful = false; end if isa(opt, 'struct') || isa(opt, 'config') diff --git a/external/fieldtrip/connectivity/private/ft_getopt.mexw32 b/external/fieldtrip/connectivity/private/ft_getopt.mexw32 index 59a9ff8e..fd5aed41 100755 Binary files a/external/fieldtrip/connectivity/private/ft_getopt.mexw32 and b/external/fieldtrip/connectivity/private/ft_getopt.mexw32 differ diff --git a/external/fieldtrip/connectivity/private/ft_getopt.mexw64 b/external/fieldtrip/connectivity/private/ft_getopt.mexw64 index 343089e8..741ebeda 100755 Binary files a/external/fieldtrip/connectivity/private/ft_getopt.mexw64 and b/external/fieldtrip/connectivity/private/ft_getopt.mexw64 differ diff --git a/external/fieldtrip/connectivity/private/ft_notification.m b/external/fieldtrip/connectivity/private/ft_notification.m index dc9b7369..a74c4052 100644 --- a/external/fieldtrip/connectivity/private/ft_notification.m +++ b/external/fieldtrip/connectivity/private/ft_notification.m @@ -337,7 +337,7 @@ ft_default.notification.(level) = s; % the remainder is fully handled by the ERROR function if ~isempty(msgId) - error(msgId, varargin{:}); + error(state); else error(varargin{:}); end diff --git a/external/fieldtrip/connectivity/private/ft_platform_supports.m b/external/fieldtrip/connectivity/private/ft_platform_supports.m index c0676b62..34639722 100644 --- a/external/fieldtrip/connectivity/private/ft_platform_supports.m +++ b/external/fieldtrip/connectivity/private/ft_platform_supports.m @@ -41,6 +41,28 @@ % % See also FT_VERSION, VERSION, VER, VERLESSTHAN +% Copyright (C) 2006, Robert Oostenveld +% Copyright (C) 2010, Eelke Spaak +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + if ~ischar(what) error('first argument must be a string'); end @@ -48,6 +70,9 @@ switch what case 'matlabversion' tf = is_matlab() && matlabversion(varargin{:}); + + case 'octaveversion' + tf = is_octave() && octaveversion(varargin{:}); case 'exists-in-private-directory' tf = is_matlab(); @@ -206,11 +231,31 @@ end % function + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function [inInterval] = matlabversion(min, max) +function [inInterval] = octaveversion(min, max) +% the version does not change, making it persistent speeds up the subsequent calls +persistent curVer + +if nargin<2 + max = min; +end +if isempty(curVer) + curVer = OCTAVE_VERSION; +end + +% perform comparison with respect to version number +[major, minor] = parseMatlabVersion(curVer); +[minMajor, minMinor] = parseMatlabVersion(min); +[maxMajor, maxMinor] = parseMatlabVersion(max); + +inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); + +end % function + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MATLABVERSION checks if the current MATLAB version is within the interval % specified by min and max. % @@ -229,27 +274,8 @@ % etc. % % See also VERSION, VER, VERLESSTHAN - -% Copyright (C) 2006, Robert Oostenveld -% Copyright (C) 2010, Eelke Spaak -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [inInterval] = matlabversion(min, max) % the version does not change, making it persistent speeds up the subsequent calls persistent curVer @@ -311,8 +337,8 @@ minor = int8((ver - floor(ver)) * 10); else % ver is string (e.g. '7.10'), parse accordingly [major, rest] = strtok(ver, '.'); - major = str2num(major); - minor = str2num(strtok(rest, '.')); + major = str2double(major); + minor = str2double(strtok(rest, '.')); end end % function diff --git a/external/fieldtrip/connectivity/transfer2coeffs.m b/external/fieldtrip/connectivity/transfer2coeffs.m index d60f5716..4573929b 100644 --- a/external/fieldtrip/connectivity/transfer2coeffs.m +++ b/external/fieldtrip/connectivity/transfer2coeffs.m @@ -1,8 +1,8 @@ function A = transfer2coeffs(H, freq, labelcmb, maxlag) -% TRANSFER2COEFFS converts a spectral transfer matrix into the time domain -% equivalent multivariate autoregressive coefficients up to a specified -% lag, starting from lag 1. +% TRANSFER2COEFFS converts a spectral transfer matrix into the time domain equivalent +% multivariate autoregressive coefficients up to a specified lag, starting from lag +% 1. if nargin<3 labelcmb = []; @@ -30,7 +30,7 @@ end if freq(1)~=0 - ft_warning('FieldTrip:transfer2iis', 'when converting the transfer function to coefficients, the frequency axis should ideally start at 0, zero padding the spectral density'); + ft_warning('FieldTrip:transfer2iis', 'when converting the transfer function to coefficients, the frequency axis should ideally start at 0, zero padding the spectral density'); dfreq = mean(dfreq); npad = freq(1)./dfreq; @@ -65,10 +65,10 @@ else N2 = 2*(N-1)+1; end - + % preallocate memory for efficiency Harr = zeros(m,m,N2) + 1i.*zeros(m,m,N2); - + % the input cross-spectral density is assumed to be weighted with a % factor of 2 in all non-DC and Nyquist bins, therefore weight the % DC-bin with a factor of sqrt(2) to get a correct two-sided representation @@ -86,7 +86,7 @@ end % invert the transfer matrix to get the fourier representation of the - % coefficients, and add an identity matrix + % coefficients, and add an identity matrix I = eye(siz(1)); for k = 1:size(Harr,3) Harr(:,:,k) = I-inv(Harr(:,:,k)); @@ -115,10 +115,10 @@ else N2 = 2*(N-1)+1; end - + % preallocate memory for efficiency Harr = zeros(m,N2) + 1i.*zeros(m,N2); - + % the input cross-spectral density is assumed to be weighted with a % factor of 2 in all non-DC and Nyquist bins, therefore weight the % DC-bin with a factor of sqrt(2) to get a correct two-sided representation @@ -136,7 +136,7 @@ end % invert the transfer matrix to get the fourier representation of the - % coefficients, and add an identity matrix + % coefficients, and add an identity matrix % % this assumes Harr to be in the rows quadruplets of pairwise % decompositions, i.e. reshapable, without checking the labelcmb @@ -147,7 +147,7 @@ Htmp = repmat(I, [1 1 ncmb]) - inv2x2(Htmp); Harr(:,k) = Htmp(:); end - + % take the inverse fft to get the coefficients A = ifft(permute(Harr, [2 1]), 'symmetric'); A = A(2:end,:); @@ -156,5 +156,5 @@ if ~isempty(maxlag) A = A(:,1:maxlag); end - + end diff --git a/external/fieldtrip/external/stats/nanmean.mexw32 b/external/fieldtrip/external/stats/nanmean.mexw32 index faee9eef..1cb70b4d 100755 Binary files a/external/fieldtrip/external/stats/nanmean.mexw32 and b/external/fieldtrip/external/stats/nanmean.mexw32 differ diff --git a/external/fieldtrip/external/stats/nanmean.mexw64 b/external/fieldtrip/external/stats/nanmean.mexw64 index 9e3821e4..33f2cfd9 100755 Binary files a/external/fieldtrip/external/stats/nanmean.mexw64 and b/external/fieldtrip/external/stats/nanmean.mexw64 differ diff --git a/external/fieldtrip/external/stats/nanstd.mexw32 b/external/fieldtrip/external/stats/nanstd.mexw32 index 520c30a6..697d7bee 100755 Binary files a/external/fieldtrip/external/stats/nanstd.mexw32 and b/external/fieldtrip/external/stats/nanstd.mexw32 differ diff --git a/external/fieldtrip/external/stats/nanstd.mexw64 b/external/fieldtrip/external/stats/nanstd.mexw64 index 587745eb..bae0fce5 100755 Binary files a/external/fieldtrip/external/stats/nanstd.mexw64 and b/external/fieldtrip/external/stats/nanstd.mexw64 differ diff --git a/external/fieldtrip/external/stats/nansum.mexw32 b/external/fieldtrip/external/stats/nansum.mexw32 index b641da48..6e552a18 100755 Binary files a/external/fieldtrip/external/stats/nansum.mexw32 and b/external/fieldtrip/external/stats/nansum.mexw32 differ diff --git a/external/fieldtrip/external/stats/nansum.mexw64 b/external/fieldtrip/external/stats/nansum.mexw64 index d0e81802..01f84319 100755 Binary files a/external/fieldtrip/external/stats/nansum.mexw64 and b/external/fieldtrip/external/stats/nansum.mexw64 differ diff --git a/external/fieldtrip/external/stats/nanvar.mexw32 b/external/fieldtrip/external/stats/nanvar.mexw32 index aa21c857..013ef344 100755 Binary files a/external/fieldtrip/external/stats/nanvar.mexw32 and b/external/fieldtrip/external/stats/nanvar.mexw32 differ diff --git a/external/fieldtrip/external/stats/nanvar.mexw64 b/external/fieldtrip/external/stats/nanvar.mexw64 index c507f056..032e367e 100755 Binary files a/external/fieldtrip/external/stats/nanvar.mexw64 and b/external/fieldtrip/external/stats/nanvar.mexw64 differ diff --git a/external/fieldtrip/fieldtrip2fiff.m b/external/fieldtrip/fieldtrip2fiff.m index 37fc8c25..27d1b78e 100644 --- a/external/fieldtrip/fieldtrip2fiff.m +++ b/external/fieldtrip/fieldtrip2fiff.m @@ -308,21 +308,30 @@ function fieldtrip2fiff(filename, data) for i2 = 1:numel(ev_value) i_type = strcmp({event.type}, ev_type{i1}); i_value = strcmp(event_value, ev_value{i2}); - marker = i1 * 10 + i2; + % if events are numeric & there's only one event type keep original code: + if ~isempty(str2num(ev_value{i2})) && numel(ev_type) == 1 + marker = str2num(ev_value{i2}); + else + marker = i1 * 10 + i2; + end if any(i_type & i_value) eve(i_type & i_value, 1) = [event(i_type & i_value).sample]; - eve(i_type & i_value, 2) = marker; + eve(i_type & i_value, 3) = marker; end end end % report event coding -newev = unique(eve(:,2)); -fprintf('EVENTS have been coded as:\n') -for i = 1:numel(newev) - i_type = floor(newev(i)/10); - i_value = mod(newev(i), 10); - fprintf('type: %s, value %s -> % 3d\n', ev_type{i_type}, ev_value{i_value}, newev(i)) +newev = unique(eve(:,3)); +if all(cellfun(@isnumeric, {event.value})) && numel(ev_type) == 1 + fprintf('EVENT codes remain the same.\n') +else + fprintf('EVENTS have been coded as:\n') + for i = 1:numel(newev) + i_type = floor(newev(i)/10); + i_value = mod(newev(i), 10); + fprintf('type: %s, value %s -> % 3d\n', ev_type{i_type}, ev_value{i_value}, newev(i)) + end end diff --git a/external/fieldtrip/fieldtrip2spss.m b/external/fieldtrip/fieldtrip2spss.m index 28d1dc25..9f48282a 100644 --- a/external/fieldtrip/fieldtrip2spss.m +++ b/external/fieldtrip/fieldtrip2spss.m @@ -1,7 +1,7 @@ function fieldtrip2spss(filename, labels, data) % FIELDTRIP2SPSS compiles data and correpsonding labels into a textfile, -% suitable for import to SPSS. +% suitable for importing into SPSS or JASP (jasp-stats.org). % % Use as % fieldtrip2spss(filename, labels, data) @@ -24,7 +24,7 @@ function fieldtrip2spss(filename, labels, data) % After importing to SPSS, click the Missing cell in the Variable View % window and enter 9999 as the missing value definition. -% Copyright (C) 2011-2014, Arjen Stolk +% Copyright (C) 2011-2017, Arjen Stolk % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -44,14 +44,6 @@ function fieldtrip2spss(filename, labels, data) % % $Id$ -% these are used by the ft_preamble/ft_postamble function and scripts -ft_revision = '$Id$'; -ft_nargin = nargin; -ft_nargout = nargout; - -% do the general setup of the function -ft_defaults -ft_preamble callinfo % check whether data and labels have the same lengths if ~isequal(size(data,2),size(labels,2)) @@ -64,6 +56,3 @@ function fieldtrip2spss(filename, labels, data) txt(end) = ''; dlmwrite(filename, txt, ''); dlmwrite(filename, data, '-append', 'delimiter', '\t', 'precision', 4); - -% do the general cleanup and bookkeeping at the end of the function -ft_postamble callinfo diff --git a/external/fieldtrip/fileio/README b/external/fieldtrip/fileio/README index 8a5650c3..d0a0a3f6 100644 --- a/external/fieldtrip/fileio/README +++ b/external/fieldtrip/fileio/README @@ -1,19 +1,36 @@ This is the FieldTrip FILEIO module. It contains functions for reading EEG and MEG data from a large variety of files. Furthermore, -additional information such as sensor positions can be read from -file. +it llows reading additional information from files, such as sensor +positions, anatomical MRIs, triangulated meshes, etc. -For more information please visit http://www.ru.nl/neuroimaging/fieldtrip +For more information please visit http://www.fieldtriptoolbox.org/development/modules The source code for zlib is available at http://www.zlib.net -The FieldTrip software is free but copyrighted software, distributed -under the terms of the GNU General Public Licence as published by -the Free Software Foundation (either version 2, or at your option -any later version). See the file COPYING for more details. - -Copyright (C) 2008-2009, Donders Institute for Brain, Cognition and Behaviour, The Netherlands (DCCN, DCC, DCN) -Copyright (C) 2003-2008, F.C. Donders Centre for Cognitive Neuroimaging, Nijmegen, The Netherlands (FCDC) -Copyright (C) 2003-2005, Center for Sensory-Motor Interaction, Aalborg University, Denmark (SMI) +------------------------------------------------------------------------------- +Copyright (C) 2008-2017, Donders Institute for Brain, Cognition and Behaviour, Radboud University, The Netherlands (DCCN, DCC, DCN) +Copyright (C) 2012-2017, Max Planck Institute for Psycholinguistics, The Netherlands (MPI) +Copyright (C) 2008-2017, The Wellcome Trust Centre for Neuroimaging, University College London, UK (UCL) +Copyright (C) 2010-2013, Swammerdam Institute for Life Sciences, University of Amsterdam (SILS) +Copyright (C) 2008-2009, Centre for Cognitive Neuroimaging in Glasgow, United Kingdom (CCNi) +Copyright (C) 2009-2009, Netherlands Institute for Neuroscience (NIN) +Copyright (C) 2003-2008, F.C. Donders Centre, Radboud University Nijmegen, The Netherlands (FCDC) +Copyright (C) 2004-2007, Nijmegen Institute for Cognition and Information, The Netherlands (NICI) +Copyright (C) 2004-2005, Universitatsklinikum Hamburg-Eppendorf, Germany (UKE) +Copyright (C) 2003-2004, Center for Sensory Motor Interaction, University Aalborg, Denmark (SMI) Copyright (C) 1999-2003, Department of Medical Physics, Radboud University Nijmegen, The Netherlands (MBFYS) +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see =4 && ~isempty(strfind(filename,',rf')) +elseif length(filename)>=4 && contains(filename,',rf') type = '4d'; manufacturer = '4D/BTi'; content = ''; @@ -398,7 +430,7 @@ type = '4d_el_ascii'; manufacturer = '4D/BTi'; content = 'electrode positions'; - + % known EEProbe file types elseif filetype_check_extension(filename, '.cnt') && (filetype_check_header(filename, 'RIFF') || filetype_check_header(filename, 'RF64')) type = 'eep_cnt'; @@ -416,13 +448,13 @@ type = 'eep_rej'; manufacturer = 'EEProbe'; content = 'rejection marks'; - + % the yokogawa_mri has to be checked prior to asa_mri, because this one is more strict elseif filetype_check_extension(filename, '.mri') && filetype_check_header(filename, char(0)) % FIXME, this detection should possibly be improved type = 'yokogawa_mri'; manufacturer = 'Yokogawa'; content = 'anatomical MRI'; - + % known ASA file types elseif filetype_check_extension(filename, '.elc') type = 'asa_elc'; @@ -453,17 +485,17 @@ type = 'asa_mri'; manufacturer = 'ASA'; content = 'MRI image header'; -elseif filetype_check_extension(filename, '.iso') +elseif filetype_check_extension(filename, '.iso') && ~filetype_check_header(filename, char([0 0 0 100])) type = 'asa_iso'; manufacturer = 'ASA'; content = 'MRI image data'; - + % known BCI2000 file types elseif filetype_check_extension(filename, '.dat') && (filetype_check_header(filename, 'BCI2000') || filetype_check_header(filename, 'HeaderLen=')) type = 'bci2000_dat'; manufacturer = 'BCI2000'; content = 'continuous EEG'; - + % known Neuroscan file types elseif filetype_check_extension(filename, '.avg') && filetype_check_header(filename, 'Version 3.0') type = 'ns_avg'; @@ -477,7 +509,7 @@ type = 'ns_eeg'; manufacturer = 'Neuroscan'; content = 'epoched EEG'; - + elseif filetype_check_extension(filename, '.eeg') && filetype_check_header(filename, 'V3.0') type = 'neuroprax_eeg'; manufacturer = 'eldith GmbH'; @@ -486,7 +518,7 @@ type = 'neuroprax_mrk'; manufacturer = 'eldith GmbH'; content = 'EEG markers'; - + % known Analyze & SPM file types elseif filetype_check_extension(filename, '.hdr') type = 'analyze_hdr'; @@ -505,12 +537,12 @@ elseif filetype_check_extension(filename, '.nii') && filetype_check_header(filename, {[28 2 0 0], [0 0 2 28]}) % header starts with the number 540 type = 'nifti2'; content = 'MRI image data'; - + % known FSL file types elseif filetype_check_extension(filename, '.nii.gz') type = 'nifti_fsl'; content = 'MRI image data'; - + % known LORETA file types elseif filetype_check_extension(filename, '.lorb') type = 'loreta_lorb'; @@ -520,7 +552,7 @@ type = 'loreta_slor'; manufacturer = 'sLORETA'; content = 'source reconstruction'; - + % known AFNI file types elseif filetype_check_extension(filename, '.brik') || filetype_check_extension(filename, '.BRIK') type = 'afni_brik'; @@ -528,7 +560,7 @@ elseif filetype_check_extension(filename, '.head') || filetype_check_extension(filename, '.HEAD') type = 'afni_head'; content = 'MRI header data'; - + % known BrainVison file types elseif filetype_check_extension(filename, '.vhdr') type = 'brainvision_vhdr'; @@ -562,13 +594,13 @@ type = 'brainvision_marker'; manufacturer = 'BrainProducts'; content = 'rejection markers'; - + % known Polhemus file types elseif filetype_check_extension(filename, '.pos') type = 'polhemus_pos'; manufacturer = 'BrainProducts/CTF/Polhemus?'; % actually I don't know whose software it is content = 'electrode positions'; - + % known Blackrock Microsystems file types elseif strncmp(x,'.ns',3) && (filetype_check_header(filename, 'NEURALCD') || filetype_check_header(filename, 'NEURALSG')) type = 'blackrock_nsx'; @@ -578,7 +610,7 @@ type = 'blackrock_nev'; manufacturer = 'Blackrock Microsystems'; contenct = 'extracellular electrode spike information'; - + % known Neuralynx file types elseif filetype_check_extension(filename, '.nev') || filetype_check_extension(filename, '.Nev') type = 'neuralynx_nev'; @@ -620,51 +652,51 @@ type = 'neuralynx_dma'; manufacturer = 'Neuralynx'; content = 'raw aplifier data directly from DMA'; -elseif isdir(filename) && (any(filetype_check_extension({ls.name}, '.nev')) || any(filetype_check_extension({ls.name}, '.Nev'))) +elseif isfolder(filename) && (any(filetype_check_extension({ls.name}, '.nev')) || any(filetype_check_extension({ls.name}, '.Nev'))) % a regular Neuralynx dataset directory that contains an event file type = 'neuralynx_ds'; manufacturer = 'Neuralynx'; content = 'dataset'; -elseif isdir(filename) && most(filetype_check_extension({ls.name}, '.ncs')) +elseif isfolder(filename) && most(filetype_check_extension({ls.name}, '.ncs')) % a directory containing continuously sampled channels in Neuralynx format type = 'neuralynx_ds'; manufacturer = 'Neuralynx'; content = 'continuously sampled channels'; -elseif isdir(filename) && most(filetype_check_extension({ls.name}, '.nse')) +elseif isfolder(filename) && most(filetype_check_extension({ls.name}, '.nse')) % a directory containing spike waveforms in Neuralynx format type = 'neuralynx_ds'; manufacturer = 'Neuralynx'; content = 'spike waveforms'; -elseif isdir(filename) && most(filetype_check_extension({ls.name}, '.nte')) +elseif isfolder(filename) && most(filetype_check_extension({ls.name}, '.nte')) % a directory containing spike timestamps in Neuralynx format type = 'neuralynx_ds'; manufacturer = 'Neuralynx'; content = 'spike timestamps'; -elseif isdir(filename) && most(filetype_check_extension({ls.name}, '.ntt')) +elseif isfolder(filename) && most(filetype_check_extension({ls.name}, '.ntt')) % a directory containing tetrode recordings in Neuralynx format type = 'neuralynx_ds'; manufacturer = 'Neuralynx'; content = 'tetrode recordings '; - -elseif isdir(p) && exist(fullfile(p, 'header'), 'file') && exist(fullfile(p, 'samples'), 'file') && exist(fullfile(p, 'events'), 'file') + +elseif isfolder(p) && exist(fullfile(p, 'header'), 'file') && exist(fullfile(p, 'samples'), 'file') && exist(fullfile(p, 'events'), 'file') type = 'fcdc_buffer_offline'; manufacturer = 'Donders Centre for Cognitive Neuroimaging'; content = 'FieldTrip buffer offline dataset'; - -elseif isdir(filename) && exist(fullfile(filename, 'info.xml'), 'file') && exist(fullfile(filename, 'signal1.bin'), 'file') + +elseif isfolder(filename) && exist(fullfile(filename, 'info.xml'), 'file') && exist(fullfile(filename, 'signal1.bin'), 'file') % this is an OS X package directory representing a complete EEG dataset % it contains a Content file, multiple xml files and one or more signalN.bin files type = 'egi_mff'; manufacturer = 'Electrical Geodesics Incorporated'; content = 'raw EEG data'; -elseif ~isdir(filename) && isdir(p) && exist(fullfile(p, 'info.xml'), 'file') && exist(fullfile(p, 'signal1.bin'), 'file') +elseif ~isfolder(filename) && isfolder(p) && exist(fullfile(p, 'info.xml'), 'file') && exist(fullfile(p, 'signal1.bin'), 'file') % the file that the user specified is one of the files in an mff package directory type = 'egi_mff'; manufacturer = 'Electrical Geodesics Incorporated'; content = 'raw EEG data'; - + % these are formally not Neuralynx file formats, but at the FCDC we use them together with Neuralynx -elseif isdir(filename) && filetype_check_neuralynx_cds(filename) +elseif isfolder(filename) && filetype_check_neuralynx_cds(filename) % a downsampled Neuralynx DMA file can be split into three separate lfp/mua/spike directories % treat them as one combined dataset type = 'neuralynx_cds'; @@ -686,17 +718,17 @@ type = 'neuralynx_bin'; manufacturer = 'Donders Centre for Cognitive Neuroimaging'; content = 'single channel continuous data'; -elseif isdir(filename) && any(filetype_check_extension({ls.name}, '.ttl')) && any(filetype_check_extension({ls.name}, '.tsl')) && any(filetype_check_extension({ls.name}, '.tsh')) +elseif isfolder(filename) && any(filetype_check_extension({ls.name}, '.ttl')) && any(filetype_check_extension({ls.name}, '.tsl')) && any(filetype_check_extension({ls.name}, '.tsh')) % a directory containing the split channels from a DMA logfile type = 'neuralynx_sdma'; manufacturer = 'Donders Centre for Cognitive Neuroimaging'; content = 'split DMA log file'; -elseif isdir(filename) && filetype_check_extension(filename, '.sdma') +elseif isfolder(filename) && filetype_check_extension(filename, '.sdma') % a directory containing the split channels from a DMA logfile type = 'neuralynx_sdma'; manufacturer = 'Donders Centre for Cognitive Neuroimaging'; content = 'split DMA log file'; - + % known Plexon file types elseif filetype_check_extension(filename, '.nex') && filetype_check_header(filename, 'NEX1') type = 'plexon_nex'; @@ -709,18 +741,18 @@ elseif filetype_check_extension(filename, '.ddt') type = 'plexon_ddt'; manufacturer = 'Plexon'; -elseif isdir(filename) && most(filetype_check_extension({ls.name}, '.nex')) && most(filetype_check_header({ls.name}, 'NEX1')) +elseif isfolder(filename) && most(filetype_check_extension({ls.name}, '.nex')) && most(filetype_check_header({ls.name}, 'NEX1')) % a directory containing multiple plexon NEX files type = 'plexon_ds'; manufacturer = 'Plexon'; content = 'electrophysiological data'; - + % known Cambridge Electronic Design file types elseif filetype_check_extension(filename, '.smr') type = 'ced_son'; manufacturer = 'Cambridge Electronic Design'; content = 'Spike2 SON filing system'; - + % known BESA file types elseif filetype_check_extension(filename, '.avr') && strcmp(type, 'unknown') type = 'besa_avr'; % FIXME, can also be EEProbe average EEG @@ -782,23 +814,23 @@ type = 'brainvoyager_srf'; manufacturer = 'BrainVoyager'; % see http://support.brainvoyager.com/installation-introduction/23-file-formats/375-users-guide-23-the-format-of-srf-files.html content = 'surface'; - + % known Dataq file formats elseif filetype_check_extension(upper(filename), '.WDQ') type = 'dataq_wdq'; manufacturer = 'dataq instruments'; content = 'electrophysiological data'; - + % old files from Pascal Fries' PhD research at the MPI elseif filetype_check_extension(filename, '.dap') && filetype_check_header(filename, char(1)) type = 'mpi_dap'; manufacturer = 'MPI Frankfurt'; content = 'electrophysiological data'; -elseif isdir(filename) && ~isempty(cell2mat(regexp({ls.name}, '.dap$'))) +elseif isfolder(filename) && ~isempty(cell2mat(regexp({ls.name}, '.dap$'))) type = 'mpi_ds'; manufacturer = 'MPI Frankfurt'; content = 'electrophysiological data'; - + % Frankfurt SPASS format, which uses the Labview Datalog (DTLG) format elseif filetype_check_extension(filename, '.ana') && filetype_check_header(filename, 'DTLG') type = 'spass_ana'; @@ -820,7 +852,7 @@ type = 'spass_bhv'; manufacturer = 'MPI Frankfurt'; content = 'electrophysiological data'; - + % known Chieti ITAB file types elseif filetype_check_extension(filename, '.raw') && (filetype_check_header(filename, 'FORMAT: ATB-BIOMAGDATA') || filetype_check_header(filename, '[HeaderType]')) type = 'itab_raw'; @@ -834,13 +866,13 @@ type = 'itab_asc'; manufacturer = 'Chieti ITAB'; content = 'headshape digitization file'; - + % known Nexstim file types elseif filetype_check_extension(filename, '.nxe') type = 'nexstim_nxe'; manufacturer = 'Nexstim'; content = 'electrophysiological data'; - + % known Tucker-Davis-Technology file types elseif filetype_check_extension(filename, '.tbk') type = 'tdt_tbk'; @@ -858,7 +890,7 @@ type = 'tdt_tev'; manufacturer = 'Tucker-Davis-Technology'; content = 'electrophysiological data'; - + % raw audio and video data from https://github.com/andreyzhd/VideoMEG % the extension *.aud/*.vid is used at NatMEG and *.audio.dat/*.video.dat seems to be used in Helsinki elseif (filetype_check_extension(filename, '.aud') || filetype_check_extension(filename, '.audio.dat')) && filetype_check_header(filename, 'ELEKTA_AUDIO_FILE') @@ -921,12 +953,17 @@ type = 'curry_dig'; manufacturer = 'Curry'; content = 'digitizer file'; - + +elseif filetype_check_extension(filename, '.txt') && filetype_check_header(filename, '#Study') + type = 'imotions_txt'; + manufacturer = 'iMotions'; + content = 'various biosignals'; + elseif filetype_check_extension(filename, '.txt') && filetype_check_header(filename, '##') type = 'smi_txt'; manufacturer = 'SensoMotoric Instruments (SMI)'; content = 'eyetracker data'; - + % known SR Research eyelink file formats elseif filetype_check_extension(filename, '.asc') && filetype_check_header(filename, '**') type = 'eyelink_asc'; @@ -936,12 +973,12 @@ type = 'eyelink_edf'; manufacturer = 'SR Research'; content = 'eyetracker data (binary)'; - + elseif filetype_check_extension(filename, '.tsv') && (filetype_check_header(filename, 'Data Properties:') || filetype_check_header(filename, 'System Properties:')) type = 'tobii_tsv'; manufacturer = 'Tobii'; content = 'eyetracker data (ascii)'; - + % known Curry V2 file types elseif filetype_check_extension(filename, '.sp0') || filetype_check_extension(filename, '.sp1') || filetype_check_extension(filename, '.sp2') || filetype_check_extension(filename, '.sp3') || filetype_check_extension(filename, '.sp4') || filetype_check_extension(filename, '.sp5') || filetype_check_extension(filename, '.sp6') || filetype_check_extension(filename, '.sp7') || filetype_check_extension(filename, '.sp8') || filetype_check_extension(filename, '.sp9') type = 'curry_sp'; @@ -959,7 +996,7 @@ type = 'curry_res'; manufacturer = 'Curry'; content = 'functional localization file'; - + % known MBFYS file types elseif filetype_check_extension(filename, '.tri') type = 'mbfys_tri'; @@ -969,7 +1006,7 @@ type = 'mbfys_ama'; manufacturer = 'MBFYS'; content = 'BEM volume conduction model'; - + % Electrical Geodesics Incorporated formats % the egi_mff format is checked earlier elseif (filetype_check_extension(filename, '.egis') || filetype_check_extension(filename, '.ave') || filetype_check_extension(filename, '.gave') || filetype_check_extension(filename, '.raw')) && (filetype_check_header(filename, [char(1) char(2) char(3) char(4) char(255) char(255)]) || filetype_check_header(filename, [char(3) char(4) char(1) char(2) char(255) char(255)])) @@ -986,7 +1023,7 @@ type = 'egi_sbin'; manufacturer = 'Electrical Geodesics Incorporated'; content = 'averaged EEG data'; - + % FreeSurfer file formats, see also http://www.grahamwideman.com/gw/brain/fs/surfacefileformats.htm elseif filetype_check_extension(filename, '.mgz') type = 'freesurfer_mgz'; @@ -1021,7 +1058,7 @@ type = 'freesurfer_annot'; manufacturer = 'FreeSurfer'; content = 'parcellation annotation'; - + elseif filetype_check_extension(filename, '.txt') && numel(strfind(filename,'_nrs_')) == 1 % This may be improved by looking into the file, rather than assuming the % filename has "_nrs_" somewhere. Also, distinction by the different file @@ -1039,13 +1076,18 @@ type = 'homer_sd'; manufacturer = 'Homer'; content = 'source detector information'; - + % known Artinis file format -elseif filetype_check_extension(filename, '.oxy3') - type = 'oxy3'; +elseif filetype_check_extension(filename, '.oxy3') + type = 'artinis_oxy3'; manufacturer = 'Artinis Medical Systems'; content = '(f)NIRS data'; +elseif isequal([f x], 'optodetemplates.xml') + type = 'artinis_xml'; + manufacturer = 'Artinis Medical Systems'; + content = '(f)NIRS optode layout'; + % known TETGEN file types, see http://tetgen.berlios.de/fformats.html elseif any(filetype_check_extension(filename, {'.node' '.poly' '.smesh' '.ele' '.face' '.edge' '.vol' '.var' '.neigh'})) && exist(fullfile(p, [f '.node']), 'file') && filetype_check_ascii(fullfile(p, [f '.node']), 100) && exist(fullfile(p, [f '.poly']), 'file') type = 'tetgen_poly'; @@ -1083,7 +1125,7 @@ type = 'tetgen_node'; manufacturer = 'TetGen, see http://tetgen.berlios.de'; content = 'geometrical data desribed with only nodes'; - + % some BrainSuite file formats, see http://brainsuite.bmap.ucla.edu/ elseif filetype_check_extension(filename, '.dfs') && filetype_check_header(filename, 'DFS_LE v2.0') type = 'brainsuite_dfs'; @@ -1097,8 +1139,8 @@ type = 'loni_dfc'; manufacturer = 'LONI'; % it is used in BrainSuite content = 'curvature information'; - - % some BrainVISA file formats, see http://brainvisa.info + + % some BrainVISA file formats, see http://brainvisa.info elseif filetype_check_extension(filename, '.mesh') && (filetype_check_header(filename, 'ascii') || filetype_check_header(filename, 'binarABCD') || filetype_check_header(filename, 'binarDCBA')) % http://brainvisa.info/doc/documents-4.4/formats/mesh.pdf type = 'brainvisa_mesh'; manufacturer = 'BrainVISA'; @@ -1107,7 +1149,7 @@ type = 'brainvisa_minf'; manufacturer = 'BrainVISA'; content = 'annotation/metadata'; - + % some other known file types elseif filetype_check_extension(filename, '.hdf5') type = 'gtec_hdf5'; @@ -1163,6 +1205,10 @@ type = 'ced_spike6mat'; manufacturer = 'Cambridge Electronic Design Limited'; content = 'electrophysiological data'; +elseif filetype_check_extension(filename, '.mat') && filetype_check_header(filename, 'MATLAB') && filetype_check_neuroomega_mat(filename) + type = 'neuroomega_mat'; + manufacturer = 'Alpha Omega'; + content = 'electrophysiological data'; elseif filetype_check_extension(filename, '.mat') && filetype_check_header(filename, 'MATLAB') type = 'matlab'; manufacturer = 'MATLAB'; @@ -1171,6 +1217,10 @@ type = 'riff_wave'; manufacturer = 'Microsoft'; content = 'audio'; +elseif filetype_check_extension(filename, '.m4a') + type = 'audio_m4a'; + manufacturer = 'Apple'; + content = 'audio'; elseif filetype_check_extension(filename, '.txt') && filetype_check_header(filename, 'Site') type = 'easycap_txt'; manufacturer = 'Easycap'; @@ -1208,22 +1258,22 @@ elseif filetype_check_extension(filename, '.spec') && (filetype_check_header(filename, ' 1 @@ -677,6 +700,10 @@ % pass the header along to speed it up, it will be read on the fly in case it is empty dat = read_mff_data(filename, 'sample', begsample, endsample, chanindx, hdr); + case {'egi_mff_v3' 'egi_mff'} % this is the default + ft_hastoolbox('mffmatlabio', 1); + dat = mff_fileio_read_data(filename, 'header', hdr, 'begtrial', begtrial, 'endtrial', endtrial, 'chanindx', chanindx); + case 'edf' % this reader is largely similar to the one for bdf % it uses a mex file for reading the 16 bit data @@ -1082,6 +1109,9 @@ end dat = dat(chanindx,begsample:endsample); + case 'nihonkohden_eeg' + dat = read_brainstorm_data(filename, hdr, begsample, endsample, chanindx); + case 'ns_avg' % NeuroScan average data orig = read_ns_avg(filename); @@ -1119,6 +1149,22 @@ dat = dat(:,chanindx,:); % select channels dimord = 'trials_chans_samples'; % selection using begsample and endsample will be done later + case 'neuromag_maxfilterlog' + log = hdr.orig; + dat = [ + log.t(:)' + log.e(:)' + log.g(:)' + log.v(:)' + log.r(:)' + log.d(:)' + ]; + for i=1:length(log.hpi) + dat = cat(1, dat, log.hpi{i}); + end + dat = dat(chanindx, begsample:endsample); + dimord = 'chans_samples'; + case {'neuromag_fif' 'neuromag_mne'} % check that the required low-level toolbox is available ft_hastoolbox('mne', 1); @@ -1170,6 +1216,16 @@ endsample = endsample - (begepoch-1)*hdr.nSamples; % correct for the number of bytes that were skipped dat = dat(:, begsample:endsample); + case 'neuroomega_mat' + % These are MATLAB *.mat files created by the software 'Map File + % Converter' from the original .mpx files recorded by NeuroOmega + dat=zeros(hdr.nChans,endsample - begsample + 1); + for i=1:hdr.nChans + v=double(hdr.orig.(hdr.label{i})); + v=v*hdr.orig.(char(strcat(hdr.label{i},'_BitResolution'))); + dat(i,:)=v(begsample:endsample); %channels sometimes have small differences in samples + end + case {'neurosim_ds' 'neurosim_signals'} [hdr, dat] = read_neurosim_signals(filename); if endsample>size(dat,2) @@ -1217,7 +1273,8 @@ tmp = np_readdata(filename, hdr.orig, begsample - 1, endsample - begsample + 1, 'samples'); dat = tmp.data(:,chanindx)'; - case 'oxy3' + case 'artinis_oxy3' + ft_hastoolbox('artinis', 1); dat = read_artinis_oxy3(filename, hdr, begsample, endsample, chanindx); case 'plexon_ds' @@ -1332,7 +1389,12 @@ case 'read_nex_data' % this is an alternative reader for nex files dat = read_nex_data(filename, hdr, begsample, endsample, chanindx); - case 'riff_wave' + case {'ricoh_ave', 'ricoh_con'} + % use the Ricoh MEG Reader toolbox for the file reading + ft_hastoolbox('ricoh_meg_reader', 1); + dat = read_ricoh_data(filename, hdr, begsample, endsample, chanindx); + + case {'riff_wave', 'audio_m4a'} dat = audioread(filename, [begsample endsample])'; dat = dat(chanindx,:); @@ -1355,6 +1417,9 @@ case 'videomeg_vid' dat = read_videomeg_vid(filename, hdr, begsample, endsample); dat = dat(chanindx,:); + + case 'video' + dat = read_video(filename, hdr, begsample, endsample, chanindx); case {'yokogawa_ave', 'yokogawa_con', 'yokogawa_raw'} % the data can be read with three toolboxes: Yokogawa MEG Reader, Maryland sqdread, @@ -1372,6 +1437,21 @@ dat = read_yokogawa_data(filename, hdr, begsample, endsample, chanindx); end + case 'blackrock_nsx' + % use the NPMK toolbox for the file reading + ft_hastoolbox('NPMK', 1); + + % ensure that the filename contains a full path specification, + % otherwise the low-level function fails + [p,f,e] = fileparts(filename); + if ~isempty(p) + % this is OK + elseif isempty(p) + filename = which(filename); + end + orig = openNSx(filename, 'duration', [begsample endsample], 'channels', chanindx); + dat = double(orig.Data); + otherwise % attempt to run dataformat as a function % in case using an external read function was desired, this is where it is executed diff --git a/external/fieldtrip/fileio/ft_read_event.m b/external/fieldtrip/fileio/ft_read_event.m index 83946073..6b18b753 100644 --- a/external/fieldtrip/fileio/ft_read_event.m +++ b/external/fieldtrip/fileio/ft_read_event.m @@ -16,8 +16,8 @@ % 'detectflank' string, can be 'bit', 'up', 'down', 'both', 'peak', 'trough' or 'auto' (default is system specific) % 'chanindx' list with channel indices in case of different sampling frequencies (only for EDF) % 'trigshift' integer, number of samples to shift from flank to detect trigger value (default = 0) -% 'trigindx' list with channel numbers for the trigger detection, only for Yokogawa (default is automatic) -% 'triglabel' list of channel labels for the trigger detection (default is all ADC* channels for Artinis oxy3-files) +% 'trigindx' list with channel numbers for the trigger detection, only for Yokogawa & Ricoh (default is automatic) +% 'triglabel' list of channel labels for the trigger detection (default is all ADC* channels for Artinis *.oxy3 files % 'threshold' threshold for analog trigger channels (default is system specific) % 'blocking' wait for the selected number of events (default = 'no') % 'timeout' amount of time in seconds to wait when blocking (default = 5) @@ -143,7 +143,7 @@ hdr = ft_getopt(varargin, 'header'); detectflank = ft_getopt(varargin, 'detectflank', 'up', true); % note that emptymeaningful=true trigshift = ft_getopt(varargin, 'trigshift'); % default is assigned in subfunction -trigindx = ft_getopt(varargin, 'trigindx'); % this allows to override the automatic trigger channel detection (e.g., useful for Yokogawa) +trigindx = ft_getopt(varargin, 'trigindx'); % this allows to override the automatic trigger channel detection (e.g., useful for Yokogawa & Ricoh) headerformat = ft_getopt(varargin, 'headerformat'); dataformat = ft_getopt(varargin, 'dataformat'); threshold = ft_getopt(varargin, 'threshold'); % this is used for analog channels @@ -427,9 +427,10 @@ case 'AnyWave' event = read_ah5_markers(hdr, filename); + case 'brainvision_vmrk' fid=fopen(filename,'rt'); - if fid==-1, + if fid==-1 ft_error('cannot open BrainVision marker file') end line = []; @@ -592,7 +593,7 @@ case 'dataq_wdq' if isempty(hdr) - hdr = ft_read_header(filename, 'headerformat', 'dataq_wdq'); + hdr = ft_read_header(filename, 'headerformat', 'dataq_wdq'); end trigger = read_wdq_data(filename, hdr.orig, 'lowbits'); [ix, iy] = find(trigger>1); %it seems as if the value of 1 is meaningless @@ -810,13 +811,18 @@ end end - case {'egi_mff_v1' 'egi_mff'} % this is currently the default + case 'egi_mff_v1' % The following represents the code that was written by Ingrid, Robert % and Giovanni to get started with the EGI mff dataset format. It might % not support all details of the file formats. + % % An alternative implementation has been provided by EGI, this is % released as fieldtrip/external/egi_mff and referred further down in % this function as 'egi_mff_v2'. + % + % An more recent implementation has been provided by EGI and Arno Delorme, this + % is released as https://github.com/arnodelorme/mffmatlabio and referred further + % down in this function as 'egi_mff_v3'. if isempty(hdr) % use the corresponding code to read the header @@ -974,6 +980,10 @@ event = rmfield(event, fn{i}); end + case {'egi_mff_v3' 'egi_mff'} % this is the default + ft_hastoolbox('mffmatlabio', 1); + event = mff_fileio_read_event(filename); + case 'smi_txt' if isempty(hdr) hdr = ft_read_header(filename); @@ -1082,7 +1092,7 @@ end case 'fcdc_buffer_offline' - if isdir(filename) + if isfolder(filename) path = filename; else [path, file, ext] = fileparts(filename); @@ -1315,7 +1325,7 @@ hdr = ft_read_header(filename); end % determine the DAP files that compromise this dataset - if isdir(filename) + if isfolder(filename) ls = dir(filename); dapfile = {}; for i=1:length(ls) @@ -1532,7 +1542,17 @@ end elseif isepoched - ft_error('Support for epoched *.fif data is not yet implemented.') + begsample = cumsum([1 repmat(hdr.nSamples, hdr.nTrials-1, 1)']); + events_id = split(split(hdr.orig.epochs.event_id, ';'), ':'); + events_label = cell2mat(events_id(:, 1)); + events_code = str2num(cell2mat(events_id(:, 2))); + for i=1:hdr.nTrials + event(end+1).type = 'trial'; + event(end ).sample = begsample(i); + event(end ).value = events_label(events_code == hdr.orig.epochs.events(i, 3), :); + event(end ).offset = -hdr.nSamplesPre; + event(end ).duration = hdr.nSamples; + end end % check whether the *.fif file is accompanied by an *.eve file @@ -1717,6 +1737,43 @@ ft_warning('FieldTrip:ft_read_event:unsupported_event_format', 'reading of events for the netmeg format is not yet supported'); event = []; + case 'neuroomega_mat' + + hdr = ft_read_header(filename, 'headerformat', eventformat, 'chantype', 'chaninfo'); + + fields_orig=who(hdr.orig); %getting digital event channels + fields_orig=fields_orig(startsWith(fields_orig,'CDIG_IN')); %compat/matlablt2016b/startsWidth.m + + rx=regexp(fields_orig,'^CDIG_IN_{1}(\d+)[a-zA-Z_]*','tokens'); + dig_channels=unique(cellfun(@(x) str2num(x{1}), [rx{:}])); + + event.type=[]; event.sample=[]; event.value=[]; + if ~ismember(detectflank,{'up','down','both'}) + ft_error('incorrect specification of ''detectflank'''); + end + if ismember(detectflank,{'up','both'}) + for i=1:length(dig_channels) + channel = ['CDIG_IN_' num2str(dig_channels(i)) '_Up']; + data = hdr.orig.(channel); + for j=1:length(hdr.orig.(channel)) + event(end+1).type = channel; + event(end ).value = dig_channels(i); + event(end ).sample = data(j); + end + end + end + if ismember(detectflank,{'down','both'}) + for i=1:length(dig_channels) + channel = ['CDIG_IN_' num2str(dig_channels(i)) '_Down']; + data = hdr.orig.(channel); + for j=1:length(hdr.orig.(channel)) + event(end+1).type = channel; + event(end ).value = dig_channels(i); + event(end ).sample = data(j); + end + end + end + case 'neuroshare' % NOTE: still under development % check that the required neuroshare toolbox is available ft_hastoolbox('neuroshare', 1); @@ -1845,6 +1902,10 @@ event(end ).value = value; % assign the trigger value just _before_ going down end + case 'nihonkohden_eeg' + ft_hastoolbox('brainstorm', 1); + event = read_brainstorm_event(filename); + case 'nimh_cortex' if isempty(hdr) hdr = ft_read_header(filename); @@ -1954,6 +2015,20 @@ case 'plexon_nex' event = read_nex_event(filename); + case {'ricoh_ave', 'ricoh_con'} + % use the Ricoh MEG Reader toolbox for the file reading + ft_hastoolbox('ricoh_meg_reader', 1); + % the user should be able to specify the analog threshold, but the code falls back to '1.6' as default + % the user should be able to specify the trigger channels + % the user should be able to specify the flank, but the code falls back to 'up' as default + if isempty(detectflank) + detectflank = 'up'; + end + if isempty(threshold) + threshold = 1.6; + end + event = read_ricoh_event(filename, 'detectflank', detectflank, 'trigindx', trigindx, 'threshold', threshold); + case 'tmsi_poly5' if isempty(hdr) hdr = ft_read_header(filename); @@ -1970,24 +2045,26 @@ if ~ft_hastoolbox('yokogawa', 0); ft_hastoolbox('yokogawa_meg_reader', 1); end - % the user should be able to specify the analog threshold + % the user should be able to specify the analog threshold, but the code falls back to '1.6' as default % the user should be able to specify the trigger channels - % the user should be able to specify the flank, but the code falls back to 'auto' as default + % the user should be able to specify the flank, but the code falls back to 'up' as default if isempty(detectflank) - detectflank = 'auto'; + detectflank = 'up'; + end + if isempty(threshold) + threshold = 1.6; end event = read_yokogawa_event(filename, 'detectflank', detectflank, 'trigindx', trigindx, 'threshold', threshold); - case 'oxy3' + case 'artinis_oxy3' ft_hastoolbox('artinis', 1); - - event = read_artinis_oxy3(filename, true); if isempty(hdr) hdr = read_artinis_oxy3(filename); end + event = read_artinis_oxy3(filename, true); if isempty(trigindx) % indx gets precedence over labels! numbers before words - triglabel = ft_getopt(varargin, 'triglabel', 'ADC*'); % this allows subselection of AD channels to be markes as trigger channels (for Artinis oxy3 data) + triglabel = ft_getopt(varargin, 'triglabel', 'ADC*'); % this allows subselection of AD channels to be markes as trigger channels (for Artinis *.oxy3 data) trigindx = find(ismember(hdr.label, ft_channelselection(triglabel, hdr.label))); end @@ -2023,12 +2100,65 @@ end event = read_spmeeg_event(filename, 'header', hdr); + case {'blackrock_nev', 'blackrock_nsx'} + % use the NPMK toolbox for the file reading + ft_hastoolbox('NPMK', 1); + + % ensure that the filename contains a full path specification, + % otherwise the low-level function fails + [p,f,e] = fileparts(filename); + if ~isempty(p) + % this is OK + elseif isempty(p) + filename = which(filename); + end + + % 'noread' prevents reading of the spike waveforms + % 'nosave' prevents the automatic conversion of + % the .nev file as a .mat file + orig = openNEV(filename, 'noread', 'nosave') + + if orig.MetaTags.SampleRes ~= 30000 + error('sampling rate is different from 30 kHz') + % FIXME: why would this be a problem? + end + + fs = orig.MetaTags.SampleRes; % sampling rate + timestamps = orig.Data.SerialDigitalIO.TimeStamp; + eventCodeTimes = double(timestamps)./double(fs); % express in seconds + eventCodes = double(orig.Data.SerialDigitalIO.UnparsedData); + + % probably not necessary for all but we often have pins up + % FIXME: what is the consequence for the values if the pins were not 'up'? + % Should this be solved more generically? E.g. with an option? + eventCodes2= eventCodes-min(eventCodes)+1; + + for k=1:numel(eventCodes2) + event(k).type = 'trigger'; + event(k).sample = eventCodeTimes(k); + event(k).value = eventCodes2(k); + event(k).duration = 1; + event(k).offset = []; + end + case 'biopac_acq' + % this one has an implementation that I guess is intended + % to work according to the 'otherwise' case, yet it requires + % a two-pass through the function, needing a header + try + hdr = feval(eventformat, filename); + event = feval(eventformat, filename, hdr); + catch + ft_warning('FieldTrip:ft_read_event:unsupported_event_format','unsupported event format (%s)', eventformat); + event = []; + end + otherwise % attempt to run eventformat as a function % in case using an external read function was desired, this is where it is executed % if it fails, the regular unsupported warning message is thrown try - event = feval(eventformat,filename); + event = feval(eventformat, filename); + catch ft_warning('FieldTrip:ft_read_event:unsupported_event_format','unsupported event format (%s)', eventformat); event = []; diff --git a/external/fieldtrip/fileio/ft_read_header.m b/external/fieldtrip/fileio/ft_read_header.m index 3e6855ff..b6a05c14 100644 --- a/external/fieldtrip/fileio/ft_read_header.m +++ b/external/fieldtrip/fileio/ft_read_header.m @@ -13,7 +13,7 @@ % 'checkmaxfilter' = boolean, whether to check that maxfilter has been correctly applied (default = true) % 'chanindx' = list with channel indices in case of different sampling frequencies (only for EDF) % 'coordsys' = string, 'head' or 'dewar' (default = 'head') -% 'coilaccuracy' = can be empty or a number (0, 1 or 2) to specify the accuracy (default = []) +% 'chantype' = string or cell of strings, channel types to be read (NeuroOmega, BlackRock). % % This returns a header structure with the following elements % hdr.Fs sampling frequency @@ -36,6 +36,9 @@ % To use an external reading function, use key-value pair: 'headerformat', FUNCTION_NAME. % (Function needs to be on the path, and take as input: filename) % +% Use cfg.chantype='chaninfo' to get hdr.chaninfo table. For BlackRock +% specify decimation with chantype:skipfactor (e.g. cfg.chantype='analog:10') +% % Depending on the file format, additional header information can be % returned in the hdr.orig subfield. % @@ -44,6 +47,7 @@ % Neuromag - Elekta (*.fif) % BTi - 4D Neuroimaging (*.m4d, *.pdf, *.xyz) % Yokogawa (*.ave, *.con, *.raw) +% Ricoh (*.ave, *.con) % NetMEG (*.nc) % ITAB - Chieti (*.mhd) % Tristan Babysquid (*.fif) @@ -63,7 +67,7 @@ % TMSi (*.Poly5) % Mega Neurone (directory) % Natus/Nicolet/Nervus (.e files) -% Nihon Kohden (*.m00) +% Nihon Kohden (*.m00, *.EEG) % % The following spike and LFP dataformats are supported % Neuralynx (*.ncs, *.nse, *.nts, *.nev, *.nrd, *.dma, *.log) @@ -153,6 +157,8 @@ chanindx = ft_getopt(varargin, 'chanindx'); % this is used for EDF with different sampling rates coordsys = ft_getopt(varargin, 'coordsys', 'head'); % this is used for ctf and neuromag_mne, it can be head or dewar coilaccuracy = ft_getopt(varargin, 'coilaccuracy'); % empty, or a number between 0-2 +chantype = ft_getopt(varargin, 'chantype', {}); +if ~iscell(chantype); chantype = {chantype}; end % optionally get the data from the URL and make a temporary local copy filename = fetch_url(filename); @@ -194,7 +200,7 @@ else % check whether the file or directory exists if ~exist(filename, 'file') - ft_error('FILEIO:InvalidFileName', 'file or directory ''%s'' does not exist', filename); + ft_error('file or directory ''%s'' does not exist', filename); end checkUniqueLabels = true; @@ -429,30 +435,109 @@ fclose(orig.Head.FILE.FID); case 'blackrock_nev' - ft_error('this still needs some work'); + % read header for nsX file associated with NEV file + % use ft_read_event to read event information in .nev file - case 'blackrock_nsx' ft_hastoolbox('NPMK', 1); + % ensure that the filename contains a full path specification, + % otherwise the low-level function fails + [p,n] = fileparts(filename); + if isempty(p) + filename = which(filename); + [p,n] = fileparts(filename); + end + + NEV = openNEV(filename,'noread','nosave'); + + %searching for associated nsX file in same folder + files=dir(strcat(fullfile(p,n),'.ns*')); + if isempty(files) + ft_error('no .ns* file associated to %s in %s',n,p); + end + + %searching for nsX file with same sampling freq that NEV + for i=1:numel(files) + nsX_hdr = ft_read_header(fullfile(p,files(i).name),'chantype',chantype); + if nsX_hdr.Fs == NEV.MetaTags.SampleRes + hdr = nsX_hdr; + break + end + end + if isempty(hdr) + ft_error('no .ns* file with same sampling frequency as %s (%i)',n,NEV.MetaTags.SampleRes); + end + + case 'blackrock_nsx' + ft_hastoolbox('NPMK', 1); % ensure that the filename contains a full path specification, % otherwise the low-level function fails - [p, f, x] = fileparts(filename); - if ~isempty(p) - % this is OK - elseif isempty(p) + p = fileparts(filename); + if isempty(p) filename = which(filename); end + orig = openNSx(filename, 'noread'); + channelstype=regexp({orig.ElectrodesInfo.Label},'[a-z]\w+','match'); + chaninfo=table({orig.ElectrodesInfo.ElectrodeID}',... + transpose(deblank({orig.ElectrodesInfo.Label})),[channelstype{1,:}]',... + {orig.ElectrodesInfo.ConnectorBank}',{orig.ElectrodesInfo.ConnectorPin}',... + transpose(deblank({orig.ElectrodesInfo.AnalogUnits})),... + 'VariableNames',{'id' 'label' 'chantype' 'bank' 'pin' 'unit'}); + + if isempty(chantype) + chantype = unique(cellfun(@(x)x(1),channelstype)); + end + + %selecting channel according to chantype + orig_label=deblank({orig.ElectrodesInfo.Label}); + orig_unit=deblank({orig.ElectrodesInfo.AnalogUnits}); + channels={}; channelstype={}; channelsunit={}; skipfactor=[]; + for c=1:length(chantype) + chantype_split=strsplit(chantype{c},':'); + if numel(chantype_split) == 2 + chantype{c}=chantype_split{1}; + skipfactor=[skipfactor,str2double(chantype_split{2})]; + elseif numel(chantype_split) > 2 + ft_error('Use : to specify skipfactor, e.g. analog:10') + end + chan_sel=strncmpi(orig_label,chantype{c},length(chantype{c})); + if sum(chan_sel)==0 + ft_warning(strjoin({'unknown chantype ',chantype{c}})) + else + channels=[channels, orig_label(chan_sel)]; + channelsunit=[channelsunit, orig_unit(chan_sel)]; + channelstype=[channelstype, repmat(chantype(c), 1, sum(chan_sel))]; + end + end - hdr = []; - hdr.Fs = orig.MetaTags.SamplingFreq; - hdr.nChans = orig.MetaTags.ChannelCount; - hdr.nSamples = orig.MetaTags.DataPoints; + skipfactor=unique(skipfactor); + if isempty(skipfactor) + skipfactor=1; + elseif length(skipfactor)>1 + ft_error('inconsistent skip factors across channels'); + end + + %If no channel selected print table with available channels and chantypes + if isempty(channels) + chaninfo %printing table for user (should probably create a ft_print_table function + ft_warning(['No channel selected, see hdr.chaninfo. \nAvailable CFG.CHANTYPEs are: ',strjoin(unique(chaninfo.chantype),' ')]) + channelstype=chaninfo.chantype; hdr.chaninfo=chaninfo; + if isempty(chantype) || ~strcmpi(chantype{1},'chaninfo') + ft_error('Use chantype=''chaninfo'' for ft_read_header to return hdr with hdf.chaninfo') + end + end + + hdr.Fs = orig.MetaTags.SamplingFreq/skipfactor; + hdr.nChans = length(channels); + hdr.nSamples = floor(orig.MetaTags.DataPoints/skipfactor); hdr.nSamplesPre = 0; hdr.nTrials = 1; %? - hdr.label = deblank({orig.ElectrodesInfo.Label})'; - hdr.chanunit = deblank({hdr.orig.ElectrodesInfo.AnalogUnits})'; + hdr.label = deblank(channels)'; + hdr.chantype = channelstype; + hdr.chanunit = channelsunit; hdr.orig = orig; + hdr.skipfactor = skipfactor; case {'brainvision_vhdr', 'brainvision_seg', 'brainvision_eeg', 'brainvision_dat'} orig = read_brainvision_vhdr(filename); @@ -859,7 +944,7 @@ hdr.orig.CateNames = CateNames; hdr.orig.CatLengths = CatLengths; - case {'egi_mff_v1' 'egi_mff'} % this is currently the default + case 'egi_mff_v1' % The following represents the code that was written by Ingrid, Robert % and Giovanni to get started with the EGI mff dataset format. It might % not support all details of the file formats. @@ -867,6 +952,10 @@ % An alternative implementation has been provided by EGI, this is % released as fieldtrip/external/egi_mff and referred further down in % this function as 'egi_mff_v2'. + % + % An more recent implementation has been provided by EGI and Arno Delorme, this + % is released as https://github.com/arnodelorme/mffmatlabio and referred further + % down in this function as 'egi_mff_v3'. if ~usejava('jvm') ft_error('the xml2struct requires MATLAB to be running with the Java virtual machine (JVM)'); @@ -1121,6 +1210,10 @@ end hdr = read_mff_header(filename); + case {'egi_mff_v3' 'egi_mff'} % this is the default + ft_hastoolbox('mffmatlabio', 1); + hdr = mff_fileio_read_header(filename); + case 'fcdc_buffer' % read from a networked buffer for realtime analysis [host, port] = filetype_check_uri(filename); @@ -1702,6 +1795,33 @@ case 'nexstim_nxe' hdr = read_nexstim_nxe(filename); + case 'neuromag_headpos' + % neuromag headposition file created with maxfilter, the position varies over time + orig = read_neuromag_headpos(filename); + hdr.Fs = 1/(orig.data(1,2)-orig.data(1,1)); + hdr.nChans = size(orig.data,1); + hdr.nSamples = size(orig.data,2); + hdr.nSamplesPre = 0; % convert from ms to samples + hdr.nTrials = 1; + hdr.label = orig.colheaders; + + case 'neuromag_maxfilterlog' + % neuromag log file created with maxfilter + log = read_neuromag_maxfilterlog(filename); + hdr = []; + hdr.label = {'t' 'e' 'g' 'v' 'r' 'd'}; + for i=1:numel(log.hpi) + for j=1:11 + hdr.label{end+1} = sprintf('hpi%d_%02d', i, j); + end + end + hdr.nChans = length(hdr.label); + hdr.nSamples = length(log.t); + hdr.nSamplesPre = 0; + hdr.nTrials = 1; + hdr.Fs = 1 / median(diff(log.t)); + hdr.orig = log; + case {'neuromag_fif' 'neuromag_mne'} % check that the required low-level toolbox is available ft_hastoolbox('mne', 1); @@ -1874,6 +1994,90 @@ % remember the original header details hdr.orig = orig; + case 'neuroomega_mat' + % These are MATLAB *.mat files created by the software 'Map File Converter' from + % the propietary .mpx files recorded by NeuroOmega + chantype_dict = {'micro','macro', 'analog', 'micro_lfp','macro_lfp','micro_hp','add_analog';... + 'CRAW', 'CMacro_RAW','CANALOG', 'CLFP', 'CMacro', 'CSPK' ,'CADD_ANALOG'}; + neuroomega_param = {'_KHz','_KHz_Orig','_Gain','_BitResolution','_TimeBegin','_TimeEnd'}; + + % identifying channels to be loaded + orig = matfile(filename); + fields_orig = who(orig); + + %is_param=endsWith(fields_orig,neuroomega_param); %Matlab 2017a + is_param=zeros(length(fields_orig),1); + for i=1:length(neuroomega_param) + is_param = is_param | ~cellfun('isempty',regexp(fields_orig,strcat(neuroomega_param(i),'$'),'start')); + end + + channels={}; channelstype={}; + for c = 1:length(chantype) + chantype_dict_sel=strcmpi(chantype_dict(1,:),chantype{c}); + if sum(chantype_dict_sel)>0 + chanbasename=chantype_dict{2, chantype_dict_sel}; + sel_chan=fields_orig(strncmpi(fields_orig,chanbasename,length(chanbasename)) & ~is_param); + if isempty(sel_chan) + ft_warning(strjoin({'chantype ',chantype{c},' selected but no ',chanbasename,' found'})) + else + channels=[channels;sel_chan]; + channelstype=[channelstype;repmat(chantype(c), size(sel_chan))]; + end + elseif ~strcmpi(chantype{c},'chaninfo') + ft_warning(strjoin({'unknown chantype ',chantype{c}})); + end + end + if ~isempty(channels) + chan_t=table; + for i=1:length(channels) + ch=channels{i}; + ch_whos=whos(orig,ch); + chan_t=[chan_t;{ch,orig.([ch,'_KHz'])*1000, orig.([ch,'_TimeBegin']), ch_whos.size(2)}]; + end + chan_t.Properties.VariableNames={'channel' 'Fs' 'T0' 'nSamples'}; + + Fs=unique(chan_t.Fs); + T0=unique(chan_t.T0); + nSamples=unique(chan_t.nSamples); + if length(Fs)>1 || length(T0)>1 + chan_t % printing table for user + ft_error('inconsistent channels with different sampling rates or initial times'); + end + if length(nSamples)>1 + chan_t % printing table for user + ft_warning('inconsistent number of samples across channels. Selecting minimun nSample') + nSamples=min(nSamples); + end + + else % If no channel selected + channels=fields_orig(contains(fields_orig,chantype_dict(2,:)) & ~is_param); + % Matching channels to chantypes + M=cell2mat(cellfun(@(x) contains(channels,x),chantype_dict(2,:),'UniformOutput',false)); + chantype_ix = sum( cumprod(M == 0, 2), 2) + 1; + Fs=cellfun(@(x) orig.([x '_KHz'])*1000,channels); + chaninfo=table(channels,chantype_dict(1,chantype_ix)',Fs,'VariableNames',{'channel' 'chantype' 'Fs'}); + if isempty(chantype) + chaninfo % printing channel info for user. ToDo: ft_print_table + ft_warning('Define chantype of interest from table above or use ''chaninfo'''); + elseif ~strcmpi(chantype{1},'chaninfo') || strcmpi(chantype{1},'info') + ft_error(['Incorrect cfg.chantype, use one of ',strjoin(unique(chaninfo.chantype),' ')]) + end + Fs=nan; nSamples=nan; channelstype=chaninfo.chantype; hdr.chaninfo=chaninfo; + end + + % building header + hdr.Fs = Fs; + hdr.nChans = length(channels); + hdr.nSamples = nSamples; + hdr.nSamplesPre = 0; + hdr.nTrials = 1; + hdr.label = deblank(channels); + % store the complete information in hdr.orig ft_read_data and ft_read_event will + % get it from there + hdr.orig = orig; + hdr.chantype = channelstype; + hdr.chanunit = repmat({'uV'}, size(hdr.label)); + case 'neuroprax_eeg' orig = np_readfileinfo(filename); @@ -1959,6 +2163,10 @@ case 'nihonkohden_m00' hdr = read_nihonkohden_hdr(filename); + case 'nihonkohden_eeg' + ft_hastoolbox('brainstorm', 1); + hdr = read_brainstorm_header(filename); + case 'nimh_cortex' cortex = read_nimh_cortex(filename, 'epp', 'no', 'eog', 'no'); % look at the first trial to determine whether it contains data in the EPP and EOG channels @@ -2045,7 +2253,7 @@ hdr.label = {tmp.hdr.entityinfo(tmp.list.analog(tmp.analog.contcount~=0)).EntityLabel}; %%% contains non-unique chans? hdr.orig = tmp; % remember the original header - case 'oxy3' + case 'artinis_oxy3' ft_hastoolbox('artinis', 1); hdr = read_artinis_oxy3(filename); @@ -2160,6 +2368,20 @@ hdr.label = hdr.label(:); hdr.nChans = length(hdr.label); + case {'ricoh_ave', 'ricoh_con', 'ricoh_mrk'} + % header can be read with Ricoh MEG Reader + hdr = read_ricoh_header(filename); + % add a gradiometer structure for forward and inverse modelling + hdr.grad = ricoh2grad(hdr); + hdr.chantype = ft_chantype(hdr.label); + unk = find(strcmp('unknown', hdr.chantype)); + % Warning message: + if ~isempty(unk) + label_unk = hdr.label(unk); + no_unk = num2cell(unk); + C = [label_unk(:), no_unk(:)] .'; + ft_warning(['Unknown channel types: (label, no) =' repmat('( %s, %d ) ', 1, length(unk) ) '\n'], C{:}) + end case 'smi_txt' smi = read_smi_txt(filename); @@ -2275,6 +2497,15 @@ hdr = read_yokogawa_header_new(filename); % add a gradiometer structure for forward and inverse modelling hdr.grad = yokogawa2grad_new(hdr); + hdr.chantype = ft_chantype(hdr.label); + unk = find(strcmp('unknown', hdr.chantype)); + % Warning message: + if ~isempty(unk) + label_unk = hdr.label(unk); + no_unk = num2cell(unk); + C = [label_unk(:), no_unk(:)] .'; + ft_warning(['Unknown channel types: (label, no) =' repmat('( %s, %d ) ', 1, length(unk) ) '\n'], C{:}) + end else ft_hastoolbox('yokogawa', 1); % try it with the old version of the toolbox hdr = read_yokogawa_header(filename); @@ -2282,7 +2513,7 @@ hdr.grad = yokogawa2grad(hdr); end - case 'riff_wave' + case {'riff_wave', 'audio_m4a'} % prior to MATLAB R2015b this used to be done with "wavread" % but the audioinfo/audioread function are at least available from 2012b up info = audioinfo(filename); @@ -2312,12 +2543,16 @@ hdr = read_videomeg_vid(filename); checkUniqueLabels = false; + case 'video' + hdr = read_video(filename); + checkUniqueLabels = false; + otherwise % attempt to run headerformat as a function % in case using an external read function was desired, this is where it is executed % if it fails, the regular unsupported error message is thrown + hdr = feval(headerformat,filename); try - hdr = feval(headerformat,filename); catch if strcmp(fallback, 'biosig') && ft_hastoolbox('BIOSIG', 1) hdr = read_biosig_header(filename); diff --git a/external/fieldtrip/fileio/ft_read_headshape.m b/external/fieldtrip/fileio/ft_read_headshape.m index 1155436d..5ddb8d52 100644 --- a/external/fieldtrip/fileio/ft_read_headshape.m +++ b/external/fieldtrip/fileio/ft_read_headshape.m @@ -84,7 +84,7 @@ coordsys = ft_getopt(varargin, 'coordsys', 'head'); % for ctf or neuromag_mne coil positions, the alternative is dewar fileformat = ft_getopt(varargin, 'format'); unit = ft_getopt(varargin, 'unit'); -image = ft_getopt(varargin, 'image',[100, 100 ,100]); % path to .jpeg file +image = ft_getopt(varargin, 'image', [100, 100 ,100]); % path to .jpeg file % Check the input, if filename is a cell-array, call ft_read_headshape recursively and combine the outputs. % This is used to read the left and right hemisphere of a Freesurfer cortical segmentation. @@ -198,7 +198,9 @@ [pathstr,name] = fileparts(filename); if exist(fullfile(pathstr,[name,'.jpg'])) image = fullfile(pathstr,[name,'.jpg']); - hasimage = 1; + hasimage = true; +else + hasimage = false; end @@ -452,8 +454,7 @@ % shape.orig.labelindx(shape.orig.labelindx==ulabelindx(k)) = k; % shape.labelindx(shape.labelindx==ulabelindx(k)) = k; % end - % FIXME the above screws up the interpretation of the labels, because the - % color table is not sorted + % FIXME the above screws up the interpretation of the labels, because the color table is not sorted shape.label = c.struct_names; shape.annotation = c.orig_tab; % to be able to recover which one shape.ctable = c.table; @@ -497,7 +498,176 @@ ft_error('incorrect coordinates specified'); end - case {'yokogawa_mrk', 'yokogawa_ave', 'yokogawa_con', 'yokogawa_raw' } + case {'ricoh_mrk', 'ricoh_ave', 'ricoh_con'} + hdr = read_ricoh_header(filename); + + %% An exported file or an original one + isexported = hdr.orig.digitize.info.done; + + %% Marker-coil positions + mrk_pnt = hdr.orig.coregist.hpi; + if any([mrk_pnt(:).meg_pos]) + mrk_pos = cat(1, mrk_pnt(1:end).meg_pos); + mrk_label = transpose({mrk_pnt(1:end).label}); + sw_ind = [3 1 2]; + mrk_pos(1:3,:)= mrk_pos(sw_ind, :); + mrk_pos = mrk_pos * 100; % unit: cm + mrk_label(1:3,:)= mrk_label(sw_ind, :); + else + ft_error('No coil information found in the file'); + end + + %% Digitized points + if ~isexported + ft_info('The input file is an original one: only marker-coil positions are loaded\n'); + % The fiducial points are represented by the marker-coil positions. + if any([mrk_pnt(:).meg_pos]) + shape.fid.pos = mrk_pos; % unit: cm + shape.fid.label = {'nas'; 'lpa'; 'rpa'; 'Marker4'; 'Marker5'}; + end + else + ft_info('The input file is a third-party-exported one including the digitized points\n'); + % All digitized points + dig_pnt = hdr.orig.digitize.point; + digitizer2meg = hdr.orig.digitize.info.digitizer2meg; + R = digitizer2meg(1:3,1:3); + T = digitizer2meg(1:3,4); + % Transform to MEG coordinate: + shape.pos = transpose( R * [[dig_pnt.x]; [dig_pnt.y]; [dig_pnt.z]] + repmat(T, 1, numel(dig_pnt))).*100; % unit: cm + shape.label = transpose( deblank({dig_pnt(1:end).name})); + % Fiducial points + nas = find(strcmpi(shape.label, 'fidnz')); + lpa = find(strcmpi(shape.label, 'fidt9')); + rpa = find(strcmpi(shape.label, 'fidt10')); + if ~isempty(nas) && ~isempty(lpa) && ~isempty(rpa) + anatfid_pos = [ shape.pos(nas,:) ; shape.pos(lpa,:) ; shape.pos(rpa,:) ]; + anatfid_label = {'nas'; 'lpa'; 'rpa'}; + end + % HPIs + HPI_1 = find(strcmpi(shape.label, 'HPI_1')); + HPI_2 = find(strcmpi(shape.label, 'HPI_2')); + HPI_3 = find(strcmpi(shape.label, 'HPI_3')); + HPI_4 = find(strcmpi(shape.label, 'HPI_4')); + HPI_5 = find(strcmpi(shape.label, 'HPI_5')); + if ~isempty(HPI_1) && ~isempty(HPI_2) && ~isempty(HPI_3) && ~isempty(HPI_4) && ~isempty(HPI_5) + HPI_pos = [ shape.pos(HPI_3,:) ;... + shape.pos(HPI_1,:) ;... + shape.pos(HPI_2,:) ;... + shape.pos(HPI_4,:) ;... + shape.pos(HPI_5,:) ]; + HPI_label = { 'HPI_3'; 'HPI_1'; 'HPI_2'; 'HPI_4'; 'HPI_5' }; + end + shape.fid.pos = [ anatfid_pos; HPI_pos; mrk_pos ]; + shape.fid.label = [ anatfid_label; HPI_label; mrk_label ]; + end + % 'cm' as a unit for 'pos': + shape.unit = 'cm'; + + case {'yokogawa_mrk', 'yokogawa_ave', 'yokogawa_con'} + if ft_hastoolbox('yokogawa_meg_reader') + hdr = read_yokogawa_header_new(filename); + %% Marker-coil positions + mrk_pnt = hdr.orig.coregist.hpi; + + % markers 1-3 identical to zero: try *.mrk file + if ~any([mrk_pnt(:).meg_pos]) + ft_info('Reading marker-coil positions from a .mrk file\n'); + [p, f, x] = fileparts(filename); + filename = fullfile(p, [f '.mrk']); + if exist(filename, 'file') + hdr_tmp = read_yokogawa_header_new(filename); + mrk_pnt = hdr_tmp.orig.coregist.hpi; + end + end + + if any([mrk_pnt(:).meg_pos]) + mrk_pos = cat(1, mrk_pnt(1:end).meg_pos); + mrk_label = transpose({mrk_pnt(1:end).label}); + sw_ind = [3 1 2]; + mrk_pos(1:3,:)= mrk_pos(sw_ind, :); + mrk_pos = mrk_pos * 100; % unit: cm + mrk_label(1:3,:)= mrk_label(sw_ind, :); + else + ft_error('No coil information found in the file'); + end + + %% An exported file or an original one + isexported = hdr.orig.digitize.info.done; + + %% Digitized points + if ~isexported + ft_info('The input file is an original one: only marker-coil positions are loaded\n'); + % The fiducial points are represented by the marker-coil positions. + if any([mrk_pnt(:).meg_pos]) + shape.fid.pos = mrk_pos; % unit: cm + shape.fid.label = {'nas'; 'lpa'; 'rpa'; 'Marker4'; 'Marker5'}; + end + else + ft_info('The input file is a third-party-exported one including the digitized points\n'); + % All digitized points + dig_pnt = hdr.orig.digitize.point; + digitizer2meg = hdr.orig.digitize.info.digitizer2meg; + R = digitizer2meg(1:3,1:3); + T = digitizer2meg(1:3,4); + % Transform to MEG coordinate: + shape.pos = transpose( R * [[dig_pnt.x]; [dig_pnt.y]; [dig_pnt.z]] + repmat(T, 1, numel(dig_pnt))).*100; % unit: cm + shape.label = transpose( deblank({dig_pnt(1:end).name})); + % Fiducial points + nas = find(strcmpi(shape.label, 'fidnz')); + lpa = find(strcmpi(shape.label, 'fidt9')); + rpa = find(strcmpi(shape.label, 'fidt10')); + if ~isempty(nas) && ~isempty(lpa) && ~isempty(rpa) + anatfid_pos = [ shape.pos(nas,:) ; shape.pos(lpa,:) ; shape.pos(rpa,:) ]; + anatfid_label = {'nas'; 'lpa'; 'rpa'}; + end + % HPIs + HPI_1 = find(strcmpi(shape.label, 'HPI_1')); + HPI_2 = find(strcmpi(shape.label, 'HPI_2')); + HPI_3 = find(strcmpi(shape.label, 'HPI_3')); + HPI_4 = find(strcmpi(shape.label, 'HPI_4')); + HPI_5 = find(strcmpi(shape.label, 'HPI_5')); + if ~isempty(HPI_1) && ~isempty(HPI_2) && ~isempty(HPI_3) && ~isempty(HPI_4) && ~isempty(HPI_5) + HPI_pos = [ shape.pos(HPI_3,:) ;... + shape.pos(HPI_1,:) ;... + shape.pos(HPI_2,:) ;... + shape.pos(HPI_4,:) ;... + shape.pos(HPI_5,:) ]; + HPI_label = { 'HPI_3'; 'HPI_1'; 'HPI_2'; 'HPI_4'; 'HPI_5' }; + end + shape.fid.pos = [ anatfid_pos; HPI_pos; mrk_pos ]; + shape.fid.label = [ anatfid_label; HPI_label; mrk_label ]; + end + % 'cm' as a unit for 'pos': + shape.unit = 'cm'; + + else % the case that "yokogawa_meg_reader" is not available + hdr = read_yokogawa_header(filename); + marker = hdr.orig.matching_info.marker; + % markers 1-3 identical to zero: try *.mrk file + if ~any([marker(:).meg_pos]) + [p, f, x] = fileparts(filename); + filename = fullfile(p, [f '.mrk']); + if exist(filename, 'file') + hdr = read_yokogawa_header(filename); + marker = hdr.orig.matching_info.marker; + end + end + + % non zero markers 1-3 + if any([marker(:).meg_pos]) + shape.fid.pos = cat(1, marker(1:5).meg_pos); + sw_ind = [3 1 2]; + shape.fid.pos(1:3,:)= shape.fid.pos(sw_ind, :); + shape.fid.label = {'nas'; 'lpa'; 'rpa'; 'Marker4'; 'Marker5'}; + else + ft_error('no coil information found in Yokogawa file'); + end + + % convert to the units of the grad, the desired default for yokogawa is centimeter. + shape = ft_convert_units(shape, 'cm'); + end + + case 'yokogawa_raw' if ft_hastoolbox('yokogawa_meg_reader') hdr = read_yokogawa_header_new(filename); marker = hdr.orig.coregist.hpi; @@ -534,6 +704,43 @@ % convert to the units of the grad, the desired default for yokogawa is centimeter. shape = ft_convert_units(shape, 'cm'); + % case {'yokogawa_mrk', 'yokogawa_ave', 'yokogawa_con', 'yokogawa_raw' } + % if ft_hastoolbox('yokogawa_meg_reader') + % hdr = read_yokogawa_header_new(filename); + % marker = hdr.orig.coregist.hpi; + % else + % hdr = read_yokogawa_header(filename); + % marker = hdr.orig.matching_info.marker; + % end + % + % % markers 1-3 identical to zero: try *.mrk file + % if ~any([marker(:).meg_pos]) + % [p, f, x] = fileparts(filename); + % filename = fullfile(p, [f '.mrk']); + % if exist(filename, 'file') + % if ft_hastoolbox('yokogawa_meg_reader') + % hdr = read_yokogawa_header_new(filename); + % marker = hdr.orig.coregist.hpi; + % else + % hdr = read_yokogawa_header(filename); + % marker = hdr.orig.matching_info.marker; + % end + % end + % end + % + % % non zero markers 1-3 + % if any([marker(:).meg_pos]) + % shape.fid.pos = cat(1, marker(1:5).meg_pos); + % sw_ind = [3 1 2]; + % shape.fid.pos(1:3,:)= shape.fid.pos(sw_ind, :); + % shape.fid.label = {'nas'; 'lpa'; 'rpa'; 'Marker4'; 'Marker5'}; + % else + % ft_error('no coil information found in Yokogawa file'); + % end + % + % % convert to the units of the grad, the desired default for yokogawa is centimeter. + % shape = ft_convert_units(shape, 'cm'); + case 'yokogawa_coregis' in_str = textread(filename, '%s'); nr_items = size(in_str,1); @@ -668,6 +875,11 @@ % copy some optional fields over with a new name shape = copyfields(tmp, shape, {'Faces', 'Curvature', 'SulciMap'}); shape = renamefields(shape, {'Faces', 'Curvature', 'SulciMap'}, {'tri', 'curv', 'sulc'}); + elseif numel(fieldnames(tmp))==1 + fn = fieldnames(tmp); + shape = tmp.(fn{1}); + % check that it has vertices and triangles + assert(isfield(shape, 'pos') && isfield(shape, 'tri'), 'no headshape found in MATLAB file') else ft_error('no headshape found in MATLAB file'); end @@ -715,24 +927,26 @@ case 'obj' ft_hastoolbox('wavefront', 1); - % Implemented for structure.io .obj thus far + % Only tested for structure.io .obj thus far obj = read_wobj(filename); - shape.pos = obj.vertices; + shape.pos = obj.vertices(:,1:3); shape.pos = shape.pos - repmat(sum(shape.pos)/length(shape.pos),[length(shape.pos),1]); %centering vertices - shape.tri = obj.objects(2).data.vertices; - if (~isempty(image) && exist('hasimage','var')) + shape.tri = obj.objects(end).data.vertices; + if hasimage texture = obj.vertices_texture; - %Refines the mesh and textures to increase resolution of the - %colormapping - for i = 1:1 - [shape.pos, shape.tri,texture] = refine(shape.pos,shape.tri,'banks',texture); - end - picture = imread(image); - color = uint8(zeros(length(shape.pos),3)); + % Refines the mesh and textures to increase resolution of the colormapping + [shape.pos, shape.tri, texture] = refine(shape.pos, shape.tri, 'banks', texture); + + picture = imread(image); + color = uint8(zeros(length(shape.pos),3)); for i=1:length(shape.pos) color(i,1:3) = picture(floor((1-texture(i,2))*length(picture)),1+floor(texture(i,1)*length(picture)),1:3); end + shape.color = color; + elseif size(obj.vertices,2)==6 + % the vertices also contain RGB colors + shape.color = obj.vertices(:,4:6); end case 'vtk' @@ -801,9 +1015,9 @@ if size(IMPORT.data,2)==6 labels = IMPORT.data(:,6); % representation of tissue type is compatible with ft_datatype_parcellation - numlabels = size(unique(labels),1); - ulabel = unique(labels); - shape.tissue = zeros(size(labels)); + numlabels = size(unique(labels),1); + ulabel = unique(labels); + shape.tissue = zeros(size(labels)); shape.tissuelabel = {}; for i = 1:numlabels shape.tissue(labels == ulabel(i)) = i; @@ -925,8 +1139,7 @@ npos = read_asa(filename, 'NumberHeadShapePoints=', '%d'); if ~isempty(npos) && npos>0 origunit = read_asa(filename, 'UnitHeadShapePoints', '%s', 1); - pos = read_asa(filename, 'HeadShapePoints', '%f', npos, ':'); - + pos = read_asa(filename, 'HeadShapePoints', '%f', npos, ':'); pos = ft_scalingfactor(origunit, 'mm')*pos; shape.pos = pos; @@ -1007,15 +1220,11 @@ try % ft_determine_units will fail for triangle-only gifties. shape = ft_determine_units(shape); - catch end end % ensure that vertex positions are given in pos, not in pnt shape = fixpos(shape); + % ensure that the numerical arrays are represented in double precision and not as integers shape = ft_struct2double(shape); - -if (~isempty(image) && exist('hasimage','var')) - shape.color = color; -end diff --git a/external/fieldtrip/fileio/ft_read_mri.m b/external/fieldtrip/fileio/ft_read_mri.m index b2576d3c..c5eb8eaf 100644 --- a/external/fieldtrip/fileio/ft_read_mri.m +++ b/external/fieldtrip/fileio/ft_read_mri.m @@ -1,37 +1,37 @@ function [mri] = ft_read_mri(filename, varargin) -% FT_READ_MRI reads anatomical and functional MRI data from different -% file formats. The output data is structured in such a way that it is -% comparable to a FieldTrip source reconstruction. +% FT_READ_MRI reads anatomical and functional MRI data from different file formats. +% The output data is structured in such a way that it is compatible with +% FT_DATATYPE_VOLUME. % % Use as % [mri] = ft_read_mri(filename) % % Additional options should be specified in key-value pairs and can be -% 'dataformat' = string specifying the file format, determining the low- -% level reading routine to be used. If no format is given, -% it is determined automatically from the file. -% The following formats can be specified: -% 'afni_head'/'afni_brik' uses afni -% 'analyze_img'/'analyze_hdr' uses spm -% 'analyze_old' uses Darren Webber's code -% 'asa_mri' -% 'ctf_mri' -% 'ctf_mri4' -% 'ctf_svl' -% 'dicom' uses freesurfer -% 'dicom_old' uses own code -% 'freesurfer_mgh' uses freesurfer -% 'freesurfer_mgz' uses freesurfer -% 'minc' uses spm (<= version spm5) -% 'nifti' uses freesurfer -% 'nifti_fsl' uses freesurfer -% 'nifti_spm' uses spm -% 'neuromag_fif' uses mne toolbox -% 'neuromag_fif_old' uses meg-pd toolbox -% 'yokogawa_mri' -% 'matlab' assumes a MATLAB *.mat file containing a mri structure -% according FieldTrip standards +% 'dataformat' = string specifying the file format, determining the low- +% level reading routine to be used. If no explicit format +% is given, it is determined automatically from the filename. +% +% The following values apply for the dataformat +% 'afni_head'/'afni_brik' uses AFNI code +% 'analyze_img'/'analyze_hdr' uses SPM code +% 'analyze_old' uses Darren Webber's code +% 'asa_mri' +% 'ctf_mri' +% 'ctf_mri4' +% 'ctf_svl' +% 'dicom' uses FreeSurfer code +% 'dicom_old' uses own code +% 'freesurfer_mgh' uses FreeSurfer code +% 'freesurfer_mgz' uses FreeSurfer code +% 'matlab' assumes a MATLAB *.mat file containing a mri structure according to FT_DATATYPE_VOLUME +% 'minc' uses SPM (<= version SPM5) +% 'neuromag_fif' uses MNE toolbox +% 'neuromag_fif_old' uses meg-pd toolbox +% 'nifti' uses FreeSurfer code +% 'nifti_fsl' uses FreeSurfer code +% 'nifti_spm' uses SPM +% 'yokogawa_mri' % % The following MRI file formats are supported % CTF - VSM MedTech (*.svl, *.mri version 4 and 5) @@ -45,14 +45,14 @@ % ANT - Advanced Neuro Technology (*.mri) % Yokogawa (*.mrk, incomplete) % -% If you have a series of DICOM files, please provide the name of any of the files +% If you have a series of DICOM files, please provide the name of any of the files % in the series (e.g. the first one). The other files will be found automatically. % % The output MRI may have a homogenous transformation matrix that converts % the coordinates of each voxel (in xgrid/ygrid/zgrid) into head % coordinates. % -% See also FT_WRITE_MRI, FT_READ_DATA, FT_READ_HEADER, FT_READ_EVENT +% See also FT_DATATYPE_VOLUME, FT_WRITE_MRI, FT_READ_DATA, FT_READ_HEADER, FT_READ_EVENT % Copyright (C) 2008-2013, Robert Oostenveld & Jan-Mathijs Schoffelen % @@ -115,328 +115,329 @@ hasspm12 = ft_hastoolbox('spm12'); % see http://www.fil.ion.ucl.ac.uk/spm/ switch dataformat -case 'ctf_mri' - [img, hdr] = read_ctf_mri(filename); - transform = hdr.transformMRI2Head; - coordsys = 'ctf'; - -case 'ctf_mri4' - [img, hdr] = read_ctf_mri4(filename); - transform = hdr.transformMRI2Head; - coordsys = 'ctf'; - -case 'ctf_svl' - [img, hdr] = read_ctf_svl(filename); - transform = hdr.transform; - -case 'asa_mri' - [img, seg, hdr] = read_asa_mri(filename); - transform = hdr.transformMRI2Head; - if isempty(seg) - % in case seg exists it will be added to the output - clear seg - end - -case 'minc' - if ~(hasspm2 || hasspm5) - fprintf('the SPM2 or SPM5 toolbox is required to read *.mnc files\n'); - ft_hastoolbox('spm2', 1); - end - % use the functions from SPM - hdr = spm_vol_minc(filename); - img = spm_read_vols(hdr); - transform = hdr.mat; - -case 'nifti_spm' - if ~(hasspm5 || hasspm8 || hasspm12) - fprintf('the SPM5 or newer toolbox is required to read *.nii files\n'); - ft_hastoolbox('spm8up', 1); - end - % use the functions from SPM - hdr = spm_vol_nifti(filename); - img = spm_read_vols(hdr); - transform = hdr.mat; - -case {'analyze_img' 'analyze_hdr'} - if ~(hasspm8 || hasspm12) - fprintf('the SPM8 or newer toolbox is required to read analyze files\n'); - ft_hastoolbox('spm8up', 1); - end - - % use the image file instead of the header - filename((end-2):end) = 'img'; - % use the functions from SPM to read the Analyze MRI - hdr = spm_vol(filename); - img = spm_read_vols(hdr); - transform = hdr.mat; - -case 'analyze_old' - % use the functions from Darren Weber's mri_toolbox to read the Analyze MRI - ft_hastoolbox('mri', 1); % from Darren Weber, see http://eeg.sourceforge.net/ - - avw = avw_img_read(filename, 0); % returned volume is LAS* - img = avw.img; - hdr = avw.hdr; - % The default Analyze orientation is axial unflipped (LAS*), which means - % that the resulting volume is according to the radiological convention. - % Most other fMRI and EEG/MEG software (except Mayo/Analyze) uses - % neurological conventions and a right-handed coordinate system, hence - % the first axis of the 3D volume (right-left) should be flipped to make - % the coordinate system comparable to SPM - ft_warning('flipping 1st dimension (L-R) to obtain volume in neurological convention'); - img = flipdim(img, 1); - - transform = diag(hdr.dime.pixdim(2:4)); - transform(4,4) = 1; - -case {'afni_brik' 'afni_head'} - % needs afni - ft_hastoolbox('afni', 1); % see http://afni.nimh.nih.gov/ - - [err, img, hdr, ErrMessage] = BrikLoad(filename); - if err - ft_error('could not read AFNI file'); - end - - % FIXME: this should be checked, but I only have a single BRIK file - % construct the homogenous transformation matrix that defines the axes - ft_warning('homogenous transformation might be incorrect for AFNI file'); - transform = eye(4); - transform(1:3,4) = hdr.ORIGIN(:); - transform(1,1) = hdr.DELTA(1); - transform(2,2) = hdr.DELTA(2); - transform(3,3) = hdr.DELTA(3); - - % FIXME: I am not sure about the "RAI" image orientation - img = flipdim(img,1); - img = flipdim(img,2); - dim = size(img); - transform(1,4) = -dim(1) - transform(1,4); - transform(2,4) = -dim(2) - transform(2,4); - -case 'neuromag_fif' - % needs mne toolbox - ft_hastoolbox('mne', 1); - - % use the mne functions to read the Neuromag MRI - hdr = fiff_read_mri(filename); - img_t = cat(3, hdr.slices.data); - img = permute(img_t,[2 1 3]); - hdr.slices = rmfield(hdr.slices, 'data'); % remove the image data to save memory - - % information below is from MNE - fiff_define_constants.m - % coordinate system 4 - is the MEG head coordinate system (fiducials) - % coordinate system 5 - is the MRI coordinate system - % coordinate system 2001 - MRI voxel coordinates - % coordinate system 2002 - Surface RAS coordinates (is mainly vertical - % shift, no rotation to 2001) - % MEG sensor positions come in system 4 - % MRI comes in system 2001 - - transform = eye(4); - if isfield(hdr, 'trans') && issubfield(hdr.trans, 'trans') - if (hdr.trans.from == 4) && (hdr.trans.to == 5) - transform = hdr.trans.trans; + case 'ctf_mri' + [img, hdr] = read_ctf_mri(filename); + transform = hdr.transformMRI2Head; + coordsys = 'ctf'; + + case 'ctf_mri4' + [img, hdr] = read_ctf_mri4(filename); + transform = hdr.transformMRI2Head; + coordsys = 'ctf'; + + case 'ctf_svl' + [img, hdr] = read_ctf_svl(filename); + transform = hdr.transform; + + case 'asa_mri' + [img, seg, hdr] = read_asa_mri(filename); + transform = hdr.transformMRI2Head; + if isempty(seg) + % in case seg exists it will be added to the output + clear seg + end + + case 'minc' + if ~(hasspm2 || hasspm5) + fprintf('the SPM2 or SPM5 toolbox is required to read *.mnc files\n'); + ft_hastoolbox('spm2', 1); + end + % use the functions from SPM + hdr = spm_vol_minc(filename); + img = spm_read_vols(hdr); + transform = hdr.mat; + + case 'nifti_spm' + if ~(hasspm5 || hasspm8 || hasspm12) + fprintf('the SPM5 or newer toolbox is required to read *.nii files\n'); + ft_hastoolbox('spm12', 1); + end + % use the functions from SPM + hdr = spm_vol_nifti(filename); + img = double(hdr.private.dat); + %img = spm_read_vols(hdr); + transform = hdr.mat; + + case {'analyze_img' 'analyze_hdr'} + if ~(hasspm8 || hasspm12) + fprintf('the SPM8 or newer toolbox is required to read analyze files\n'); + ft_hastoolbox('spm8up', 1); + end + + % use the image file instead of the header + filename((end-2):end) = 'img'; + % use the functions from SPM to read the Analyze MRI + hdr = spm_vol(filename); + img = spm_read_vols(hdr); + transform = hdr.mat; + + case 'analyze_old' + % use the functions from Darren Weber's mri_toolbox to read the Analyze MRI + ft_hastoolbox('mri', 1); % from Darren Weber, see http://eeg.sourceforge.net/ + + avw = avw_img_read(filename, 0); % returned volume is LAS* + img = avw.img; + hdr = avw.hdr; + % The default Analyze orientation is axial unflipped (LAS*), which means + % that the resulting volume is according to the radiological convention. + % Most other fMRI and EEG/MEG software (except Mayo/Analyze) uses + % neurological conventions and a right-handed coordinate system, hence + % the first axis of the 3D volume (right-left) should be flipped to make + % the coordinate system comparable to SPM + ft_warning('flipping 1st dimension (L-R) to obtain volume in neurological convention'); + img = flipdim(img, 1); + + transform = diag(hdr.dime.pixdim(2:4)); + transform(4,4) = 1; + + case {'afni_brik' 'afni_head'} + % needs afni + ft_hastoolbox('afni', 1); % see http://afni.nimh.nih.gov/ + + [err, img, hdr, ErrMessage] = BrikLoad(filename); + if err + ft_error('could not read AFNI file'); + end + + % FIXME: this should be checked, but I only have a single BRIK file + % construct the homogenous transformation matrix that defines the axes + ft_warning('homogenous transformation might be incorrect for AFNI file'); + transform = eye(4); + transform(1:3,4) = hdr.ORIGIN(:); + transform(1,1) = hdr.DELTA(1); + transform(2,2) = hdr.DELTA(2); + transform(3,3) = hdr.DELTA(3); + + % FIXME: I am not sure about the "RAI" image orientation + img = flipdim(img,1); + img = flipdim(img,2); + dim = size(img); + transform(1,4) = -dim(1) - transform(1,4); + transform(2,4) = -dim(2) - transform(2,4); + + case 'neuromag_fif' + % needs mne toolbox + ft_hastoolbox('mne', 1); + + % use the mne functions to read the Neuromag MRI + hdr = fiff_read_mri(filename); + img_t = cat(3, hdr.slices.data); + img = permute(img_t,[2 1 3]); + hdr.slices = rmfield(hdr.slices, 'data'); % remove the image data to save memory + + % information below is from MNE - fiff_define_constants.m + % coordinate system 4 - is the MEG head coordinate system (fiducials) + % coordinate system 5 - is the MRI coordinate system + % coordinate system 2001 - MRI voxel coordinates + % coordinate system 2002 - Surface RAS coordinates (is mainly vertical + % shift, no rotation to 2001) + % MEG sensor positions come in system 4 + % MRI comes in system 2001 + + transform = eye(4); + if isfield(hdr, 'trans') && issubfield(hdr.trans, 'trans') + if (hdr.trans.from == 4) && (hdr.trans.to == 5) + transform = hdr.trans.trans; + else + ft_warning('W: trans does not transform from 4 to 5.'); + ft_warning('W: Please check the MRI fif-file'); + end else - ft_warning('W: trans does not transform from 4 to 5.'); - ft_warning('W: Please check the MRI fif-file'); + ft_warning('W: trans structure is not defined.'); + ft_warning('W: Maybe coregistration is missing?'); end - else - ft_warning('W: trans structure is not defined.'); - ft_warning('W: Maybe coregistration is missing?'); - end - if isfield(hdr, 'voxel_trans') && issubfield(hdr.voxel_trans, 'trans') - % centers the coordinate system - % and switches from mm to m - if (hdr.voxel_trans.from == 2001) && (hdr.voxel_trans.to == 5) - % matlab_shift compensates for the different index conventions - % between C and matlab - - % the lines below is old code (prior to Jan 3, 2013) and only works with - % 1 mm resolution MRIs - %matlab_shift = [ 0 0 0 0.001; 0 0 0 -0.001; 0 0 0 0.001; 0 0 0 0]; - % transform transforms from 2001 to 5 and further to 4 - %transform = transform\(hdr.voxel_trans.trans+matlab_shift); - - % the lines below should work with arbitrary resolution - matlab_shift = eye(4); - matlab_shift(1:3,4) = [-1,-1,-1]; - transform = transform\(hdr.voxel_trans.trans * matlab_shift); - - coordsys = 'neuromag'; - mri.unit = 'm'; + if isfield(hdr, 'voxel_trans') && issubfield(hdr.voxel_trans, 'trans') + % centers the coordinate system + % and switches from mm to m + if (hdr.voxel_trans.from == 2001) && (hdr.voxel_trans.to == 5) + % matlab_shift compensates for the different index conventions + % between C and matlab + + % the lines below is old code (prior to Jan 3, 2013) and only works with + % 1 mm resolution MRIs + %matlab_shift = [ 0 0 0 0.001; 0 0 0 -0.001; 0 0 0 0.001; 0 0 0 0]; + % transform transforms from 2001 to 5 and further to 4 + %transform = transform\(hdr.voxel_trans.trans+matlab_shift); + + % the lines below should work with arbitrary resolution + matlab_shift = eye(4); + matlab_shift(1:3,4) = [-1,-1,-1]; + transform = transform\(hdr.voxel_trans.trans * matlab_shift); + + coordsys = 'neuromag'; + mri.unit = 'm'; + else + ft_warning('W: voxel_trans does not transform from 2001 to 5.'); + ft_warning('W: Please check the MRI fif-file'); + end else - ft_warning('W: voxel_trans does not transform from 2001 to 5.'); + ft_warning('W: voxel_trans structure is not defined.'); ft_warning('W: Please check the MRI fif-file'); end - else - ft_warning('W: voxel_trans structure is not defined.'); - ft_warning('W: Please check the MRI fif-file'); - end - -case 'neuromag_fif_old' - % needs meg_pd toolbox - ft_hastoolbox('meg-pd', 1); - - % use the meg_pd functions to read the Neuromag MRI - [img,coords] = loadmri(filename); - dev = loadtrans(filename,'MRI','HEAD'); - transform = dev*coords; - hdr.coords = coords; - hdr.dev = dev; - -case 'dicom' - % this seems to return a right-handed volume with the transformation - % matrix stored in the file headers. - - % needs the freesurfer toolbox - ft_hastoolbox('freesurfer', 1); - [dcmdir,junk1,junk2] = fileparts(filename); - if isempty(dcmdir), - dcmdir = '.'; - end - [img,transform,hdr,mr_params] = load_dicom_series(dcmdir,dcmdir,filename); - transform = vox2ras_0to1(transform); - -case 'dicom_old' - % this does not necessarily return a right-handed volume and only a - % transformation-matrix with the voxel size - - % this uses the Image processing toolbox - % the DICOM file probably represents a stack of slices, possibly even multiple volumes - orig = dicominfo(filename); - dim(1) = orig.Rows; - dim(2) = orig.Columns; - - [p, f] = fileparts(filename); - - % this works for the Siemens scanners at the FCDC - tok = tokenize(f, '.'); - for i=5:length(tok) - tok{i} = '*'; - end - filename = sprintf('%s.', tok{:}); % reconstruct the filename with wildcards and '.' between the segments - filename = filename(1:end-1); % remove the last '.' - dirlist = dir(fullfile(p, filename)); - dirlist = {dirlist.name}; - - if isempty(dirlist) - % this is for the Philips data acquired at KI - ft_warning('could not determine list of dicom files, trying with *.dcm'); - dirlist = dir(fullfile(p, '*.dcm')); - dirlist = {dirlist.name}; - end - - if isempty(dirlist) - ft_warning('could not determine list of dicom files, trying with *.ima'); - dirlist = dir(fullfile(p, '*.ima')); + + case 'neuromag_fif_old' + % needs meg_pd toolbox + ft_hastoolbox('meg-pd', 1); + + % use the meg_pd functions to read the Neuromag MRI + [img,coords] = loadmri(filename); + dev = loadtrans(filename,'MRI','HEAD'); + transform = dev*coords; + hdr.coords = coords; + hdr.dev = dev; + + case 'dicom' + % this seems to return a right-handed volume with the transformation + % matrix stored in the file headers. + + % needs the freesurfer toolbox + ft_hastoolbox('freesurfer', 1); + [dcmdir,junk1,junk2] = fileparts(filename); + if isempty(dcmdir), + dcmdir = '.'; + end + [img,transform,hdr,mr_params] = load_dicom_series(dcmdir,dcmdir,filename); + transform = vox2ras_0to1(transform); + + case 'dicom_old' + % this does not necessarily return a right-handed volume and only a + % transformation-matrix with the voxel size + + % this uses the Image processing toolbox + % the DICOM file probably represents a stack of slices, possibly even multiple volumes + orig = dicominfo(filename); + dim(1) = orig.Rows; + dim(2) = orig.Columns; + + [p, f] = fileparts(filename); + + % this works for the Siemens scanners at the FCDC + tok = tokenize(f, '.'); + for i=5:length(tok) + tok{i} = '*'; + end + filename = sprintf('%s.', tok{:}); % reconstruct the filename with wildcards and '.' between the segments + filename = filename(1:end-1); % remove the last '.' + dirlist = dir(fullfile(p, filename)); dirlist = {dirlist.name}; - end - - if length(dirlist)==1 - % try something else to get a list of all the slices - dirlist = dir(fullfile(p, '*')); - dirlist = {dirlist(~[dirlist.isdir]).name}; - end - - keep = false(1, length(dirlist)); - for i=1:length(dirlist) - filename = char(fullfile(p, dirlist{i})); - if ~strcmp(dataformat, 'dicom_old') - keep(i) = false; - fprintf('skipping ''%s'' because of incorrect filetype\n', filename); + + if isempty(dirlist) + % this is for the Philips data acquired at KI + ft_warning('could not determine list of dicom files, trying with *.dcm'); + dirlist = dir(fullfile(p, '*.dcm')); + dirlist = {dirlist.name}; end - % read the header information - info = dicominfo(filename); - if info.SeriesNumber~=orig.SeriesNumber - keep(i) = false; - fprintf('skipping ''%s'' because of different SeriesNumber\n', filename); - else - keep(i) = true; - hdr(i) = info; + + if isempty(dirlist) + ft_warning('could not determine list of dicom files, trying with *.ima'); + dirlist = dir(fullfile(p, '*.ima')); + dirlist = {dirlist.name}; end - end - % remove the files that were skipped - hdr = hdr(keep); - dirlist = dirlist(keep); - - % pre-allocate enough space for the subsequent slices - dim(3) = length(dirlist); - img = zeros(dim(1), dim(2), dim(3)); - for i=1:length(dirlist) - filename = char(fullfile(p, dirlist{i})); - fprintf('reading image data from ''%s''\n', filename); - img(:,:,i) = dicomread(hdr(i)); - end - - % reorder the slices - [z, indx] = sort(cell2mat({hdr.SliceLocation})); - hdr = hdr(indx); - img = img(:,:,indx); - - try - % construct a homgeneous transformation matrix that performs the scaling from voxels to mm - dx = hdr(1).PixelSpacing(1); - dy = hdr(1).PixelSpacing(2); - dz = hdr(2).SliceLocation - hdr(1).SliceLocation; - transform = eye(4); - transform(1,1) = dx; - transform(2,2) = dy; - transform(3,3) = dz; - end - -case {'nifti', 'freesurfer_mgz', 'freesurfer_mgh', 'nifti_fsl'} - if strcmp(dataformat, 'freesurfer_mgz') && ispc - ft_error('Compressed .mgz files cannot be read on a PC'); - end - - ft_hastoolbox('freesurfer', 1); - tmp = MRIread(filename); - ndims = numel(size(tmp.vol)); - if ndims==3 - img = permute(tmp.vol, [2 1 3]); %FIXME although this is probably correct - %see the help of MRIread, anecdotally columns and rows seem to need a swap - %in order to match the transform matrix (alternatively a row switch of the - %latter can be done) - elseif ndims==4 - img = permute(tmp.vol, [2 1 3 4]); - end - hdr = rmfield(tmp, 'vol'); - transform = tmp.vox2ras1; - -case 'yokogawa_mri' - ft_hastoolbox('yokogawa', 1); - fid = fopen(filename, 'rb'); - mri_info = GetMeg160MriInfoM(fid); - patient_info = GetMeg160PatientInfoFromMriFileM(fid); - [data_style, model, marker, image_parameter, normalize, besa_fiducial_point] = GetMeg160MriFileHeaderInfoM(fid); - fclose(fid); - - % gather all meta-information - hdr.mri_info = mri_info; - hdr.patient_info = patient_info; - hdr.data_style = data_style; - hdr.model = model; - hdr.marker = marker; - hdr.image_parameter = image_parameter; - hdr.normalize = normalize; - hdr.besa_fiducial_point = besa_fiducial_point; - - ft_error('FIXME yokogawa_mri implementation is incomplete'); - -case 'matlab' - mri = loadvar(filename, 'mri'); - -otherwise - ft_error('unrecognized filetype ''%s'' for ''%s''', dataformat, filename); + + if length(dirlist)==1 + % try something else to get a list of all the slices + dirlist = dir(fullfile(p, '*')); + dirlist = {dirlist(~[dirlist.isdir]).name}; + end + + keep = false(1, length(dirlist)); + for i=1:length(dirlist) + filename = char(fullfile(p, dirlist{i})); + if ~strcmp(dataformat, 'dicom_old') + keep(i) = false; + fprintf('skipping ''%s'' because of incorrect filetype\n', filename); + end + % read the header information + info = dicominfo(filename); + if info.SeriesNumber~=orig.SeriesNumber + keep(i) = false; + fprintf('skipping ''%s'' because of different SeriesNumber\n', filename); + else + keep(i) = true; + hdr(i) = info; + end + end + % remove the files that were skipped + hdr = hdr(keep); + dirlist = dirlist(keep); + + % pre-allocate enough space for the subsequent slices + dim(3) = length(dirlist); + img = zeros(dim(1), dim(2), dim(3)); + for i=1:length(dirlist) + filename = char(fullfile(p, dirlist{i})); + fprintf('reading image data from ''%s''\n', filename); + img(:,:,i) = dicomread(hdr(i)); + end + + % reorder the slices + [z, indx] = sort(cell2mat({hdr.SliceLocation})); + hdr = hdr(indx); + img = img(:,:,indx); + + try + % construct a homgeneous transformation matrix that performs the scaling from voxels to mm + dx = hdr(1).PixelSpacing(1); + dy = hdr(1).PixelSpacing(2); + dz = hdr(2).SliceLocation - hdr(1).SliceLocation; + transform = eye(4); + transform(1,1) = dx; + transform(2,2) = dy; + transform(3,3) = dz; + end + + case {'nifti', 'freesurfer_mgz', 'freesurfer_mgh', 'nifti_fsl'} + if strcmp(dataformat, 'freesurfer_mgz') && ispc + ft_error('Compressed .mgz files cannot be read on a PC'); + end + + ft_hastoolbox('freesurfer', 1); + tmp = MRIread(filename); + ndims = numel(size(tmp.vol)); + if ndims==3 + img = permute(tmp.vol, [2 1 3]); %FIXME although this is probably correct + %see the help of MRIread, anecdotally columns and rows seem to need a swap + %in order to match the transform matrix (alternatively a row switch of the + %latter can be done) + elseif ndims==4 + img = permute(tmp.vol, [2 1 3 4]); + end + hdr = rmfield(tmp, 'vol'); + transform = tmp.vox2ras1; + + case 'yokogawa_mri' + ft_hastoolbox('yokogawa', 1); + fid = fopen(filename, 'rb'); + mri_info = GetMeg160MriInfoM(fid); + patient_info = GetMeg160PatientInfoFromMriFileM(fid); + [data_style, model, marker, image_parameter, normalize, besa_fiducial_point] = GetMeg160MriFileHeaderInfoM(fid); + fclose(fid); + + % gather all meta-information + hdr.mri_info = mri_info; + hdr.patient_info = patient_info; + hdr.data_style = data_style; + hdr.model = model; + hdr.marker = marker; + hdr.image_parameter = image_parameter; + hdr.normalize = normalize; + hdr.besa_fiducial_point = besa_fiducial_point; + + ft_error('FIXME yokogawa_mri implementation is incomplete'); + + case 'matlab' + mri = loadvar(filename, 'mri'); + + otherwise + ft_error('unrecognized filetype ''%s'' for ''%s''', dataformat, filename); end if exist('img', 'var') % set up the axes of the volume in voxel coordinates - nx = size(img,1); - ny = size(img,2); - nz = size(img,3); - mri.dim = [nx ny nz]; + %nx = size(img,1); + %ny = size(img,2); + %nz = size(img,3); + mri.dim = size(img);%[nx ny nz]; % store the anatomical data mri.anatomy = img; end diff --git a/external/fieldtrip/fileio/ft_read_sens.m b/external/fieldtrip/fileio/ft_read_sens.m index 7998167f..61077400 100644 --- a/external/fieldtrip/fileio/ft_read_sens.m +++ b/external/fieldtrip/fileio/ft_read_sens.m @@ -1,13 +1,7 @@ function [sens] = ft_read_sens(filename, varargin) -% FT_READ_SENS read sensor positions from various manufacturer specific files. The -% following acquisition system and analysis platform file formats are currently -% supported: -% -% asa_elc besa_elp besa_pos besa_sfp yokogawa_ave yokogawa_con yokogawa_raw 4d -% 4d_pdf 4d_m4d 4d_xyz ctf_ds ctf_res4 itab_raw itab_mhd netmeg neuromag_fif -% neuromag_mne neuromag_mne_elec neuromag_mne_grad polhemus_fil polhemus_pos -% zebris_sfp spmeeg_mat eeglab_set localite_pos matlab +% FT_READ_SENS read sensor positions from various manufacturer specific files. See +% further down for the list of file types that are supported. % % Use as % grad = ft_read_sens(filename, ...) % for gradiometers @@ -15,7 +9,7 @@ % % Additional options should be specified in key-value pairs and can be % 'fileformat' = string, see the list of supported file formats (the default is determined automatically) -% 'senstype' = string, can be 'eeg' or 'meg', specifies which type of sensors to read from a fif file (default = 'eeg') +% 'senstype' = string, can be 'eeg' or 'meg', specifies which type of sensors to read from the file (default = 'eeg') % 'coordsys' = string, 'head' or 'dewar' (default = 'head') % 'coilaccuracy' = can be empty or a number (0, 1 or 2) to specify the accuracy (default = []) % @@ -37,10 +31,18 @@ % grad.label = cell-array of length N with the label of each of the channels % grad.chanpos = Nx3 matrix with the positions of each sensor % +% Files from the following acquisition systems and analysis platforms file formats +% are supported. +% +% asa_elc besa_elp besa_pos besa_sfp yokogawa_ave yokogawa_con yokogawa_raw 4d +% 4d_pdf 4d_m4d 4d_xyz ctf_ds ctf_res4 itab_raw itab_mhd netmeg neuromag_fif +% neuromag_mne neuromag_mne_elec neuromag_mne_grad polhemus_fil polhemus_pos +% zebris_sfp spmeeg_mat eeglab_set localite_pos artiins_oxy3 matlab +% % See also FT_READ_HEADER, FT_TRANSFORM_SENS, FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD, % FT_DATATYPE_SENS -% Copyright (C) 2005-2016 Robert Oostenveld +% Copyright (C) 2005-2018 Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -63,17 +65,23 @@ % optionally get the data from the URL and make a temporary local copy filename = fetch_url(filename); -% test whether the file exists -if ~exist(filename, 'file') - ft_error('file ''%s'' does not exist', filename); -end - % get the options fileformat = ft_getopt(varargin, 'fileformat', ft_filetype(filename)); senstype = ft_getopt(varargin, 'senstype'); % can be eeg or meg, default is automatic when [] coordsys = ft_getopt(varargin, 'coordsys', 'head'); % this is used for ctf and neuromag_mne, it can be head or dewar coilaccuracy = ft_getopt(varargin, 'coilaccuracy'); % empty, or a number between 0 to 2 +realtime = any(strcmp(fileformat, {'fcdc_buffer', 'ctf_shm', 'fcdc_mysql'})); + +if realtime + % skip the rest of the initial checks to increase the speed for realtime operation +else + % test whether the file exists + if ~exist(filename, 'file') + ft_error('file ''%s'' does not exist', filename); + end +end + switch fileformat %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % read the content from various files that contain EEG electrode positions @@ -81,9 +89,14 @@ case 'asa_elc' sens = read_asa_elc(filename); + case 'artinis_oxy3' + ft_hastoolbox('artinis', 1); + hdr = read_artinis_oxy3(filename); + sens = hdr.opto; + case 'polhemus_pos' sens = read_brainvision_pos(filename); - + case 'besa_elp' ft_error('unknown fileformat for electrodes or gradiometers'); % the code below does not yet work @@ -98,7 +111,7 @@ r = ones(size(el)); [x, y, z] = sph2cart(az, el, r); sens.chanpos = [x y z]; - + case 'besa_pos' tmp = importdata(filename); if ~isnumeric(tmp) @@ -139,7 +152,7 @@ sens.label{i} = sprintf('%03d', i); end end - + case 'besa_sfh' sfh = readBESAsfh(filename); sens.label = sfh.SurfacePointsLabels(:); @@ -152,16 +165,16 @@ end sens.label = sens.label(sel); sens.elecpos = sens.elecpos(sel,:); - + case 'besa_sfp' [lab, pos] = read_besa_sfp(filename); sens.label = lab; sens.elecpos = pos; - + case 'bioimage_mgrid' sens = read_bioimage_mgrid(filename); - - case {'ctf_ds', 'ctf_res4', 'ctf_old', 'neuromag_fif', 'neuromag_mne', '4d', '4d_pdf', '4d_m4d', '4d_xyz', 'yokogawa_ave', 'yokogawa_con', 'yokogawa_raw', 'itab_raw' 'itab_mhd', 'netmeg'} + + case {'ctf_ds', 'ctf_res4', 'ctf_old', 'neuromag_fif', 'neuromag_mne', '4d', '4d_pdf', '4d_m4d', '4d_xyz', 'yokogawa_ave', 'yokogawa_con', 'yokogawa_raw', 'ricoh_ave', 'ricoh_con', 'itab_raw' 'itab_mhd', 'netmeg'} % gradiometer information is always stored in the header of the MEG dataset, hence uses the standard fieldtrip/fileio ft_read_header function hdr = ft_read_header(filename, 'headerformat', fileformat, 'coordsys', coordsys, 'coilaccuracy', coilaccuracy); % sometimes there can also be electrode position information in the header @@ -186,19 +199,42 @@ else ft_error('neither electrode nor gradiometer information is present'); end - + + case 'fcdc_buffer' + % the online header should have a binary blob with the sensor information + hdr = ft_read_header(filename, 'headerformat', fileformat); + if isfield(hdr, 'orig') && isfield(hdr.orig, 'neuromag_header') + hdr = decode_fif(hdr.orig.neuromag_header); + end + if isfield(hdr, 'orig') && isfield(hdr.orig, 'ctf_res4') + hdr = decode_res4(hdr.orig.ctf_res4); + end + if isempty(senstype) + % set the default + ft_warning('both electrode and gradiometer information is present, returning the electrode information by default'); + senstype = 'eeg'; + end + switch lower(senstype) + case 'eeg' + sens = hdr.elec; + case 'meg' + sens = hdr.grad; + otherwise + ft_error('incorrect specification of senstype'); + end + case 'neuromag_mne_grad' % the file can contain both, force reading the gradiometer info % note that this functionality overlaps with senstype=eeg/meg hdr = ft_read_header(filename, 'headerformat', 'neuromag_mne', 'coordsys', coordsys, 'coilaccuracy', coilaccuracy); sens = hdr.grad; - + case 'neuromag_mne_elec' % the file can contain both, force reading the electrode info % note that this functionality overlaps with senstype=eeg/meg hdr = ft_read_header(filename, 'headerformat', 'neuromag_mne', 'coordsys', coordsys, 'coilaccuracy', coilaccuracy); sens = hdr.elec; - + case {'spmeeg_mat', 'eeglab_set'} % this is for EEG formats where electrode positions can be stored with the data hdr = ft_read_header(filename, 'coordsys', coordsys, 'coilaccuracy', coilaccuracy); @@ -209,7 +245,7 @@ else ft_error('no electrodes or gradiometers found in the file') end - + case 'polhemus_fil' % these are created at the FIL in London with a polhemus tracker [sens.fid.pnt, sens.pnt, sens.fid.label] = read_polhemus_fil(filename, 0); @@ -218,7 +254,7 @@ for i=1:size(sens.pnt, 1) sens.label{i} = sprintf('%03d', i); end - + case 'matlab' % MATLAB files can contain all sensor arrays matfile = filename; % this solves a problem with the MATLAB compiler v3 @@ -231,14 +267,14 @@ % read whatever variable is in the file, this will error if the file contains multiple variables sens = loadvar(matfile); end - + case 'zebris_sfp' % these are created by a Zebris tracker, at CRC in Liege at least. [sens.fid.pnt, sens.chanpos, sens.fid.label, sens.label] = read_zebris(filename, 0); % convert to columns sens.label = sens.label(:); sens.fid.label = sens.fid.label(:); - + case '4d_el_ascii' fid = fopen(filename, 'rt'); c = textscan(fid, '%s%s%f%f%f'); @@ -269,53 +305,53 @@ sens.fid.label = l(sel); sens.fid.pnt = [x(sel) y(sel) z(sel)]; end - + case {'localite_pos','localite_ins'} if ~usejava('jvm') % Using xml2struct requires java fid = fopen(filename); - + % Read marker-file and store contents in cells of strings tmp = textscan(fid,'%s'); - + fclose(fid); - + % Search for cells that contain coordinates selx = strncmp('data0',tmp{1},5); sely = strncmp('data1',tmp{1},5); selz = strncmp('data2',tmp{1},5); sellab = strncmp('description',tmp{1},5); - + % Extract cells that contain coordinates xtemp = tmp{1}(selx); ytemp = tmp{1}(sely); ztemp = tmp{1}(selz); labtemp = tmp{1}(sellab); - + % Determine which channels are set. In localite channels that are not set % automatically receive coordinates [0, 0, 0] and should therefore % be discarded. settemp = tmp{1}(strncmp('set',tmp{1},3)); selset = strncmp('set="f',settemp,6); - + % Remove channels that are not set xtemp(selset) = []; ytemp(selset) = []; ztemp(selset) = []; labtemp(selset) = []; - + % Convert cells that contain coordinates from string to double x = []; y = []; z = []; lbl = []; - + for i=1:numel(xtemp) x(i,1) = str2double(xtemp{i}(8:end-1)); y(i,1) = str2double(ytemp{i}(8:end-1)); z(i,1) = str2double(ztemp{i}(8:end-3)); lbl{i,1} = labtemp{i}(14:end-1); end - + % Create and fill sens structure sens = []; sens.elecpos = [x y z]; @@ -323,9 +359,9 @@ sens.label = lbl; else tmp = xml2struct(filename); - + sens = []; - + % Loop through structure obtained from xml-file and store % coordinate information into sens structure of channels that are % set. @@ -337,16 +373,16 @@ sens.label{i} = tmp(i).Marker.description; end end - + sens.chanpos = sens.elecpos; end - + case 'easycap_txt' % Read the file and store all contents in cells of strings fid = fopen(filename); tmp = textscan(fid,'%s%s%s%s'); fclose(fid); - + sens = []; if all(cellfun(@isempty, tmp{4})) % it contains theta and phi @@ -370,7 +406,57 @@ sens.elecpos = [x y z]; sens.chanpos = [x y z]; end - + + case 'neuromag_iso' + ft_hastoolbox('mne', 1); + FIFF = fiff_define_constants(); + [fid, tree, dir] = fiff_open(filename); + isotrak = fiff_dir_tree_find(tree, FIFF.FIFFB_ISOTRAK); + sel = find([isotrak.dir.kind]==FIFF.FIFF_DIG_POINT); + sens = []; + sens.elecpos = nan(numel(sel),3); + sens.chanpos = nan(numel(sel),3); + coordsys = nan(numel(sel),1); + for i=sel + tag = fiff_read_tag(fid,isotrak.dir(i).pos); + sens.elecpos(i,:) = tag.data.r; + sens.chanpos(i,:) = tag.data.r; + coordsys(i) = tag.data.coord_frame; + end + fclose(fid); + + if all(coordsys==FIFF.FIFFV_COORD_DEVICE) + sens.coordsys = 'device'; + elseif all(coordsys==FIFF.FIFFV_COORD_ISOTRAK) + sens.coordsys = 'isotrak'; + elseif all(coordsys==FIFF.FIFFV_COORD_HPI) + sens.coordsys = 'hpi'; + elseif all(coordsys==FIFF.FIFFV_COORD_HEAD) + sens.coordsys = 'head'; + else + sens.coordsys = 'unknown'; + end + + ft_warning('creating fake channel names for neuromag_iso'); + for i=1:size(sens.chanpos,1) + sens.label{i} = sprintf('%d', i); + end + + case 'neuromag_cal' + dat = cell(1,14); + [dat{:}] = textread(filename, '%s%f%f%f%f%f%f%f%f%f%f%f%f%f'); + label = dat{1}; + x = dat{2}; + y = dat{3}; + z = dat{4}; + rot = cat(2, dat{5:13}); + cal = dat{14}; % ?? + % construct the sensor structucture + % it seems that the channel positions are expressed in dewar cordinates + % it would be possible to use coil_def.dat to construct the coil positions + sens.label = label; + sens.chanpos = [x y z]; + otherwise ft_error('unknown fileformat for electrodes or gradiometers'); end % switch fileformat diff --git a/external/fieldtrip/fileio/ft_read_spike.m b/external/fieldtrip/fileio/ft_read_spike.m index 7791a8aa..0577a1da 100644 --- a/external/fieldtrip/fileio/ft_read_spike.m +++ b/external/fieldtrip/fileio/ft_read_spike.m @@ -224,7 +224,7 @@ % x.spk.y (containing the waveform info) % x.fet.y (containing features: do we need this?) - if isdir(filename), + if isfolder(filename), tmp = dir(filename); filenames = {tmp.name}'; end diff --git a/external/fieldtrip/fileio/ft_read_vol.m b/external/fieldtrip/fileio/ft_read_vol.m index e4268b1d..9425fdea 100644 --- a/external/fieldtrip/fileio/ft_read_vol.m +++ b/external/fieldtrip/fileio/ft_read_vol.m @@ -15,7 +15,7 @@ % % See also FT_TRANSFORM_VOL, FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD -% Copyright (C) 2008-2013 Robert Oostenveld +% Copyright (C) 2008-2018 Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -50,18 +50,26 @@ case 'matlab' % FIXME in the future the file should contain the variable 'headmodel' instead of vol headmodel = loadvar(filename, 'vol'); - + case 'ctf_hdm' headmodel = read_ctf_hdm(filename); - + case 'asa_vol' headmodel = read_asa_vol(filename); headmodel.type = 'asa'; - + case 'mbfys_ama' ama = loadama(filename); headmodel = ama2vol(ama); - + + case 'neuromag_fif' + ft_hastoolbox('mne', 1); + global FIFF + bem = mne_read_bem_surfaces(filename); + headmodel.bnd.pos = bem.rr; + headmodel.bnd.tri = bem.tris; + headmodel.coordsys = fif2coordsys(bem.coord_frame); + otherwise ft_error('unknown fileformat for volume conductor model'); end @@ -69,3 +77,30 @@ % this will ensure that the structure is up to date, e.g. that the type is correct and that it has units headmodel = ft_datatype_headmodel(headmodel); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function coordsys = fif2coordsys(coord_frame) +ft_hastoolbox('mne', 1); +global FIFF + +switch coord_frame + case FIFF.FIFFV_COORD_DEVICE + coordsys = 'device'; + case FIFF.FIFFV_COORD_HPI + coordsys = 'hpi'; + case FIFF.FIFFV_COORD_HEAD + coordsys = 'head'; + case FIFF.FIFFV_COORD_MRI + coordsys = 'mri'; + case FIFF.FIFFV_COORD_MRI_SLICE + coordsys = 'mri_slice'; + case FIFF.FIFFV_COORD_MRI_DISPLAY + coordsys = 'mri_display'; + case FIFF.FIFFV_COORD_DICOM_DEVICE + coordsys = 'dicom_device'; + case FIFF.FIFFV_COORD_IMAGING_DEVICE + coordsys = 'imaging_device'; + otherwise + error('unrecognized coord_frame') +end diff --git a/external/fieldtrip/fileio/ft_write_data.m b/external/fieldtrip/fileio/ft_write_data.m index fe3b972c..fd2c7bbb 100644 --- a/external/fieldtrip/fileio/ft_write_data.m +++ b/external/fieldtrip/fileio/ft_write_data.m @@ -412,6 +412,13 @@ function ft_write_data(filename, dat, varargin) % file does not yet exist, which is not a problem end save(filename, 'dat', 'hdr'); + + case 'mff' + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % MFF files using Phillips plugin + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + ft_hastoolbox('mffmatlabio', 1); + mff_fileio_write(filename, hdr, dat, evt); case 'neuralynx_sdma' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -464,7 +471,7 @@ function ft_write_data(filename, dat, varargin) filename{i} = fullfile(dirname, [file '.' hdr.label{i} '.bin']); end - if ~isdir(dirname) + if ~isfolder(dirname) mkdir(dirname); end diff --git a/external/fieldtrip/fileio/ft_write_mri.m b/external/fieldtrip/fileio/ft_write_mri.m index 75e90af5..6f0bc175 100644 --- a/external/fieldtrip/fileio/ft_write_mri.m +++ b/external/fieldtrip/fileio/ft_write_mri.m @@ -4,23 +4,22 @@ % MRI to a file. % % Use as -% V = ft_write_mri(filename, dat, ...) +% ft_write_mri(filename, img, ...) +% where img represents the 3-D array with image values. % -% The specified filename can already contain the filename extention, -% but that is not required since it will be added automatically. +% The specified filename can already contain the filename extention, but that is not +% required since it will be added automatically. % % Additional options should be specified in key-value pairs and can be -% 'spmversion' spmversion to be used (in case data needs to be -% written in analyze format -% 'dataformat' string, see below -% 'transform' transformation matrix, specifying the transformation -% from voxel coordinates to head coordinates +% 'dataformat' = string, see below +% 'transform' = transformation matrix, specifying the transformation from voxel coordinates to head coordinates +% 'spmversion' = version of SPM to be used, in case data needs to be written in analyze format % % The supported dataformats are -% analyze -% nifti -% vista -% mgz (freesurfer) +% 'analyze' +% 'nifti' +% 'vista' +% 'mgz' (freesurfer) % % See also FT_READ_MRI, FT_WRITE_DATA, FT_WRITE_HEADSHAPE @@ -49,6 +48,12 @@ spmversion = ft_getopt(varargin, 'spmversion', 'SPM8'); dataformat = ft_getopt(varargin, 'dataformat'); % FIXME this is inconsistent with ft_read_mri, which uses 'format' +if isstruct(dat) && isfield(dat, 'anatomy') && isequal(transform, eye(4)) + % this is an anatomical MRI as returned by FT_READ_MRI + transform = dat.transform; + dat = dat.anatomy; +end + if isempty(dataformat) % only do the autodetection if the format was not specified dataformat = ft_filetype(filename); diff --git a/external/fieldtrip/fileio/private/biopac_acq.m b/external/fieldtrip/fileio/private/biopac_acq.m new file mode 100644 index 00000000..4ecb5774 --- /dev/null +++ b/external/fieldtrip/fileio/private/biopac_acq.m @@ -0,0 +1,71 @@ +function varargout = biopac_acq(filename, hdr, begsample, endsample, chanindx) + +% BIOPAC_ACQ is a wrapper to plug in the reading function from Mathworks file exchange. +% +% Use as +% hdr = biopac_acq(filename); +% dat = biopac_acq(filename, hdr, begsample, endsample, chanindx); +% +% See also FT_FILETYPE, FT_READ_HEADER, FT_READ_DATA, FT_READ_EVENT + +% Copyright (C) 2018 Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +persistent acq previous_fullname + +% use the full filename including path to distinguish between similarly named files in different directories +fullname = which(filename); + +if isempty(previous_fullname) || ~isequal(fullname, previous_fullname) + % remember the full filename including path + previous_fullname = fullname; + + % add fieldtrip/external/fileexchange to the path + ft_hastoolbox('fileexchange', 1); + + % read the header and data + acq = load_acq(filename, false); +else + % use the persistent variable to speed up subsequent read operations +end + +if nargin==1 + % convert to FieldTrip header representation + hdr.Fs = 1000/acq.hdr.graph.sample_time; + hdr.nChans = size(acq.data,2); + hdr.nSamples = size(acq.data,1); + hdr.nSamplesPre = 0; + hdr.nTrials = 1; % assume that it is a single continuous segment + for i=1:numel(acq.hdr.per_chan_data) + hdr.label{i} = acq.hdr.per_chan_data(i).comment_text; + end + hdr.orig = acq.hdr; + varargout{1} = hdr; + +elseif nargin==2 + % convert to FieldTrip event representation + event = []; + varargout{1} = event; + +else + % select the requested channels and samples from the data and transpose + dat = acq.data(begsample:endsample,chanindx)'; + varargout{1} = dat; +end diff --git a/external/fieldtrip/fileio/private/channelposition.m b/external/fieldtrip/fileio/private/channelposition.m index 1eecb56a..2952c3f2 100644 --- a/external/fieldtrip/fileio/private/channelposition.m +++ b/external/fieldtrip/fileio/private/channelposition.m @@ -5,11 +5,11 @@ % % Use as % [pos, ori, lab] = channelposition(sens) -% where sens is an electrode or gradiometer array. +% where sens is an electrode, gradiometer or optode array. % % See also FT_DATATYPE_SENS -% Copyright (C) 2009-2012, Robert Oostenveld & Vladimir Litvak +% Copyright (C) 2009-2018, Robert Oostenveld & Vladimir Litvak % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -49,6 +49,8 @@ sens.coilori = nan(size(sens.coilpos)); elseif ~isfield(sens, 'coilori') && isfield(sens, 'elecpos') sens.coilori = nan(size(sens.elecpos)); +elseif ~isfield(sens, 'coilori') && isfield(sens, 'optopos') + sens.coilori = nan(size(sens.optopos)); end switch ft_senstype(sens) @@ -261,6 +263,8 @@ nelec = size(sens.elecpos,1); % these are the electrodes elseif isfield(sens, 'coilpos') ncoil = size(sens.coilpos,1); % these are the coils + elseif isfield(sens, 'optopos') + nopto = size(sens.optopos,1); % these are the optodes end if ~isfield(sens, 'tra') && isfield(sens, 'elecpos') && nchan==nelec @@ -286,11 +290,18 @@ pnt = sens.coilpos; ori = sens.coilori; lab = sens.label; + + elseif ~isfield(sens, 'tra') && isfield(sens, 'optopos') && nchan==nopto + % there is one optode per channel, which means that the channel position is identical to the coil position + pnt = sens.optopos; + ori = nan(size(pnt)); + lab = sens.label; elseif isfield(sens, 'tra') % each channel depends on multiple sensors (electrodes or coils), compute a weighted position for the channel % for MEG gradiometer channels this means that the position is in between the two coils % for bipolar EEG channels this means that the position is in between the two electrodes + % for NIRS channels this means that the position is in between the transmit and receive optode pnt = nan(nchan,3); ori = nan(nchan,3); if isfield(sens, 'coilpos') @@ -306,14 +317,18 @@ weight = weight ./ sum(weight); pnt(i,:) = weight * sens.elecpos; end + elseif isfield(sens, 'optopos') + for i=1:nchan + weight = abs(sens.tra(i,:)); + weight = weight ./ sum(weight); + pnt(i,:) = weight * sens.optopos; + end end lab = sens.label; - end - end % switch senstype -n = size(lab,2); +n = size(lab,2); % this is to fix the planar layouts, which cannot be plotted anyway if n>1 && size(lab, 1)>1 % this is to prevent confusion when lab happens to be a row array pnt = repmat(pnt, n, 1); diff --git a/external/fieldtrip/fileio/private/dataset2files.m b/external/fieldtrip/fileio/private/dataset2files.m index fae0883f..9093c846 100644 --- a/external/fieldtrip/fileio/private/dataset2files.m +++ b/external/fieldtrip/fileio/private/dataset2files.m @@ -129,7 +129,7 @@ headerfile = fullfile(path, [file '.mat']); datafile = fullfile(path, [file '.bin']); case 'fcdc_buffer_offline' - if isdir(filename) + if isfolder(filename) path = filename; else [path, file, ext] = fileparts(filename); @@ -141,7 +141,7 @@ headerfile = fullfile(path, [file '.tsq']); datafile = fullfile(path, [file '.tev']); case 'egi_mff' - if ~isdir(filename); + if ~isfolder(filename); [path, file, ext] = fileparts(filename); headerfile = path; datafile = path; diff --git a/external/fieldtrip/fileio/private/decode_res4.m b/external/fieldtrip/fileio/private/decode_res4.m index 4a19f128..e974c207 100644 --- a/external/fieldtrip/fileio/private/decode_res4.m +++ b/external/fieldtrip/fileio/private/decode_res4.m @@ -1,10 +1,12 @@ -function R4F = decode_res4(chunk) +function R4F = decode_res4(chunk, varargin) % DECODE_RES4 is a helper function for real-time processing of CTF data. This % function is used to decode the content of the optional ctf_res4 chunck. % % See also DECODE_FIF, DECODE_NIFTI1, SAP2MATLAB +coordsys = ft_getopt(varargin, 'coordsys', 'dewar'); + % decode the res4 file content if 0 % using READ_CTF_RES4 -- this does not produce a proper .grad structure @@ -34,7 +36,7 @@ fwrite(F, 'MEG42CP'); fclose(F); - R4F = ft_read_header(dsname, 'coordsys', 'dewar'); + R4F = ft_read_header(dsname, 'coordsys', coordsys); delete(res4fn); delete(meg4fn); diff --git a/external/fieldtrip/fileio/private/defaultId.m b/external/fieldtrip/fileio/private/defaultId.m index f4e1ee99..e7569675 100644 --- a/external/fieldtrip/fileio/private/defaultId.m +++ b/external/fieldtrip/fileio/private/defaultId.m @@ -40,7 +40,9 @@ % remove the non-FieldTrip functions from the path, these should not be part of the default message identifier keep = true(size(stack)); -[v, p] = ft_version; +p = fileparts(mfilename('fullpath')); +% strip away '/utilities/private' where this function is located +p = p(1:end-18); for i=1:numel(stack) keep(i) = strncmp(p, stack(i).file, length(p)); end diff --git a/external/fieldtrip/fileio/private/events_tsv.m b/external/fieldtrip/fileio/private/events_tsv.m new file mode 100644 index 00000000..b390ea65 --- /dev/null +++ b/external/fieldtrip/fileio/private/events_tsv.m @@ -0,0 +1,76 @@ +function event = events_tsv(filename) + +% helper function to extract events from a BIDS events.tsv file + +opts = detectImportOptions(filename,'filetype','text'); + +% this gets the numeric stuff as doubles right away, yet might erroneously +% typecast intended strings (e.g. in event_value) into doubles. +tsv = readtable(filename, opts); +tsv_naive = readtable(filename, 'filetype', 'text'); + +tsv = table2struct(tsv); +tsv_naive = table2struct(tsv_naive); + +fn = fieldnames(tsv); +checkflag = false(1,numel(fn)); +for k = 1:numel(fn) + checkflag(1,k) = ~ischar(tsv(1).(fn{k})) && ischar(tsv_naive(1).(fn{k})); +end + +% heuristic: the element can stay double if all elements of the corresponding +% element in tsv_naive contain only numbers, or nan +for k = find(checkflag) + for m = 1:numel(tsv_naive) + if ~is_a_number(tsv_naive(m).(fn{k})) && ~is_a_nan(tsv_naive(m).(fn{k})) + tsv(m).(fn{k}) = tsv_naive(m).(fn{k}); + end + end +end + +% a FieldTrip event structure should contain +% type, value, sample, offset and duration as fields +% +% a BIDS event table contains +% event_type, event_value + +event = tsv; +%event = struct([]); +%event = copyfields(tsv, event, {'type' 'value' 'sample' 'offset' 'duration'}); +if ~isempty(event) + if ~isfield(event, 'type') + try, + for k = 1:numel(event) + event(k).type = tsv(k).event_type; + end + end + end + if ~isfield(event, 'value') + try, + for k = 1:numel(event) + event(k).value = tsv(k).event_value; + end + end + end + if ~isfield(event, 'offset') + event(1).offset = []; + end + if ~isfield(event, 'sample') + event(1).sample = []; + end + if ~isfield(event, 'duration') + event(1).duration = []; + end +end + + +function bool = is_a_number(input) + +pattern = '[0-9\.]'; +bool = length(regexp(input, pattern))==length(input); + +function bool = is_a_nan(input) + +pattern = 'nan'; +bool = ~isempty(regexp(lower(input), pattern)); + diff --git a/external/fieldtrip/fileio/private/filetype_check_header.m b/external/fieldtrip/fileio/private/filetype_check_header.m index ea211f93..4e963b0b 100644 --- a/external/fieldtrip/fileio/private/filetype_check_header.m +++ b/external/fieldtrip/fileio/private/filetype_check_header.m @@ -51,7 +51,7 @@ for i=1:length(filename) val(i) = filetype_check_header(filename{i}, head, offset); end -elseif isdir(filename) +elseif isfolder(filename) % a directory cannot have a header val = false; elseif ~exist(filename, 'file') diff --git a/external/fieldtrip/fileio/private/fixsampleinfo.m b/external/fieldtrip/fileio/private/fixsampleinfo.m index cc29030d..9bed0c8a 100644 --- a/external/fieldtrip/fileio/private/fixsampleinfo.m +++ b/external/fieldtrip/fileio/private/fixsampleinfo.m @@ -23,24 +23,31 @@ % check whether we're dealing with a timelock structure that has trials istimelock = hastime && hastrial && ~iscell(data.trial) && ~iscell(data.time); +israw = hastime && hastrial && iscell(data.trial) && iscell(data.time); + +% if the data does not have repetitions (i.e. trials) then it does not make sense to keep the sampleinfo +if ~hastrial && isfield(data, 'sampleinfo') + data = rmfield(data, 'sampleinfo'); + return +end if ~hasfsample && hastime - if istimelock - data.fsample = median(1./diff(data.time)); - else + if israw data.fsample = median(1./diff(data.time{1})); + elseif hastime + data.fsample = median(1./diff(data.time)); end end if hastrial - if istimelock - ntrial = size(data.trial,1); - else + if israw ntrial = numel(data.trial); + elseif istimelock + ntrial = size(data.trial,1); end else ntrial = dimlength(data, 'rpt'); - if ~isfinite(ntrial) && strcmp(data.dimord(1:6), 'rpttap') && isfield(data, 'cumtapcnt'), + if ~isfinite(ntrial) && strcmp(data.dimord(1:6), 'rpttap') && isfield(data, 'cumtapcnt') ntrial = numel(data.cumtapcnt); elseif ~isfinite(ntrial) ntrial = 1; @@ -49,17 +56,24 @@ trl = ft_findcfg(data.cfg, 'trl'); -if istimelock - nsmp = ones(ntrial,1) .* size(data.trial,3); -else +if istable(trl) + % the subsequent code requires this to be an array + trl = table2array(trl(:,1:3)); +end + +if israw nsmp = zeros(ntrial,1); - if hastrial + if israw for i=1:ntrial nsmp(i) = size(data.trial{i}, 2); end elseif ~isempty(trl) nsmp = trl(:,2) - trl(:,1) + 1; end +elseif istimelock + nsmp = ones(ntrial,1) .* size(data.trial,3); +elseif hastime + nsmp = ones(ntrial,1) .* length(data.time); end if isempty(trl) @@ -77,24 +91,25 @@ if isempty(trl) || ~all(nsmp==trl(:,2)-trl(:,1)+1) ft_warning('reconstructing sampleinfo by assuming that the trials are consecutive segments of a continuous recording'); + dbstack % construct a trial definition on the fly, assume that the trials are % consecutive segments of a continuous recording - if ntrial==1, + if ntrial==1 begsample = 1; else begsample = cat(1, 0, cumsum(nsmp(1:end-1))) + 1; end endsample = begsample + nsmp - 1; - - if istimelock - offset = ones(ntrial,1) .* time2offset(data.time, data.fsample); - else - offset = zeros(ntrial,1); - if hastime, + + if israw + offset = zeros(ntrial,1); + if hastime for i=1:ntrial offset(i) = time2offset(data.time{i}, data.fsample); end end + elseif hastime + offset = ones(ntrial,1) .* time2offset(data.time, data.fsample); end trl = [begsample endsample offset]; end @@ -106,11 +121,6 @@ ft_warning('failed to create sampleinfo field'); end -if (~isfield(data, 'trialinfo') || isempty(data.trialinfo)) && ~isempty(trl) && size(trl, 2) > 3, +if (~isfield(data, 'trialinfo') || isempty(data.trialinfo)) && ~isempty(trl) && size(trl, 2) > 3 data.trialinfo = trl(:, 4:end); end - -% if data does not have repetitions (i.e. trials) then it does not make sense to keep the sampleinfo -if ~hastrial && isfield(data, 'sampleinfo') - data = rmfield(data, 'sampleinfo'); -end diff --git a/external/fieldtrip/fileio/private/ft_apply_montage.m b/external/fieldtrip/fileio/private/ft_apply_montage.m index 5262952b..63f9cab0 100644 --- a/external/fieldtrip/fileio/private/ft_apply_montage.m +++ b/external/fieldtrip/fileio/private/ft_apply_montage.m @@ -39,6 +39,7 @@ % 'balancename' string, name of the montage (default = '') % 'feedback' string, see FT_PROGRESS (default = 'text') % 'warning' boolean, whether to show warnings (default = true) +% 'showcallinfo' string, 'yes' or 'no' (default = 'no') % % If the first input is a montage, then the second input montage will be % applied to the first. In effect, the output montage will first do @@ -79,11 +80,12 @@ input = fixoldorg(input); % the input might be a montage or a sensor array % get optional input arguments -keepunused = ft_getopt(varargin, 'keepunused', 'no'); -inverse = ft_getopt(varargin, 'inverse', 'no'); -feedback = ft_getopt(varargin, 'feedback', 'text'); -showwarning = ft_getopt(varargin, 'warning', true); -bname = ft_getopt(varargin, 'balancename', ''); +keepunused = ft_getopt(varargin, 'keepunused', 'no'); +inverse = ft_getopt(varargin, 'inverse', 'no'); +feedback = ft_getopt(varargin, 'feedback', 'text'); +showwarning = ft_getopt(varargin, 'warning', true); +bname = ft_getopt(varargin, 'balancename', ''); +showcallinfo = ft_getopt(varargin, 'showcallinfo', 'no'); if istrue(showwarning) warningfun = @warning; @@ -254,6 +256,7 @@ if k > 0 && isfield(input, 'trial') % check for raw data now only cfg = []; cfg.channel = addlabel; + cfg.showcallinfo = showcallinfo; data_unused = ft_selectdata(cfg, input); % use an anonymous function to test for the presence of NaNs in the input data hasnan = @(x) any(isnan(x(:))); diff --git a/external/fieldtrip/fileio/private/ft_checkdata.m b/external/fieldtrip/fileio/private/ft_checkdata.m index d0ee2d49..92ff6c75 100644 --- a/external/fieldtrip/fileio/private/ft_checkdata.m +++ b/external/fieldtrip/fileio/private/ft_checkdata.m @@ -23,7 +23,7 @@ % isnirs = yes, no % hasunit = yes, no % hascoordsys = yes, no -% hassampleinfo = yes, no, ifmakessense (only applies to raw data) +% hassampleinfo = yes, no, ifmakessense (applies to raw and timelock data) % hascumtapcnt = yes, no (only applies to freq data) % hasdim = yes, no % hasdof = yes, no @@ -32,6 +32,7 @@ % segmentationstyle = indexed, probabilistic (only applies to segmentation) % parcellationstyle = indexed, probabilistic (only applies to parcellation) % hasbrain = yes, no (only applies to segmentation) +% trialinfostyle = matrix, table or empty % % For some options you can specify multiple values, e.g. % [data] = ft_checkdata(data, 'senstype', {'ctf151', 'ctf275'}), e.g. in megrealign @@ -112,6 +113,7 @@ segmentationstyle = ft_getopt(varargin, 'segmentationstyle'); % this will be passed on to the corresponding ft_datatype_xxx function parcellationstyle = ft_getopt(varargin, 'parcellationstyle'); % this will be passed on to the corresponding ft_datatype_xxx function hasbrain = ft_getopt(varargin, 'hasbrain'); +trialinfostyle = ft_getopt(varargin, 'trialinfostyle'); % check whether people are using deprecated stuff depHastrialdef = ft_getopt(varargin, 'hastrialdef'); @@ -240,6 +242,20 @@ issource = false; end +if isfield(data, 'trialinfo') + if strcmp(trialinfostyle, 'table') + if ismatrix(data.trialinfo) + data.trialinfo = array2table(data.trialinfo); + end + elseif strcmp(trialinfostyle, 'matrix') + if istable(data.trialinfo) + data.trialinfo = table2array(data.trialinfo); + end + else + % no conversion is needed + end +end + % the ft_datatype_XXX functions ensures the consistency of the XXX datatype % and provides a detailed description of the dataformat and its history if iscomp % this should go before israw/istimelock/isfreq @@ -247,7 +263,7 @@ elseif israw data = ft_datatype_raw(data, 'hassampleinfo', hassampleinfo); elseif istimelock - data = ft_datatype_timelock(data); + data = ft_datatype_timelock(data, 'hassampleinfo', hassampleinfo); elseif isfreq data = ft_datatype_freq(data); elseif isspike @@ -381,7 +397,7 @@ okflag = 1; elseif isequal(dtype(iCell), {'timelock+comp'}) && israw && iscomp data = raw2timelock(data); - data = ft_datatype_timelock(data); + data = ft_datatype_timelock(data, 'hassampleinfo', hassampleinfo); istimelock = 1; iscomp = 1; israw = 0; @@ -394,7 +410,7 @@ okflag = 1; elseif isequal(dtype(iCell), {'raw'}) && istimelock if iscomp - data = removefields(data, {'topo', 'topolabel', 'unmixing'}); % these fields are not desired + data = removefields(data, {'topo', 'topolabel', 'topodimord', 'unmixing', 'unmixingdimord'}); % these fields are not desired iscomp = 0; end data = timelock2raw(data); @@ -404,56 +420,56 @@ okflag = 1; elseif isequal(dtype(iCell), {'comp'}) && israw && iscomp data = keepfields(data, {'label', 'topo', 'topolabel', 'unmixing', 'elec', 'grad', 'cfg'}); % these are the only relevant fields - data = ft_datatype_comp(data); + data = ft_datatype_comp(data, 'hassampleinfo', hassampleinfo); israw = 0; iscomp = 1; okflag = 1; elseif isequal(dtype(iCell), {'comp'}) && istimelock && iscomp data = keepfields(data, {'label', 'topo', 'topolabel', 'unmixing', 'elec', 'grad', 'cfg'}); % these are the only relevant fields - data = ft_datatype_comp(data); + data = ft_datatype_comp(data, 'hassampleinfo', hassampleinfo); istimelock = 0; iscomp = 1; okflag = 1; elseif isequal(dtype(iCell), {'comp'}) && isfreq && iscomp data = keepfields(data, {'label', 'topo', 'topolabel', 'unmixing', 'elec', 'grad', 'cfg'}); % these are the only relevant fields - data = ft_datatype_comp(data); + data = ft_datatype_comp(data, 'hassampleinfo', 'no'); % freq data does not have sampleinfo isfreq = 0; iscomp = 1; okflag = 1; elseif isequal(dtype(iCell), {'raw'}) && israw if iscomp - data = removefields(data, {'topo', 'topolabel', 'unmixing'}); % these fields are not desired + data = removefields(data, {'topo', 'topolabel', 'topodimord', 'unmixing', 'unmixingdimord'}); % these fields are not desired iscomp = 0; end - data = ft_datatype_raw(data); + data = ft_datatype_raw(data, 'hassampleinfo', hassampleinfo); okflag = 1; elseif isequal(dtype(iCell), {'timelock'}) && istimelock if iscomp - data = removefields(data, {'topo', 'topolabel', 'unmixing'}); % these fields are not desired + data = removefields(data, {'topo', 'topolabel', 'topodimord', 'unmixing', 'unmixingdimord'}); % these fields are not desired iscomp = 0; end - data = ft_datatype_timelock(data); + data = ft_datatype_timelock(data, 'hassampleinfo', hassampleinfo); okflag = 1; elseif isequal(dtype(iCell), {'freq'}) && isfreq if iscomp - data = removefields(data, {'topo', 'topolabel', 'unmixing'}); % these fields are not desired + data = removefields(data, {'topo', 'topolabel', 'topodimord', 'unmixing', 'unmixingdimord'}); % these fields are not desired iscomp = 0; end data = ft_datatype_freq(data); okflag = 1; elseif isequal(dtype(iCell), {'timelock'}) && israw if iscomp - data = removefields(data, {'topo', 'topolabel', 'unmixing'}); % these fields are not desired + data = removefields(data, {'topo', 'topolabel', 'topodimord', 'unmixing', 'unmixingdimord'}); % these fields are not desired iscomp = 0; end data = raw2timelock(data); - data = ft_datatype_timelock(data); + data = ft_datatype_timelock(data, 'hassampleinfo', hassampleinfo); israw = 0; istimelock = 1; okflag = 1; elseif isequal(dtype(iCell), {'raw'}) && isfreq if iscomp - data = removefields(data, {'topo', 'topolabel', 'unmixing'}); % these fields are not desired + data = removefields(data, {'topo', 'topolabel', 'topodimord', 'unmixing', 'unmixingdimord'}); % these fields are not desired iscomp = 0; end data = freq2raw(data); @@ -465,13 +481,13 @@ elseif isequal(dtype(iCell), {'raw'}) && ischan data = chan2timelock(data); data = timelock2raw(data); - data = ft_datatype_raw(data); + data = ft_datatype_raw(data, 'hassampleinfo', hassampleinfo); ischan = 0; israw = 1; okflag = 1; elseif isequal(dtype(iCell), {'timelock'}) && ischan data = chan2timelock(data); - data = ft_datatype_timelock(data); + data = ft_datatype_timelock(data, 'hassampleinfo', hassampleinfo); ischan = 0; istimelock = 1; okflag = 1; @@ -571,9 +587,9 @@ if ~isempty(iseeg) if isequal(iseeg, 'yes') - okflag = isfield(data, 'grad'); + okflag = isfield(data, 'elec'); elseif isequal(iseeg, 'no') - okflag = ~isfield(data, 'grad'); + okflag = ~isfield(data, 'elec'); end if ~okflag && isequal(iseeg, 'yes') diff --git a/external/fieldtrip/fileio/private/ft_convert_units.m b/external/fieldtrip/fileio/private/ft_convert_units.m index 47279027..b1ac7b89 100644 --- a/external/fieldtrip/fileio/private/ft_convert_units.m +++ b/external/fieldtrip/fileio/private/ft_convert_units.m @@ -46,9 +46,6 @@ % 2) determine the requested scaling factor to obtain the output units % 3) try to apply the scaling to the known geometrical elements in the input object -% ensure the correct number of input and output arguments -% narginchk(2,inf); % see below -% nargoutchk(0,1); % the "target" input argument has been made required in Aug 2017 % prior to that it was also possible to use this function to estimate units diff --git a/external/fieldtrip/fileio/private/ft_datatype_comp.m b/external/fieldtrip/fileio/private/ft_datatype_comp.m index 15466a69..56a611eb 100644 --- a/external/fieldtrip/fileio/private/ft_datatype_comp.m +++ b/external/fieldtrip/fileio/private/ft_datatype_comp.m @@ -68,8 +68,8 @@ % get the optional input arguments, which should be specified as key-value pairs version = ft_getopt(varargin, 'version', 'latest'); -hassampleinfo = ft_getopt(varargin, 'hassampleinfo', []); % the default is determined in ft_datatype_raw -hastrialinfo = ft_getopt(varargin, 'hastrialinfo', []); % the default is determined in ft_datatype_raw +hassampleinfo = ft_getopt(varargin, 'hassampleinfo', []); % the default is determined elsewhere +hastrialinfo = ft_getopt(varargin, 'hastrialinfo', []); % the default is determined elsewhere if strcmp(version, 'latest') version = '2014'; diff --git a/external/fieldtrip/fileio/private/ft_datatype_freq.m b/external/fieldtrip/fileio/private/ft_datatype_freq.m index 77321874..464d3633 100644 --- a/external/fieldtrip/fileio/private/ft_datatype_freq.m +++ b/external/fieldtrip/fileio/private/ft_datatype_freq.m @@ -6,7 +6,7 @@ % channel-level data. This data structure is usually generated with the % FT_FREQANALYSIS function. % -% An example of a freq structure containing the powerspectrum for 306 channels +% An example of a freq data structure containing the powerspectrum for 306 channels % and 120 frequencies is % % dimord: 'chan_freq' defines how the numeric data should be interpreted @@ -15,7 +15,7 @@ % freq: [1x120 double] the frequencies expressed in Hz % cfg: [1x1 struct] the configuration used by the function that generated this data structure % -% An example of a freq structure containing the time-frequency resolved +% An example of a freq data structure containing the time-frequency resolved % spectral estimates of power (i.e. TFR) for 306 channels, 120 frequencies % and 60 timepoints is % @@ -38,11 +38,6 @@ % Obsoleted fields: % - % -% Historical fields: -% - cfg, crsspctrm, cumsumcnt, cumtapcnt, dimord, elec, foi, -% fourierspctrm, freq, grad, label, labelcmb, powspctrm, time, toi, see -% bug2513 -% % Revision history: % % (2011/latest) The description of the sensors has changed, see FT_DATATYPE_SENS @@ -113,16 +108,17 @@ switch version case '2011' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % ensure that the sensor structures are up to date if isfield(freq, 'grad') - % ensure that the gradiometer structure is up to date freq.grad = ft_datatype_sens(freq.grad); end - if isfield(freq, 'elec') - % ensure that the electrode structure is up to date freq.elec = ft_datatype_sens(freq.elec); end - + if isfield(freq, 'opto') + freq.opto = ft_datatype_sens(freq.opto); + end + if isfield(freq, 'foi') && ~isfield(freq, 'freq') % this was still the case in early 2006 freq.freq = freq.foi; diff --git a/external/fieldtrip/fileio/private/ft_datatype_mvar.m b/external/fieldtrip/fileio/private/ft_datatype_mvar.m index 3ddef362..8f4d18eb 100644 --- a/external/fieldtrip/fileio/private/ft_datatype_mvar.m +++ b/external/fieldtrip/fileio/private/ft_datatype_mvar.m @@ -7,8 +7,8 @@ % in the frequency-domain. This is usually obtained from FT_MVARANALYSIS, % optionally in combination with FT_FREQANALYSIS. % -% The following is an example of sensor level MVAR model data in the time -% domain +% The following is an example of sensor level MVAR model data in the time domain +% % dimord: 'chan_chan_lag' defines how the numeric data should be interpreted % label: {3x1 cell} the channel labels % coeffs: [3x3x5 double] numeric data (MVAR model coefficients 3 channels x 3 channels x 5 time lags) @@ -17,8 +17,8 @@ % fsampleorig: 200 % cfg: [1x1 struct] % -% The following is an example of sensor-level MVAR model data in the frequency -% domain +% The following is an example of sensor-level MVAR model data in the frequency domain +% % dimord: 'chan_chan_freq' defines how the numeric data should be interpreted % label: {3x1 cell} the channel labels % freq: [1x101 double] the frequencies, expressed in Hz @@ -86,15 +86,16 @@ switch version case '2011' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % ensure that the sensor structures are up to date if isfield(mvar, 'grad') - % ensure that the gradiometer structure is up to date mvar.grad = ft_datatype_sens(mvar.grad); end - if isfield(mvar, 'elec') - % ensure that the electrode structure is up to date mvar.elec = ft_datatype_sens(mvar.elec); end + if isfield(mvar, 'opto') + mvar.opto = ft_datatype_sens(mvar.opto); + end case '2008' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/fileio/private/ft_datatype_raw.m b/external/fieldtrip/fileio/private/ft_datatype_raw.m index 98940c87..08c181c5 100644 --- a/external/fieldtrip/fileio/private/ft_datatype_raw.m +++ b/external/fieldtrip/fileio/private/ft_datatype_raw.m @@ -16,13 +16,13 @@ % trialinfo: [266x1 double] optional trigger or condition codes for each trial % hdr: [1x1 struct] the full header information of the original dataset on disk % grad: [1x1 struct] information about the sensor array (for EEG it is called elec) -% cfg: [1x1 struct] the configuration used by the function that generated this data structure +% cfg: [1x1 struct] the configuration used by the function that generated this data structure % % Required fields: % - time, trial, label % % Optional fields: -% - sampleinfo, trialinfo, grad, elec, hdr, cfg +% - sampleinfo, trialinfo, grad, elec, opto, hdr, cfg % % Deprecated fields: % - fsample @@ -30,10 +30,6 @@ % Obsoleted fields: % - offset % -% Historical fields: -% - cfg, elec, fsample, grad, hdr, label, offset, sampleinfo, time, -% trial, trialdef, see bug2513 -% % Revision history: % % (2011/latest) The description of the sensors has changed, see FT_DATATYPE_SENS @@ -88,43 +84,18 @@ assert(size(data.trial{i},1)==length(data.label), 'inconsistent number of channels in trial %d', i); end +% convert it into true/false if isequal(hassampleinfo, 'ifmakessense') - hassampleinfo = 'no'; % default to not adding it - if isfield(data, 'sampleinfo') && size(data.sampleinfo,1)~=numel(data.trial) - % it does not make sense, so don't keep it - hassampleinfo = 'no'; - end - if isfield(data, 'sampleinfo') - hassampleinfo = 'yes'; % if it's already there, consider keeping it - numsmp = data.sampleinfo(:,2)-data.sampleinfo(:,1)+1; - for i=1:length(data.trial) - if size(data.trial{i},2)~=numsmp(i) - % it does not make sense, so don't keep it - hassampleinfo = 'no'; - % the actual removal will be done further down - ft_warning('removing inconsistent sampleinfo'); - break - end - end - end + hassampleinfo = makessense(data, 'sampleinfo'); +else + hassampleinfo = istrue(hassampleinfo); end - if isequal(hastrialinfo, 'ifmakessense') - hastrialinfo = 'no'; - if isfield(data, 'trialinfo') - hastrialinfo = 'yes'; - if size(data.trialinfo,1)~=numel(data.trial) - % it does not make sense, so don't keep it - hastrialinfo = 'no'; - ft_warning('removing inconsistent trialinfo'); - end - end + hastrialinfo = makessense(data, 'trialinfo'); +else + hastrialinfo = istrue(hastrialinfo); end -% convert it into true/false -hassampleinfo = istrue(hassampleinfo); -hastrialinfo = istrue(hastrialinfo); - if strcmp(version, 'latest') version = '2011'; end @@ -136,15 +107,16 @@ switch version case '2011' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % ensure that the sensor structures are up to date if isfield(data, 'grad') - % ensure that the gradiometer structure is up to date data.grad = ft_datatype_sens(data.grad); end - if isfield(data, 'elec') - % ensure that the electrode structure is up to date data.elec = ft_datatype_sens(data.elec); end + if isfield(data, 'opto') + data.opto = ft_datatype_sens(data.opto); + end if ~isfield(data, 'fsample') for i=1:length(data.time) @@ -272,12 +244,14 @@ fsample = data.fsample; end -begtime = zeros(1, length(data.time)); -endtime = zeros(1, length(data.time)); +begtime = nan(1, length(data.time)); +endtime = nan(1, length(data.time)); numsample = zeros(1, length(data.time)); for i=1:length(data.time) - begtime(i) = data.time{i}(1); - endtime(i) = data.time{i}(end); + if ~isempty(data.time{i}) + begtime(i) = data.time{i}(1); + endtime(i) = data.time{i}(end); + end numsample(i) = length(data.time{i}); end diff --git a/external/fieldtrip/fileio/private/ft_datatype_sens.m b/external/fieldtrip/fileio/private/ft_datatype_sens.m index a3c1d2ec..b0c304be 100644 --- a/external/fieldtrip/fileio/private/ft_datatype_sens.m +++ b/external/fieldtrip/fileio/private/ft_datatype_sens.m @@ -161,7 +161,8 @@ sens = fixoldorg(sens, false); % ensure that all numbers are represented in double precision - sens = ft_struct2double(sens); + % this only affects the top-level fields and does not recurse + sens = ft_struct2double(sens, 1); % in version 2011v2 this was optional, now it is required if ~isfield(sens, 'chantype') || all(strcmp(sens.chantype, 'unknown')) diff --git a/external/fieldtrip/fileio/private/ft_datatype_source.m b/external/fieldtrip/fileio/private/ft_datatype_source.m index f1ac33ba..afbac44f 100644 --- a/external/fieldtrip/fileio/private/ft_datatype_source.m +++ b/external/fieldtrip/fileio/private/ft_datatype_source.m @@ -31,10 +31,6 @@ % Obsoleted fields: % - xgrid, ygrid, zgrid, transform, latency, frequency % -% Historical fields: -% - avg, cfg, cumtapcnt, df, dim, freq, frequency, inside, method, -% outside, pos, time, trial, vol, see bug2513 -% % Revision history: % % (2014) The subfields in the avg and trial fields are now present in the diff --git a/external/fieldtrip/fileio/private/ft_datatype_timelock.m b/external/fieldtrip/fileio/private/ft_datatype_timelock.m index a9a507e7..d78d2460 100644 --- a/external/fieldtrip/fileio/private/ft_datatype_timelock.m +++ b/external/fieldtrip/fileio/private/ft_datatype_timelock.m @@ -2,27 +2,25 @@ % FT_DATATYPE_TIMELOCK describes the FieldTrip MATLAB structure for timelock data % -% The timelock data structure represents averaged or non-averaged -% event-releted potentials (ERPs, in case of EEG) or ERFs (in case -% of MEG). This data structure is usually generated with the -% FT_TIMELOCKANALYSIS or FT_TIMELOCKGRANDAVERAGE function. +% The timelock data structure represents averaged or non-averaged event-releted +% potentials (ERPs, in case of EEG) or ERFs (in case of MEG). This data structure is +% usually generated with the FT_TIMELOCKANALYSIS or FT_TIMELOCKGRANDAVERAGE function. % -% An example of a timelock structure containing the ERF for 151 channels -% MEG data is +% An example of a timelock structure containing the ERF for 151 channels MEG data is % % dimord: 'chan_time' defines how the numeric data should be interpreted -% avg: [151x600 double] the numeric data (in this example it contains the average values of the activity for 151 channels x 600 timepoints) +% avg: [151x600 double] the average values of the activity for 151 channels x 600 timepoints +% var: [151x600 double] the variance of the activity for 151 channels x 600 timepoints % label: {151x1 cell} the channel labels (e.g. 'MRC13') % time: [1x600 double] the timepoints in seconds -% var: [151x600 double] the variance of the activity for 151 channels x 600 timepoints -% grad: [1x1 struct] information about the sensor array (for EEG-data it is called elec) -% cfg: [1x1 struct] configuration structure used by the invoking FieldTrip function +% grad: [1x1 struct] information about the sensor array (for EEG data it is called elec) +% cfg: [1x1 struct] the configuration used by the function that generated this data structure % % Required fields: % - label, dimord, time % % Optional fields: -% - avg, var, dof, cov, trial, grad, elec, cfg +% - avg, var, dof, cov, trial, trialinfo, sampleinfo, grad, elec, opto, cfg % % Deprecated fields: % - @@ -30,10 +28,6 @@ % Obsoleted fields: % - fsample % -% Historical fields: -% - avg, cfg, cov, dimord, dof, dofvec, elec, fsample, grad, label, -% numcovsamples, numsamples, time, trial, var, see bug2513 -% % Revision history: % % (2017/latest) The data structure cannot contain an average and simultaneously single @@ -69,7 +63,21 @@ % $Id$ % get the optional input arguments, which should be specified as key-value pairs -version = ft_getopt(varargin, 'version', 'latest'); +version = ft_getopt(varargin, 'version', 'latest'); +hassampleinfo = ft_getopt(varargin, 'hassampleinfo', 'ifmakessense'); % can be yes/no/ifmakessense +hastrialinfo = ft_getopt(varargin, 'hastrialinfo', 'ifmakessense'); % can be yes/no/ifmakessense + +% convert it into true/false +if isequal(hassampleinfo, 'ifmakessense') + hassampleinfo = makessense(timelock, 'sampleinfo'); +else + hassampleinfo = istrue(hassampleinfo); +end +if isequal(hastrialinfo, 'ifmakessense') + hastrialinfo = makessense(timelock, 'trialinfo'); +else + hastrialinfo = istrue(hastrialinfo); +end if strcmp(version, 'latest') version = '2017'; @@ -99,7 +107,17 @@ switch version case '2017' - timelock = ft_datatype_timelock(timelock, 'version', '2011v2'); + % ensure that the sensor structures are up to date + if isfield(timelock, 'grad') + timelock.grad = ft_datatype_sens(timelock.grad); + end + if isfield(timelock, 'elec') + timelock.elec = ft_datatype_sens(timelock.elec); + end + if isfield(timelock, 'opto') + timelock.opto = ft_datatype_sens(timelock.opto); + end + fn = fieldnames(timelock); fn = setdiff(fn, ignorefields('appendtimelock')); dimord = cell(size(fn)); @@ -123,19 +141,36 @@ end end + if (hassampleinfo && ~isfield(timelock, 'sampleinfo')) || (hastrialinfo && ~isfield(timelock, 'trialinfo')) + % try to reconstruct the sampleinfo and trialinfo + timelock = fixsampleinfo(timelock); + end + + if ~hassampleinfo && isfield(timelock, 'sampleinfo') + timelock = rmfield(timelock, 'sampleinfo'); + end + + if ~hastrialinfo && isfield(timelock, 'trialinfo') + timelock = rmfield(timelock, 'trialinfo'); + end + + % this field can be present in raw data, but is not desired in timelock data + timelock = removefields(timelock, {'fsample'}); + case '2011v2' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % ensure that the sensor structures are up to date if isfield(timelock, 'grad') - % ensure that the gradiometer structure is up to date timelock.grad = ft_datatype_sens(timelock.grad); end - if isfield(timelock, 'elec') - % ensure that the electrode structure is up to date timelock.elec = ft_datatype_sens(timelock.elec); end + if isfield(timelock, 'opto') + timelock.opto = ft_datatype_sens(timelock.opto); + end - % these fields can be present in raw data, but not desired in timelock data + % these fields can be present in raw data, but are not desired in timelock data timelock = removefields(timelock, {'sampleinfo', 'fsample'}); case '2003' diff --git a/external/fieldtrip/fileio/private/ft_determine_units.m b/external/fieldtrip/fileio/private/ft_determine_units.m index 8c5864a3..37a49ff5 100644 --- a/external/fieldtrip/fileio/private/ft_determine_units.m +++ b/external/fieldtrip/fileio/private/ft_determine_units.m @@ -38,9 +38,6 @@ % % $Id$ -% ensure the correct number of input and output arguments -% narginchk(1,1); -% nargoutchk(0,1); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/fileio/private/ft_fetch_data.m b/external/fieldtrip/fileio/private/ft_fetch_data.m index a374bfdc..2ac66f2f 100644 --- a/external/fieldtrip/fileio/private/ft_fetch_data.m +++ b/external/fieldtrip/fileio/private/ft_fetch_data.m @@ -36,11 +36,28 @@ end % get the options -hdr = ft_getopt(varargin, 'header'); -begsample = ft_getopt(varargin, 'begsample'); -endsample = ft_getopt(varargin, 'endsample'); -chanindx = ft_getopt(varargin, 'chanindx'); -allowoverlap = ft_getopt(varargin, 'allowoverlap', false); +if true + p = inputParser; + p.KeepUnmatched = true; + addOptional(p, 'header', []); + addOptional(p, 'begsample', []); + addOptional(p, 'endsample', []); + addOptional(p, 'chanindx', []); + addOptional(p, 'allowoverlap', false); + parse(p,varargin{:}); + hdr = p.Results.header; + begsample = p.Results.begsample; + endsample = p.Results.endsample; + chanindx = p.Results.chanindx; + allowoverlap = p.Results.allowoverlap; +else + hdr = ft_getopt(varargin, 'header'); + begsample = ft_getopt(varargin, 'begsample'); + endsample = ft_getopt(varargin, 'endsample'); + chanindx = ft_getopt(varargin, 'chanindx'); + allowoverlap = ft_getopt(varargin, 'allowoverlap', false); +end + allowoverlap = istrue(allowoverlap); if isempty(hdr) @@ -132,8 +149,9 @@ sel = find(count>1); % must be row vector for smplop=sel % find in which trials the sample occurs - seltrl = find(smplop>=trl(:,1) & smplop<=trl(:,2)); % which trials - selsmp = smplop - trl(seltrl,1) + 1; % which sample in each of the trials + seltrl = find(smplop>=trl(:,1) + 1 - begsample & ... + smplop<=trl(:,2) + 1 - begsample); % which trials, requires the adjustment with begsample, if different from 1, JM 20180116 + selsmp = smplop - trl(seltrl,1) + begsample; % which sample in each of the trials, requires the adjustment with begsample, rather than 1 for i=2:length(seltrl) % compare all occurences to the first one if ~all(data.trial{seltrl(i)}(:,selsmp(i)) == data.trial{seltrl(1)}(:,selsmp(1))) diff --git a/external/fieldtrip/fileio/private/ft_fetch_header.m b/external/fieldtrip/fileio/private/ft_fetch_header.m index 6cae9091..315a93a5 100644 --- a/external/fieldtrip/fileio/private/ft_fetch_header.m +++ b/external/fieldtrip/fileio/private/ft_fetch_header.m @@ -45,12 +45,24 @@ end % fill in hdr.nChans -hdr.nChans = length(data.label); +hdr.nChans = numel(data.label); -% fill in hdr.label -hdr.label = data.label; +% fill in the channel labels +hdr.label = data.label(:); -% fill in hdr.Fs (sample frequency) +% fill in the channel type and units +if isfield(data, 'hdr') && isfield(data.hdr, 'chantype') + hdr.chantype = data.hdr.chantype; +else + hdr.chantype = repmat({'unknown'}, hdr.nChans, 1); +end +if isfield(data, 'hdr') && isfield(data.hdr, 'chanunit') + hdr.chanunit = data.hdr.chanunit; +else + hdr.chanunit = repmat({'unknown'}, hdr.nChans, 1); +end + +% fill in sample frequency hdr.Fs = data.fsample; % determine hdr.nSamples, hdr.nSamplesPre, hdr.nTrials @@ -59,7 +71,7 @@ hdr.nSamplesPre = 0; hdr.nTrials = 1; -% retrieve the gradiometer and/or electrode information +% retrieve the gradiometer and/or electrode and/or optode information if isfield(data, 'grad') hdr.grad = data.grad; elseif isfield(data, 'hdr') && isfield(data.hdr, 'grad') @@ -70,6 +82,11 @@ elseif isfield(data, 'hdr') && isfield(data.hdr, 'elec') hdr.elec = data.hdr.elec; end +if isfield(data, 'opto') + hdr.opto = data.opto; +elseif isfield(data, 'hdr') && isfield(data.hdr, 'opto') + hdr.opto = data.hdr.opto; +end % retrieve the synchronization information if isfield(data, 'hdr') && isfield(data.hdr, 'FirstTimeStamp') @@ -78,3 +95,4 @@ if isfield(data, 'hdr') && isfield(data.hdr, 'TimeStampPerSample') hdr.TimeStampPerSample = data.hdr.TimeStampPerSample; end + diff --git a/external/fieldtrip/fileio/private/ft_findcfg.m b/external/fieldtrip/fileio/private/ft_findcfg.m index 7dfce3c2..81b17ac9 100644 --- a/external/fieldtrip/fileio/private/ft_findcfg.m +++ b/external/fieldtrip/fileio/private/ft_findcfg.m @@ -36,8 +36,8 @@ % if var(1)~='.' % var = ['.' var]; % end -val = []; -depth = 0; +val = []; +depth = 0; status = 0; while ~status @@ -46,7 +46,7 @@ if issubfield(cfg, var) val = getsubfield(cfg, var); status = 1; - elseif issubfield(cfg, 'previous'); + elseif issubfield(cfg, 'previous') [val, status] = ft_findcfg(cfg.previous, var); if status, break; end elseif iscell(cfg) diff --git a/external/fieldtrip/fileio/private/ft_getopt.m b/external/fieldtrip/fileio/private/ft_getopt.m index 0d433115..5f2f387b 100644 --- a/external/fieldtrip/fileio/private/ft_getopt.m +++ b/external/fieldtrip/fileio/private/ft_getopt.m @@ -9,7 +9,7 @@ % s = structure or cell-array % key = string % default = any valid MATLAB data type (optional, default = []) -% emptymeaningful = boolean value (optional, default = 0) +% emptymeaningful = boolean value (optional, default = false) % % If the key is present as field in the structure, or as key-value pair in the % cell-array, the corresponding value will be returned. @@ -49,7 +49,7 @@ end if nargin < 4 - emptymeaningful = 0; + emptymeaningful = false; end if isa(opt, 'struct') || isa(opt, 'config') diff --git a/external/fieldtrip/fileio/private/ft_getopt.mexw32 b/external/fieldtrip/fileio/private/ft_getopt.mexw32 index 59a9ff8e..fd5aed41 100755 Binary files a/external/fieldtrip/fileio/private/ft_getopt.mexw32 and b/external/fieldtrip/fileio/private/ft_getopt.mexw32 differ diff --git a/external/fieldtrip/fileio/private/ft_getopt.mexw64 b/external/fieldtrip/fileio/private/ft_getopt.mexw64 index 343089e8..741ebeda 100755 Binary files a/external/fieldtrip/fileio/private/ft_getopt.mexw64 and b/external/fieldtrip/fileio/private/ft_getopt.mexw64 differ diff --git a/external/fieldtrip/fileio/private/ft_hastoolbox.m b/external/fieldtrip/fileio/private/ft_hastoolbox.m index 055d238f..cc8883dc 100644 --- a/external/fieldtrip/fileio/private/ft_hastoolbox.m +++ b/external/fieldtrip/fileio/private/ft_hastoolbox.m @@ -52,7 +52,7 @@ if isdeployed % it is not possible to check the presence of functions or change the path in a compiled application - status = 1; + status = true; return end @@ -78,7 +78,8 @@ 'MATLAB2BESA' 'see http://www.besa.de/downloads/matlab/ and get the "MATLAB to BESA Export functions"' 'EEPROBE' 'see http://www.ant-neuro.com, or contact Maarten van der Velde' 'YOKOGAWA' 'this is deprecated, please use YOKOGAWA_MEG_READER instead' - 'YOKOGAWA_MEG_READER' 'see http://www.yokogawa.com/me/me-login-en.htm' + 'YOKOGAWA_MEG_READER' 'contact Ricoh engineers' + 'RICOH_MEG_READER' 'contact Ricoh engineers' 'BEOWULF' 'see http://robertoostenveld.nl, or contact Robert Oostenveld' 'MENTAT' 'see http://robertoostenveld.nl, or contact Robert Oostenveld' 'SON2' 'see http://www.kcl.ac.uk/depsta/biomedical/cfnr/lidierth.html, or contact Malcolm Lidierth' @@ -131,7 +132,7 @@ 'MYSQL' 'see http://www.mathworks.com/matlabcentral/fileexchange/8663-mysql-database-connector' 'ISO2MESH' 'see http://iso2mesh.sourceforge.net/cgi-bin/index.cgi?Home or contact Qianqian Fang' 'DATAHASH' 'see http://www.mathworks.com/matlabcentral/fileexchange/31272' - 'IBTB' 'see http://www.ibtb.org' + 'IBTB' 'see https://github.com/selimonat/InformationBreakdownToolbox' 'ICASSO' 'see http://www.cis.hut.fi/projects/ica/icasso' 'XUNIT' 'see http://www.mathworks.com/matlabcentral/fileexchange/22846-matlab-xunit-test-framework' 'PLEXON' 'available from http://www.plexon.com/assets/downloads/sdk/ReadingPLXandDDTfilesinMatlab-mexw.zip' @@ -143,8 +144,8 @@ 'BRAINSUITE' 'see http://brainsuite.bmap.ucla.edu/processing/additional-tools/' 'BRAINVISA' 'see http://brainvisa.info' 'FILEEXCHANGE' 'see http://www.mathworks.com/matlabcentral/fileexchange/' - 'NEURALYNX_V6' 'see http://neuralynx.com/research_software/file_converters_and_utilities/ and take the version from Neuralynx (windows only)' - 'NEURALYNX_V3' 'see http://neuralynx.com/research_software/file_converters_and_utilities/ and take the version from Ueli Rutishauser' + 'NEURALYNX_V6' 'see https://neuralynx.com/software/category/matlab-netcom-utilities/ and take the version from Neuralynx' + 'NEURALYNX_V3' 'see http://www.urut.ch/new/serendipity/index.php?/pages/nlxtomatlab.html' 'NPMK' 'see https://github.com/BlackrockMicrosystems/NPMK' 'VIDEOMEG' 'see https://github.com/andreyzhd/VideoMEG' 'WAVEFRONT' 'see http://mathworks.com/matlabcentral/fileexchange/27982-wavefront-obj-toolbox' @@ -152,6 +153,9 @@ 'BREWERMAP' 'see https://nl.mathworks.com/matlabcentral/fileexchange/45208-colorbrewer--attractive-and-distinctive-colormaps' 'CELLFUNCTION' 'see https://github.com/schoffelen/cellfunction' 'MARS' 'see http://www.parralab.org/mars' + 'JSONLAB' 'see https://se.mathworks.com/matlabcentral/fileexchange/33381-jsonlab--a-toolbox-to-encode-decode-json-files' + 'MFFMATLABIO' 'see https://github.com/arnodelorme/mffmatlabio' + 'JSONIO' 'see https://github.com/gllmflndn/JSONio' }; if nargin<2 @@ -238,7 +242,9 @@ case 'YOKOGAWA16BITBETA6' dependency = @()hasyokogawa('16bitBeta6'); case 'YOKOGAWA_MEG_READER' - dependency = @()hasyokogawa('1.4'); + dependency = @()hasyokogawa('1.5'); + case 'RICOH_MEG_READER' + dependency = @()hasricoh('1.0'); case 'BEOWULF' dependency = {'evalwulf', 'evalwulf', 'evalwulf'}; case 'MENTAT' @@ -309,6 +315,8 @@ dependency = {'macaque71.mat', 'motif4funct_wei'}; case 'CCA' dependency = {'ccabss'}; + case 'MFFMATLABIO' + dependency = {'eegplugin_mffmatlabio', 'mff_getobj'}; case 'EGI_MFF' dependency = {'mff_getObject', 'mff_getSummaryInfo'}; case 'TOOLBOX_GRAPH' @@ -327,7 +335,7 @@ case 'DATAHASH' dependency = {'DataHash'}; case 'IBTB' - dependency = {'make_ibtb','binr'}; + dependency = {'binr','information'}; case 'ICASSO' dependency = {'icassoEst'}; case 'XUNIT' @@ -371,6 +379,12 @@ dependency = {'ghdf5read' 'ghdf5fileimport'}; case 'MARS' dependency = {'spm_mars_mrf'}; + case 'JSONLAB' + dependency = {'loadjson' 'savejson'}; + case 'PLOTLY' + dependency = {'fig2plotly' 'savejson'}; + case 'JSONIO' + dependency = {'jsonread', 'jsonwrite', 'jsonread.mexa64'}; % the following are FieldTrip modules/toolboxes case 'FILEIO' @@ -442,19 +456,19 @@ % for linux computers in the Donders Centre for Cognitive Neuroimaging prefix = '/home/common/matlab'; - if ~status && isdir(prefix) + if ~status && isfolder(prefix) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end % for windows computers in the Donders Centre for Cognitive Neuroimaging prefix = 'h:\common\matlab'; - if ~status && isdir(prefix) + if ~status && isfolder(prefix) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end % use the MATLAB subdirectory in your homedirectory, this works on linux and mac prefix = fullfile(getenv('HOME'), 'matlab'); - if ~status && isdir(prefix) + if ~status && isfolder(prefix) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end @@ -490,9 +504,10 @@ % helper function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function status = myaddpath(toolbox, silent) +global ft_default if isdeployed ft_warning('cannot change path settings for %s in a compiled application', toolbox); - status = 1; + status = true; elseif exist(toolbox, 'dir') if ~silent ft_warning('off','backtrace'); @@ -500,16 +515,24 @@ ft_warning('on','backtrace'); end if any(~cellfun(@isempty, regexp(toolbox, {'spm2', 'spm5', 'spm8', 'spm12'}))) - % SPM needs to be added with the subdirectories + % SPM needs to be added with all its subdirectories addpath(genpath(toolbox)); + % check whether the mex files are compatible + check_spm_mex; else addpath(toolbox); end - status = 1; + % remember the toolbox that was just added to the path, it will be cleaned up by FT_POSTAMBLE_HASTOOLBOX + if ~isfield(ft_default, 'toolbox') || ~isfield(ft_default.toolbox, 'cleanup') + ft_default.toolbox.cleanup = {}; + end + ft_default.toolbox.cleanup{end+1} = toolbox; + status = true; elseif (~isempty(regexp(toolbox, 'spm2$', 'once')) || ~isempty(regexp(toolbox, 'spm5$', 'once')) || ~isempty(regexp(toolbox, 'spm8$', 'once')) || ~isempty(regexp(toolbox, 'spm12$', 'once'))) && exist([toolbox 'b'], 'dir') + % the final release version of SPM is not available, add the beta version instead status = myaddpath([toolbox 'b'], silent); else - status = 0; + status = false; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -582,10 +605,31 @@ token = regexp(version_str,'(\d*)','tokens'); v = str2num([token{:}{:}]); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function status = check_spm_mex() +status = true; +try + % this will always result in an error + spm_conv_vol +catch + me = lasterror; + % any error is ok, except when due to an invalid MEX file + status = ~isequal(me.identifier, 'MATLAB:mex:ErrInvalidMEXFile'); +end +if ~status + % SPM8 mex file issues are common on macOS with recent MATLAB versions + ft_warning('the SPM mex files are incompatible with your platform, see http://bit.ly/2OGF6US'); +end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % helper function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function status = has_license(toolbox_name) +% NOTE: this explicitly checks out a license, which may be suboptimal in +% terms of license use. Consider using the option 'test', but this needs to +% be checked with respect to backward compatibility first status = license('checkout', toolbox_name)==1; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -607,7 +651,6 @@ assert(false,'this should not happen'); end - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % helper function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -616,3 +659,10 @@ % must be in path and not a variable status = ~isempty(w) && ~isequal(w, 'variable'); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% ISFOLDER is needed for versions prior to 2017b +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function tf = isfolder(dirpath) +tf = exist(dirpath,'dir') == 7; + diff --git a/external/fieldtrip/fileio/private/ft_notification.m b/external/fieldtrip/fileio/private/ft_notification.m index dc9b7369..a74c4052 100644 --- a/external/fieldtrip/fileio/private/ft_notification.m +++ b/external/fieldtrip/fileio/private/ft_notification.m @@ -337,7 +337,7 @@ ft_default.notification.(level) = s; % the remainder is fully handled by the ERROR function if ~isempty(msgId) - error(msgId, varargin{:}); + error(state); else error(varargin{:}); end diff --git a/external/fieldtrip/fileio/private/ft_platform_supports.m b/external/fieldtrip/fileio/private/ft_platform_supports.m index c0676b62..34639722 100644 --- a/external/fieldtrip/fileio/private/ft_platform_supports.m +++ b/external/fieldtrip/fileio/private/ft_platform_supports.m @@ -41,6 +41,28 @@ % % See also FT_VERSION, VERSION, VER, VERLESSTHAN +% Copyright (C) 2006, Robert Oostenveld +% Copyright (C) 2010, Eelke Spaak +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + if ~ischar(what) error('first argument must be a string'); end @@ -48,6 +70,9 @@ switch what case 'matlabversion' tf = is_matlab() && matlabversion(varargin{:}); + + case 'octaveversion' + tf = is_octave() && octaveversion(varargin{:}); case 'exists-in-private-directory' tf = is_matlab(); @@ -206,11 +231,31 @@ end % function + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function [inInterval] = matlabversion(min, max) +function [inInterval] = octaveversion(min, max) +% the version does not change, making it persistent speeds up the subsequent calls +persistent curVer + +if nargin<2 + max = min; +end +if isempty(curVer) + curVer = OCTAVE_VERSION; +end + +% perform comparison with respect to version number +[major, minor] = parseMatlabVersion(curVer); +[minMajor, minMinor] = parseMatlabVersion(min); +[maxMajor, maxMinor] = parseMatlabVersion(max); + +inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); + +end % function + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MATLABVERSION checks if the current MATLAB version is within the interval % specified by min and max. % @@ -229,27 +274,8 @@ % etc. % % See also VERSION, VER, VERLESSTHAN - -% Copyright (C) 2006, Robert Oostenveld -% Copyright (C) 2010, Eelke Spaak -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [inInterval] = matlabversion(min, max) % the version does not change, making it persistent speeds up the subsequent calls persistent curVer @@ -311,8 +337,8 @@ minor = int8((ver - floor(ver)) * 10); else % ver is string (e.g. '7.10'), parse accordingly [major, rest] = strtok(ver, '.'); - major = str2num(major); - minor = str2num(strtok(rest, '.')); + major = str2double(major); + minor = str2double(strtok(rest, '.')); end end % function diff --git a/external/fieldtrip/fileio/private/ft_senstype.m b/external/fieldtrip/fileio/private/ft_senstype.m index b6753338..09712abe 100644 --- a/external/fieldtrip/fileio/private/ft_senstype.m +++ b/external/fieldtrip/fileio/private/ft_senstype.m @@ -156,7 +156,6 @@ isnirs = isa(input, 'struct') && isfield(input, 'label') && isfield(input, 'transceiver'); islabel = isa(input, 'cell') && ~isempty(input) && isa(input{1}, 'char'); haslabel = isa(input, 'struct') && isfield(input, 'label'); -haschantype = isa(input, 'struct') && isfield(input, 'chantype'); if ~(isdata || isheader || isgrad || iselec || isnirs || islabel || haslabel) && isfield(input, 'hdr') input = input.hdr; @@ -228,6 +227,7 @@ sens = []; end +haschantype = isfield(sens, 'chantype'); if isfield(sens, 'type') % preferably the structure specifies its own type diff --git a/external/fieldtrip/fileio/private/hasricoh.m b/external/fieldtrip/fileio/private/hasricoh.m new file mode 100644 index 00000000..97b6c068 --- /dev/null +++ b/external/fieldtrip/fileio/private/hasricoh.m @@ -0,0 +1,33 @@ +function [version] = hasricoh(desired) + +%% HASRICOH tests whether the official toolbox for RICOH MEG systems by +%% Ricoh Company, Ltd. is installed or not. +%% Use as +%% string = hasricoh; +%% which returns a string describing the toolbox version '1.0'. +%% An empty string is returned if the toolbox is not installed. +%% The string "unknown" is returned if it is installed but +%% the version is unknown. +%% +%% Alternatively you can use it as +%% [boolean] = hasricoh(desired); +%% where desired is a string with the desired version. +%% +%% See also READ_RICOH_HEADER, READ_RICOH_DATA, READ_RICOH_EVENT, RICOH2GRAD + +if exist('getRVersion', 'file') + res = getRVersion(); + version = res.version; +else + % return empty if none of them is present + version = []; +end + +if nargin>0 + % return a true/false value + if isempty(version) + version = false; + else + version = strcmpi(version, desired); + end +end diff --git a/external/fieldtrip/fileio/private/hasyokogawa.m b/external/fieldtrip/fileio/private/hasyokogawa.m index cf309aab..e013ca78 100644 --- a/external/fieldtrip/fileio/private/hasyokogawa.m +++ b/external/fieldtrip/fileio/private/hasyokogawa.m @@ -7,7 +7,7 @@ % Use as % string = hasyokogawa; % which returns a string describing the toolbox version, e.g. "12bitBeta3", -% "16bitBeta3", or "16bitBeta6" for preliminary versions, or '1.4' for the +% "16bitBeta3", or "16bitBeta6" for preliminary versions, or '1.5' for the % official Yokogawa MEG Reader Toolbox. An empty string is returned if the toolbox % is not installed. The string "unknown" is returned if it is installed but % the version is unknown. @@ -43,9 +43,9 @@ % there are a few versions of the old preliminary implementation, such as % 12bitBeta3, 16bitBeta3 and 16bitBeta6. In 2011 a completely new -% implementation was officially released, which contains functions with -% other names. At the time of writing this, the new implementation is -% version 1.4. +% implementation was officially released, which contains functions with other names. +% At the time of writing this [2018.06.08], +% the new implementation, Yokogawa MEG Reader, is version 1.5, in which EEG data are supported. if exist('getYkgwVersion', 'file') res = getYkgwVersion(); diff --git a/external/fieldtrip/fileio/private/isricohmegfile.m b/external/fieldtrip/fileio/private/isricohmegfile.m new file mode 100644 index 00000000..9b6bce0d --- /dev/null +++ b/external/fieldtrip/fileio/private/isricohmegfile.m @@ -0,0 +1,19 @@ +function val = isricohmegfile(filename) + +%% The extentions, .con, .ave, and .mrk are common between Ricoh and Yokogawa systems. +%% isricohmegfile idetifies whether the file is generated from Ricoh system or not. +%% This function uses a function in YOKOGAWA_MEG_READER toolbox, getYkgwHdrSystem. + +if ft_hastoolbox('yokogawa_meg_reader', 3) + sys_info = getYkgwHdrSystem(filename); + ver = sys_info.version; + if ver > 2 % The file is Ricoh one. + val = true; + ft_hastoolbox('ricoh_meg_reader', 3); + else + val = false; + end +else + msg = 'Error occurred; yokogawa_meg_reader is not installed.'; + error(msg) +end diff --git a/external/fieldtrip/fileio/private/labelcmb2indx.m b/external/fieldtrip/fileio/private/labelcmb2indx.m index 8fc4dc2e..fbf3e285 100644 --- a/external/fieldtrip/fileio/private/labelcmb2indx.m +++ b/external/fieldtrip/fileio/private/labelcmb2indx.m @@ -65,9 +65,6 @@ [indx, label] = labelcmb2indx(tmplabelcmb, unique(tmplabelcmb(:))); [blockindx, blocklabel] = labelcmb2indx(tmpblock, blocklabel); blockindx = blockindx(:,1); -% for k = 1:numel(blocklabel) -% nperblock(k,1) = sum(blockindx==k); -% end return; end @@ -82,18 +79,13 @@ nchan = numel(label); for k = 1:nchan - %sel1 = strcmp(label{k}, labelcmb(:,1)); - %sel2 = strcmp(label{k}, labelcmb(:,2)); sel = strcmp(label{k}, labelcmb); if nargin==1, - %autoindx = find(sel1 & sel2); autoindx = find(sel(:,1) & sel(:,2), 1, 'first'); if isempty(autoindx), ft_error('the required autocombination is not found in the input'); end else autoindx = k; end - %indx(sel1,1) = autoindx; - %indx(sel2,2) = autoindx; indx(sel(:,1),1) = autoindx; indx(sel(:,2),2) = autoindx; end diff --git a/external/fieldtrip/fileio/private/mxDeserialize.m b/external/fieldtrip/fileio/private/mxDeserialize.m index d9b4843f..243bf2ed 100644 --- a/external/fieldtrip/fileio/private/mxDeserialize.m +++ b/external/fieldtrip/fileio/private/mxDeserialize.m @@ -6,7 +6,7 @@ % See also MXSERIALIZE % Copyright (C) 2005, Brad Phelan http://xtargets.com -% Copyright (C) 2007, Robert Oostenveld http://www.fcdonders.ru.nl +% Copyright (C) 2007, Robert Oostenveld http://robertoostenveld.nl % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. diff --git a/external/fieldtrip/fileio/private/mxSerialize.m b/external/fieldtrip/fileio/private/mxSerialize.m index a2534051..b476fd28 100644 --- a/external/fieldtrip/fileio/private/mxSerialize.m +++ b/external/fieldtrip/fileio/private/mxSerialize.m @@ -6,7 +6,7 @@ % See also MXDESERIALIZE % Copyright (C) 2005, Brad Phelan http://xtargets.com -% Copyright (C) 2007, Robert Oostenveld http://www.fcdonders.ru.nl +% Copyright (C) 2007, Robert Oostenveld http://robertoostenveld.nl % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. diff --git a/external/fieldtrip/fileio/private/normals.m b/external/fieldtrip/fileio/private/normals.m new file mode 100644 index 00000000..03c0f8c3 --- /dev/null +++ b/external/fieldtrip/fileio/private/normals.m @@ -0,0 +1,80 @@ +function [nrm] = normals(pnt, tri, opt) + +% NORMALS compute the surface normals of a triangular mesh +% for each triangle or for each vertex +% +% [nrm] = normals(pnt, tri, opt) +% where opt is either 'vertex' or 'triangle' + +% Copyright (C) 2002-2007, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +if nargin<3 + opt='vertex'; +elseif (opt(1)=='v' || opt(1)=='V') + opt='vertex'; +elseif (opt(1)=='t' || opt(1)=='T') + opt='triangle'; +else + ft_error('invalid optional argument'); +end + +npnt = size(pnt,1); +ntri = size(tri,1); + +% shift to center +pnt(:,1) = pnt(:,1)-mean(pnt(:,1),1); +pnt(:,2) = pnt(:,2)-mean(pnt(:,2),1); +pnt(:,3) = pnt(:,3)-mean(pnt(:,3),1); + +% compute triangle normals +% nrm_tri = zeros(ntri, 3); +% for i=1:ntri +% v2 = pnt(tri(i,2),:) - pnt(tri(i,1),:); +% v3 = pnt(tri(i,3),:) - pnt(tri(i,1),:); +% nrm_tri(i,:) = cross(v2, v3); +% end + +% vectorized version of the previous part +v2 = pnt(tri(:,2),:) - pnt(tri(:,1),:); +v3 = pnt(tri(:,3),:) - pnt(tri(:,1),:); +nrm_tri = cross(v2, v3); + + +if strcmp(opt, 'vertex') + % compute vertex normals + nrm_pnt = zeros(npnt, 3); + for i=1:ntri + nrm_pnt(tri(i,1),:) = nrm_pnt(tri(i,1),:) + nrm_tri(i,:); + nrm_pnt(tri(i,2),:) = nrm_pnt(tri(i,2),:) + nrm_tri(i,:); + nrm_pnt(tri(i,3),:) = nrm_pnt(tri(i,3),:) + nrm_tri(i,:); + end + % normalise the direction vectors to have length one + nrm = nrm_pnt ./ (sqrt(sum(nrm_pnt.^2, 2)) * ones(1,3)); +else + % normalise the direction vectors to have length one + nrm = nrm_tri ./ (sqrt(sum(nrm_tri.^2, 2)) * ones(1,3)); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% fast cross product to replace the MATLAB standard version +function [c] = cross(a,b) +c = [a(:,2).*b(:,3)-a(:,3).*b(:,2) a(:,3).*b(:,1)-a(:,1).*b(:,3) a(:,1).*b(:,2)-a(:,2).*b(:,1)]; + diff --git a/external/fieldtrip/fileio/private/presentation_log.m b/external/fieldtrip/fileio/private/presentation_log.m new file mode 100644 index 00000000..dc482cdf --- /dev/null +++ b/external/fieldtrip/fileio/private/presentation_log.m @@ -0,0 +1,110 @@ +function event = presentation_log(filename) + +% PRESENTATION_LOG reads a NBS Presentation scenario log file and +% represents it as a FieldTrip event structure. +% +% See also FT_READ_EVENT + +% Copyright (C) 2018 Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +fid = fopen(filename, 'rt'); + +line = {}; +while ~feof(fid) + line = cat(1, line, fgetl(fid)); +end +fclose(fid); + +% remove empty lines +sel = cellfun(@isempty, line); +line = line(~sel); + +header1 = find(startsWith(line, 'Subject')); +header2 = find(startsWith(line, 'Event')); +if isempty(header2) + header2 = length(line)+1; +end + +part1 = line((header1+1):(header2-1)); +part2 = line((header2+1):(end)); + +VariableNames = tokenize(line{header1}, sprintf('\t')); +for i=1:length(VariableNames) + VariableNames{i} = fixname(VariableNames{i}); +end + +Values = cell(length(part1), length(VariableNames)); +for i=1:length(part1) + val = tokenize(part1{i}, sprintf('\t')); + val((end+1):length(VariableNames)) = {''}; + Values(i,:) = val; +end + +table1 = cell2table(Values); +VariableNames = uniquelabels(VariableNames); +table1.Properties.VariableNames = VariableNames; + +% where possible convert columns from string into numeric values +for i=1:numel(VariableNames) + numeric = table1.(VariableNames{i}); + empty = cellfun(@isempty, numeric); + numeric(empty) = {'0'}; % this will be replaced with nan later on + numeric = cellfun(@str2double, numeric); + if ~any(isnan(numeric)) + numeric(empty) = nan; + table1.(VariableNames{i}) = numeric; + end +end + +pause = find(strcmp(table1.event_type, 'Pause')); +resume = find(strcmp(table1.event_type, 'Resume')); +assert(numel(pause)==numel(resume)); +if ~isempty(pause) + ft_notice('%d pause events detected', numel(pause)); + for i=1:length(pause) + sel = (pause(i)+1):length(table1.time); + ft_notice('adding %.1f seconds of pause to the time of %d subsequent events', table1.duration(pause(i))/1e4, length(sel)); + table1.time(sel) = table1.time(sel) + table1.duration(pause(i)); + end +end + +numevent = size(table1,1); +type = table1.event_type; +value = table1.code; +sample = num2cell(nan(numevent, 1)); +timestamp = num2cell(table1.time); +duration = cell(numevent, 1); % num2cell(table1.duration); % don't put the duration values in, because the units are non-defined +offset = cell(numevent, 1); + +event = struct('type', type, 'value', value, 'sample', sample, 'timestamp', timestamp, 'offset', offset, 'duration', duration); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function c = uniquelabels(c) +for i=1:numel(c)-1 + sel = find(strcmp(c((i+1):end), c{i})); + if ~isempty(sel) + for j=1:numel(sel) + c{sel(j)+i} = sprintf('%s_%d', c{sel(j)+i}, j+1); + end + end +end diff --git a/external/fieldtrip/fileio/private/quaternion.m b/external/fieldtrip/fileio/private/quaternion.m index bd34c4f6..d77b70d4 100644 --- a/external/fieldtrip/fileio/private/quaternion.m +++ b/external/fieldtrip/fileio/private/quaternion.m @@ -9,11 +9,12 @@ % Q [q0, q1, q2, q3, q4, q5, q6] vector with parameters % H corresponding homogenous transformation matrix % -% See Elekta/Neuromag MaxFilter manual version 2.2, section "D2 Coordinate Matching", -% page 77 for more details and +% If the input vector has length 6, it is assumed to represent a unit quaternion without scaling. +% +% See Elekta/Neuromag MaxFilter manual version 2.2, section "D2 Coordinate Matching", page 77 for more details and % https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Conversion_to_and_from_the_matrix_representation -% Copyright (C) 2016, Robert Oostenveld +% Copyright (C) 2016-2017, Robert Oostenveld % % This program is free software; you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by @@ -47,6 +48,13 @@ % % $Id$ +if numel(q)==6 + % this is used a lot by the Elekta software, where the first element is left out and a rigid body transformation wothout scaling is used. + % see also https://github.com/mne-tools/mne-python/blob/maint/0.15/mne/transforms.py#L1137 + q0 = sqrt(1 - q(1)^2 - q(2)^2 - q(3)^2); + q = [q0 q]; +end + if numel(q)~=7 ft_error('incorrect input vector'); end diff --git a/external/fieldtrip/fileio/private/read_16bit.mexw32 b/external/fieldtrip/fileio/private/read_16bit.mexw32 index 641231a1..6fa211a2 100755 Binary files a/external/fieldtrip/fileio/private/read_16bit.mexw32 and b/external/fieldtrip/fileio/private/read_16bit.mexw32 differ diff --git a/external/fieldtrip/fileio/private/read_16bit.mexw64 b/external/fieldtrip/fileio/private/read_16bit.mexw64 index 8b467aaa..c8daf0c3 100755 Binary files a/external/fieldtrip/fileio/private/read_16bit.mexw64 and b/external/fieldtrip/fileio/private/read_16bit.mexw64 differ diff --git a/external/fieldtrip/fileio/private/read_24bit.mexw32 b/external/fieldtrip/fileio/private/read_24bit.mexw32 index e9bffc40..ee92b065 100755 Binary files a/external/fieldtrip/fileio/private/read_24bit.mexw32 and b/external/fieldtrip/fileio/private/read_24bit.mexw32 differ diff --git a/external/fieldtrip/fileio/private/read_24bit.mexw64 b/external/fieldtrip/fileio/private/read_24bit.mexw64 index e58f4e06..4e8a8eca 100755 Binary files a/external/fieldtrip/fileio/private/read_24bit.mexw64 and b/external/fieldtrip/fileio/private/read_24bit.mexw64 differ diff --git a/external/fieldtrip/fileio/private/read_besa_besa.m b/external/fieldtrip/fileio/private/read_besa_besa.m index 59e67681..e6ef89e0 100644 --- a/external/fieldtrip/fileio/private/read_besa_besa.m +++ b/external/fieldtrip/fileio/private/read_besa_besa.m @@ -246,7 +246,7 @@ end % Read BDAT tag and offset -[~,ofst_BDAT] = read_tag_offset_pair(fid,'BDAT'); +[dum,ofst_BDAT] = read_tag_offset_pair(fid,'BDAT'); % Check that file is not shorter than expected if(file_length < (ftell(fid)+ofst_BDAT)) @@ -274,7 +274,7 @@ n_samples = fread(fid,1,'*uint32'); % Read DATA tag and offset -[~,data_block_length] = read_tag_offset_pair(fid,'DATA'); +[dum,data_block_length] = read_tag_offset_pair(fid,'DATA'); data_block_offset = double(ftell(fid)); % Read data @@ -858,7 +858,7 @@ case num2str([CONST_FLOAT CONST_UNCOMPRESSED]) fseek(fid,0,'bof'); %% Header Block -[~,ofst_BCF1] = read_tag_offset_pair(fid,'BCF1'); +[dum,ofst_BCF1] = read_tag_offset_pair(fid,'BCF1'); % Read data in header block while ~feof(fid) && ftell(fid) < (8+ofst_BCF1) % 8 for header tag ('BCF1') and header offset (uint32) @@ -1061,7 +1061,7 @@ case num2str([CONST_FLOAT CONST_UNCOMPRESSED]) tags.offsets(end+1) = tags.next_BTAG_ofst; % Read BTAG tag and offset -[~,tag_block_length] = read_tag_offset_pair(fid,'BTAG'); +[dum,tag_block_length] = read_tag_offset_pair(fid,'BTAG'); % Untagged offset to next BTAG section tags.next_BTAG_ofst = fread(fid,1,'*int64'); @@ -1115,7 +1115,7 @@ case num2str([CONST_FLOAT CONST_UNCOMPRESSED]) file_info.offsets(end+1) = file_info.next_BFMI_ofst; % Read BFMI tag and offset -[~,fileinfo_block_length] = read_tag_offset_pair(fid,'BFMI'); +[dum,fileinfo_block_length] = read_tag_offset_pair(fid,'BFMI'); % Untagged offset to next BFMI section file_info.next_BFMI_ofst = fread(fid,1,'*int64'); @@ -1382,7 +1382,7 @@ case num2str([CONST_FLOAT CONST_UNCOMPRESSED]) channel_info.offsets(end+1) = channel_info.next_BCAL_ofst; % Read BCAL tag and offset -[~,channel_block_length] = read_tag_offset_pair(fid,'BCAL'); +[dum,channel_block_length] = read_tag_offset_pair(fid,'BCAL'); % Untagged offset to next BCAL section channel_info.next_BCAL_ofst = fread(fid,1,'*int64'); @@ -1596,14 +1596,14 @@ case num2str([CONST_FLOAT CONST_UNCOMPRESSED]) end % Read BEVT tag and offset -[~,event_block_length] = read_tag_offset_pair(fid,'BEVT'); +[dum,event_block_length] = read_tag_offset_pair(fid,'BEVT'); % Read LIST tag and offset but don't save anything read_tag_offset_pair(fid,'LIST'); % Now inside of LIST block % Read HEAD tag - Assuming that it is first tag in LIST block -[~,head_length] = read_tag_offset_pair(fid,'HEAD'); +[dum,head_length] = read_tag_offset_pair(fid,'HEAD'); head_offset = ftell(fid); % Read data in header block while ~feof(fid) && ftell(fid) < (head_offset+head_length) diff --git a/external/fieldtrip/fileio/private/read_brainstorm_data.m b/external/fieldtrip/fileio/private/read_brainstorm_data.m new file mode 100644 index 00000000..127be102 --- /dev/null +++ b/external/fieldtrip/fileio/private/read_brainstorm_data.m @@ -0,0 +1,68 @@ +function [dat] = read_brainstorm_data(filename, hdr, begsample, endsample, chanindx) + +% READ_BRAINSTORM_DATA reads .EEG files that have been generated by +% the Nihon Kohden system. The function constitutes a wrapper around +% BrainStorm3 functionalities +% +% Use as +% [dat] = read_brainstorm_data(filename, hdr, begsample, endsample, chanindx) +% +% The function has not been tested on NK1200 files with multiple epochs +% +% See also READ_BRAINSTORM_HEADER, READ_BRAINSTORM_EVENT + +% Copyright (C) 2018, Arjen Stolk & Sandon Griffin +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + +% default is to select all channels +if nargin<5 + chanrange{1} = [1 hdr.nChans]; % all channels +else + if isempty(find(diff(chanindx)~=1)) % if chanindx is consecutively numbered + chanrange{1} = [chanindx(1) chanindx(end)]; + else % otherwise loop around the chanindx entries + chanrange = cell(numel(chanindx), 1); + for c = 1:numel(chanindx) + chanrange{c} = [chanindx(c) chanindx(c)]; + end + end +end + +% call BrainStorm's in_fopen_nk +[sFile] = in_fopen_nk(filename); + +% check number of epochs/trials +if hdr.nTrials>1 + ft_warning('support for multiple epochs/trials has not been tested') +end + +% call BrainStorm's in_fread_nk +num = []; +dat = []; +for e = 1:hdr.nTrials + for c = 1:numel(chanrange) + sfid = fopen(filename); + tmp = in_fread_nk(sFile, sfid, e, [begsample-1 endsample-1], chanrange{c}); % NK1200 samples starts at 0 + num = [num; tmp]; clear tmp % stack channels + fclose(sfid); + end + dat = [dat num]; clear num % append epochs/trials +end diff --git a/external/fieldtrip/fileio/private/read_brainstorm_event.m b/external/fieldtrip/fileio/private/read_brainstorm_event.m new file mode 100644 index 00000000..50f6ff74 --- /dev/null +++ b/external/fieldtrip/fileio/private/read_brainstorm_event.m @@ -0,0 +1,48 @@ +function [event] = read_brainstorm_event(filename) + +% READ_BRAINSTORM_EVENT reads the event information from .EEG files +% that have been generated by the Nihon Kohden system. The function +% constitutes a wrapper around BrainStorm3 functionalities +% +% Use as +% [event] = read_brainstorm_event(filename) +% +% See also READ_NK1200_HEADER, READ_NK1200_DATA + +% Copyright (C) 2018, Arjen Stolk & Sandon Griffin +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + +% call BrainStorm's in_fopen_nk +[sFile] = in_fopen_nk(filename); + +% create the event file by pulling values from the output +event = []; +for e = 1:numel(sFile.events) + event(e).type = sFile.events(e).label; + event(e).sample = sFile.events(e).samples(1); + event(e).value = sFile.events(e).label; + event(e).offset = 0; + if numel(sFile.events(e).samples)>1 + event(e).duration = sFile.events(e).samples(2)-sFile.events(e).samples(1)+1; + else + event(e).duration = 0; + end +end diff --git a/external/fieldtrip/fileio/private/read_brainstorm_header.m b/external/fieldtrip/fileio/private/read_brainstorm_header.m new file mode 100644 index 00000000..842ec67a --- /dev/null +++ b/external/fieldtrip/fileio/private/read_brainstorm_header.m @@ -0,0 +1,54 @@ +function [hdr] = read_brainstorm_header(filename) + +% READ_BRAINSTORM_HEADER reads the header information from .EEG files +% that have been generated by the Nihon Kohden system. The function +% constitutes a wrapper around BrainStorm3 functionalities +% +% Use as +% [hdr] = read_brainstorm_header(filename) +% +% See also READ_BRAINSTORM_DATA, READ_BRAINSTORM_EVENT + +% Copyright (C) 2018, Arjen Stolk & Sandon Griffin +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + +% call BrainStorm's in_fopen_nk +[sFile, ChannelMat] = in_fopen_nk(filename); + +% create the header file by pulling values from the output +hdr.orig = sFile.header; +hdr.nChans = sFile.header.num_channels; +hdr.Fs = sFile.header.sample_rate; +hdr.nSamples = sFile.epochs.samples(end)-sFile.epochs.samples(1)+1; +hdr.nSamplesPre = 0; +hdr.nTrials = length(sFile.epochs); +hdr.label = cell(hdr.nChans, 1); +for n = 1:hdr.nChans + hdr.label{n} = ChannelMat.Channel(n).Name; +end +hdr.chanunit = cell(hdr.nChans, 1); +for n = 1:hdr.nChans + hdr.chanunit{n} = 'unknown'; +end +hdr.chantype = cell(hdr.nChans, 1); +for n = 1:hdr.nChans + hdr.chantype{n} = 'unknown'; +end diff --git a/external/fieldtrip/fileio/private/read_brainvision_vhdr.m b/external/fieldtrip/fileio/private/read_brainvision_vhdr.m index 7f305c05..5de7342b 100644 --- a/external/fieldtrip/fileio/private/read_brainvision_vhdr.m +++ b/external/fieldtrip/fileio/private/read_brainvision_vhdr.m @@ -121,3 +121,43 @@ hdr.label = hdr.label(:); hdr.reference = hdr.reference(:); hdr.resolution = hdr.resolution(:); + +%read in impedance values +hdr.impedances.channels=[]; +hdr.impedances.reference=[]; +hdr.impedances.ground=NaN; +if ~isempty(hdr.NumberOfChannels) + chanCounter=0; + refCounter=0; + impCounter=0; + while chanCounter= 0 fileline = fgets(fid); diff --git a/external/fieldtrip/fileio/private/read_edf.m b/external/fieldtrip/fileio/private/read_edf.m index c208e9dc..31caf1ac 100644 --- a/external/fieldtrip/fileio/private/read_edf.m +++ b/external/fieldtrip/fileio/private/read_edf.m @@ -164,17 +164,19 @@ end % check validity of PhysMin and PhysMax if (length(EDF.PhysMin) ~= EDF.NS) - fprintf(2,'Warning OPENEDF: Failing Physical Minimum\n'); + fprintf(2,'Warning OPENEDF: Failing Physical Minimum, taking Digital Minimum instead\n'); EDF.PhysMin = EDF.DigMin; end if (length(EDF.PhysMax) ~= EDF.NS) - fprintf(2,'Warning OPENEDF: Failing Physical Maximum\n'); + fprintf(2,'Warning OPENEDF: Failing Physical Maximum, taking Digital Maximum instead\n'); EDF.PhysMax = EDF.DigMax; end - if (any(EDF.PhysMin >= EDF.PhysMax)) - fprintf(2,'Warning OPENEDF: Physical Minimum larger than Maximum\n'); - EDF.PhysMin = EDF.DigMin; - EDF.PhysMax = EDF.DigMax; + idx_PhysMin_ge_PhysMax = EDF.PhysMin >= EDF.PhysMax; + if (any(idx_PhysMin_ge_PhysMax)) + tmplabel = cellfun(@(x) [x ' '], cellstr(EDF.Label(idx_PhysMin_ge_PhysMax,:)),'UniformOutput',false)'; + fprintf(2,['Warning OPENEDF: Physical Minimum larger than Maximum.\nPLEASE recheck if the scaling and polarity in the following channels are still correct if used:\n' tmplabel{:} '\n']); + %EDF.PhysMin = EDF.DigMin; + %EDF.PhysMax = EDF.DigMax; end EDF.PreFilt= char(fread(EDF.FILE.FID,[80,EDF.NS],'char')'); EDF.SPR = str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'char')')); % samples per data record @@ -183,9 +185,9 @@ EDF.Cal = (EDF.PhysMax-EDF.PhysMin)./(EDF.DigMax-EDF.DigMin); EDF.Off = EDF.PhysMin - EDF.Cal .* EDF.DigMin; - tmp = find(EDF.Cal < 0); - EDF.Cal(tmp) = ones(size(tmp)); - EDF.Off(tmp) = zeros(size(tmp)); + %tmp = find(EDF.Cal < 0); + %EDF.Cal(tmp) = ones(size(tmp)); + %EDF.Off(tmp) = zeros(size(tmp)); EDF.Calib=[EDF.Off';(diag(EDF.Cal))]; %EDF.Calib=sparse(diag([1; EDF.Cal])); @@ -433,10 +435,17 @@ % SUBFUNCTION for reading the 16 bit values %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function buf = readLowLevel(filename, offset, numwords) -if offset < 2*1024^2 +is_below_2GB = offset < 2*1024^2; +read_16bit_success = true; +if is_below_2GB % use the external mex file, only works for <2GB + try buf = read_16bit(filename, offset, numwords); -else + catch e + read_16bit_success = false; + end +end +if ~is_below_2GB || ~read_16bit_success % use plain matlab, thanks to Philip van der Broek fp = fopen(filename,'r','ieee-le'); status = fseek(fp, offset, 'bof'); @@ -447,6 +456,6 @@ fclose(fp); if (num=2); -if ~isdir(dataset) +if ~isfolder(dataset) ft_error('dataset should be a directory'); end diff --git a/external/fieldtrip/fileio/private/read_neuromag_headpos.m b/external/fieldtrip/fileio/private/read_neuromag_headpos.m new file mode 100644 index 00000000..c5206e43 --- /dev/null +++ b/external/fieldtrip/fileio/private/read_neuromag_headpos.m @@ -0,0 +1,51 @@ +function [data] = read_neuromag_headpos(filename) + +% READ_NEUROMAG_HEADPOS reads head position information from file. The file +% contains information about Time, Quaternions (q1-q6), goodness of +% fit (g-value) and error. +% Time q1 q2 q3 q4 q5 q6 g-value error +% +% data = read_neuromag_headpos(filename) +% +% where the returned structure data has the fields +% data.data Contains the numeric values +% data.textdata Contains the Column name +% data.coldata Contains the Column name + +% Copyright (C) 2017, Simon Homoelle +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +data = importdata(filename); +data.data = data.data'; +data.colheaders{2} = 'QUAT001'; +data.colheaders{3} = 'QUAT002'; +data.colheaders{4} = 'QUAT003'; +data.colheaders{5} = 'QUAT004'; +data.colheaders{6} = 'QUAT005'; +data.colheaders{7} = 'QUAT006'; + +data.textdata{2} = 'QUAT001'; +data.textdata{3} = 'QUAT002'; +data.textdata{4} = 'QUAT003'; +data.textdata{5} = 'QUAT004'; +data.textdata{6} = 'QUAT005'; +data.textdata{7} = 'QUAT006'; + + diff --git a/external/fieldtrip/fileio/private/read_neuromag_maxfilterlog.m b/external/fieldtrip/fileio/private/read_neuromag_maxfilterlog.m new file mode 100644 index 00000000..f9492c43 --- /dev/null +++ b/external/fieldtrip/fileio/private/read_neuromag_maxfilterlog.m @@ -0,0 +1,96 @@ +function log = read_neuromag_maxfilterlog(filename) + +% READ_NEUROMAG_MAXFILTERLOG reads the ascii logfile that is produced by MaxFilter +% +% Use as +% log = read_neuromag_maxfilterlog(filename) +% +% See also READ_NEUROMAG_EVE, READ_NEUROMAG_HC + +% Copyright (C) 2017, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +fid = fopen(filename, 'rt'); +if fid<0 + ft_error('cannot open %s', filername); +end + +% ************************************************************ +% *** This is Elekta Neuromag MaxFilter(TM) version 2.2.15 *** +% *** Compilation date: Dec 11 2012 14:48:44 *** +% *** Please report problems to meg-support@elekta.com *** +% ************************************************************ + +line = fgetl(fid); +line = fgetl(fid); assert(contains(line, 'MaxFilter')); +line = fgetl(fid); +line = fgetl(fid); +line = fgetl(fid); + +numhpi = nan; + +while ~feof(fid) + % read the next line + line = fgetl(fid); + + if startsWith(line, 'HPIFIT:') && contains(line, 'digitized') + % HPIFIT: 4 coils digitized in order 1 2 3 4 + numhpi = sscanf(line, 'HPIFIT: %d'); + for i=1:numhpi + log.hpi{i} = zeros(11, 0); + end + log.t = []; + log.e = []; + log.g = []; + log.v = []; + log.r = []; + log.d = []; + end + + if startsWith(line, 'Hpi fit OK') + % Hpi fit OK, movements [mm/s] = 55.8 / 48.1 / 42.7 / 52.3 / + % 79.5 0.0 0.0 / 79.2 -0.3 0.2 / g = 0.999 err = 0.5 mm / 0.998 -0.056 -0.011 + % 0.0 79.5 0.0 / 0.1 80.0 -0.2 / g = 1.000 err = 0.5 mm / 0.057 0.989 0.137 + % -79.5 0.0 0.0 / -78.8 -1.0 0.2 / g = 0.994 err = 1.2 mm / 0.003 -0.138 0.990 + % 0.0 -79.5 0.0 / -0.6 -78.6 -0.2 / g = 0.999 err = 1.0 mm / -0.2 1.4 5.8 + % #t = 170.510, #e = 0.08 cm, #g = 0.998, #v = 0.75 cm/s, #r = 0.61 rad/s, #d = 0.26 cm + for i=1:numhpi + line = fgetl(fid); + % sometimes the line starts with a '(' + if line(1)=='(' + value = sscanf(line, '( %f %f %f / %f %f %f / g = %f err = %f mm ) %f %f %f'); + else + value = sscanf(line, '%f %f %f / %f %f %f / g = %f err = %f mm / %f %f %f'); + end + log.hpi{i} = cat(2, log.hpi{i}, value); + end + line = fgetl(fid); + value = sscanf(line, '#t = %f, #e = %f cm, #g = %f, #v = %f cm/s, #r = %f rad/s, #d = %f cm'); + log.t = cat(1, log.t, value(1)); + log.e = cat(1, log.e, value(2)); + log.g = cat(1, log.g, value(3)); + log.v = cat(1, log.v, value(4)); + log.r = cat(1, log.r, value(5)); + log.d = cat(1, log.d, value(6)); + end + +end % while ~feof + +fclose(fid); diff --git a/external/fieldtrip/fileio/private/read_neurosim_evolution.m b/external/fieldtrip/fileio/private/read_neurosim_evolution.m index ef53782e..d98449b8 100644 --- a/external/fieldtrip/fileio/private/read_neurosim_evolution.m +++ b/external/fieldtrip/fileio/private/read_neurosim_evolution.m @@ -32,7 +32,7 @@ % % $Id$ -if isdir(filename) +if isfolder(filename) filename = fullfile(filename, 'evolution'); end diff --git a/external/fieldtrip/fileio/private/read_neurosim_signals.m b/external/fieldtrip/fileio/private/read_neurosim_signals.m index 60c409a4..08b141fc 100644 --- a/external/fieldtrip/fileio/private/read_neurosim_signals.m +++ b/external/fieldtrip/fileio/private/read_neurosim_signals.m @@ -25,7 +25,7 @@ % % $Id$ -if isdir(filename) +if isfolder(filename) filename = fullfile(filename, 'signals'); end diff --git a/external/fieldtrip/fileio/private/read_neurosim_spikes.m b/external/fieldtrip/fileio/private/read_neurosim_spikes.m index 88a513cd..aa15f0be 100644 --- a/external/fieldtrip/fileio/private/read_neurosim_spikes.m +++ b/external/fieldtrip/fileio/private/read_neurosim_spikes.m @@ -39,7 +39,7 @@ % % $Id$ -if isdir(filename) +if isfolder(filename) filename = fullfile(filename, 'spikes'); end diff --git a/external/fieldtrip/fileio/private/read_nihonkohden_hdr.m b/external/fieldtrip/fileio/private/read_nihonkohden_hdr.m index eb16bb16..f619a651 100644 --- a/external/fieldtrip/fileio/private/read_nihonkohden_hdr.m +++ b/external/fieldtrip/fileio/private/read_nihonkohden_hdr.m @@ -91,12 +91,12 @@ dc = textscan(fid,'%f',hdr.nChans*hdr.nSamples,'delimiter','\n','headerlines',0); df = dc{1};clear dc; -df = df.*10; % multiply by 10, don't forget to divide when reading in data! -fprintf('converting ASCII floats to 16-bit signed ints'); +%df = df.*10; % multiply by 10, don't forget to divide when reading in data! +%fprintf('converting ASCII floats to 16-bit signed ints'); dat = reshape(df,hdr.nChans,hdr.nSamples); -clear df; -dat = int16(dat)./10; +%clear df; +%dat = int16(dat)./10; hdr.dat = double(dat); clear dat; diff --git a/external/fieldtrip/fileio/private/read_nihonkohden_m00.m b/external/fieldtrip/fileio/private/read_nihonkohden_m00.m index 1eab9e99..460921dd 100644 --- a/external/fieldtrip/fileio/private/read_nihonkohden_m00.m +++ b/external/fieldtrip/fileio/private/read_nihonkohden_m00.m @@ -54,14 +54,14 @@ dc = textscan(fid,'%f',hdr.nChans*nsmp,'delimiter','\n','headerlines',begsample-1); df = dc{1};clear dc; -df = df.*mult; % multiply by 10, don't forget to divide when reading in data! +%df = df.*mult; % multiply by 10, don't forget to divide when reading in data! -fprintf('converting ASCII floats to 16-bit signed ints'); +%fprintf('converting ASCII floats to 16-bit signed ints'); dat = reshape(df,hdr.nChans,nsmp); -clear df; -dat = int16(dat)./mult; -dat = double(dat); +%clear df; +%dat = int16(dat)./mult; +%dat = double(dat); % should we double it? No clue why the int16 and the multiplier. % Later convert data has to be multiplied to transform it into microvolts % nkdata.eeg=nkdata.eeg./nkdata.multiplier; (Diego) diff --git a/external/fieldtrip/fileio/private/read_ply.m b/external/fieldtrip/fileio/private/read_ply.m index ea36fa73..3010df2f 100644 --- a/external/fieldtrip/fileio/private/read_ply.m +++ b/external/fieldtrip/fileio/private/read_ply.m @@ -11,7 +11,7 @@ % % See also WRITE_PLY, WRITE_VTK, READ_VTK -% Copyright (C) 2013, Robert Oostenveld +% Copyright (C) 2013-2018, Robert Oostenveld % % $Id$ @@ -61,9 +61,11 @@ end line = readline(fid); - if ~strcmp(line, 'property list uchar int vertex_index') && ~strcmp(line, 'property list uchar int vertex_indices') - % the wikipedia documentation specifies vertex_index, but the OPTOCAT files - % have vertex_indices + if ~strcmp(line, 'property list uchar int vertex_index') && ... + ~strcmp(line, 'property list uchar int vertex_indices') && ... + ~strcmp(line, 'property list uchar uint vertex_index') && ... + ~strcmp(line, 'property list uchar uint vertex_indices') + % the wikipedia documentation specifies vertex_index, but the OPTOCAT files have vertex_indices % it would not be very difficult to enhance the reader here with another % representation of the faces, i.e. something else than "uchar int" @@ -72,7 +74,7 @@ end line = readline(fid); - while ~strcmp(line, 'end_header'); + while ~strcmp(line, 'end_header') line = readline(fid); end @@ -204,7 +206,7 @@ function line = readline(fid) % read the next line from the ascii header, skip all comment lines line = fgetl(fid); -while strncmp(line, 'comment', 7); +while strncmp(line, 'comment', 7) line = fgetl(fid); end end % function readline diff --git a/external/fieldtrip/fileio/private/read_ricoh_data.m b/external/fieldtrip/fileio/private/read_ricoh_data.m new file mode 100644 index 00000000..0d592a3c --- /dev/null +++ b/external/fieldtrip/fileio/private/read_ricoh_data.m @@ -0,0 +1,108 @@ +function [dat] = read_ricoh_data(filename, hdr, begsample, endsample, chanindx) + +%function [dat] = read_ricoh_data(filename, hdr, begsample, endsample, chanindx) +% +%% READ_RICOH_DATA reads continuous or averaged MEG data +%% generated by the RICOH MEG system and software, +%% and allows the data to be used in FieldTrip. +%% +%% Use as +%% [dat] = read_ricoh_data(filename, hdr, begsample, endsample, chanindx) +%% +%% This is a wrapper function around the function getRData +%% +%% See also READ_RICOH_HEADER, READ_RICOH_EVENT + +if ~ft_hastoolbox('ricoh_meg_reader') + ft_error('cannot determine whether Ricoh toolbox is present'); +end + +hdr = hdr.orig; % use the original Ricoh header, not the FieldTrip header + +% default is to select all channels +if nargin<5 + chanindx = 1:hdr.channel_count; +end + +handles = definehandles; + +switch hdr.acq_type + case handles.AcqTypeEvokedAve + % dat is returned as double + start_sample = begsample - 1; % samples start at 0 + sample_length = endsample - begsample + 1; + epoch_count = 1; + start_epoch = 0; + dat = getRData(filename, start_sample, sample_length); + + case handles.AcqTypeContinuousRaw + % dat is returned as double + start_sample = begsample - 1; % samples start at 0 + sample_length = endsample - begsample + 1; + epoch_count = 1; + start_epoch = 0; + dat = getRData(filename, start_sample, sample_length); + + % Unlike Yokogawa system, "AcqTypeEvokedRaw" is not supported for Ricoh system. + + otherwise + ft_error('unknown data type'); +end + + +if size(dat,1)~=hdr.channel_count + ft_error('could not read all channels'); +elseif size(dat,2)~=(endsample-begsample+1) + ft_error('could not read all samples'); +end + +% select only the desired channels +dat = dat(chanindx,:); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% this defines some usefull constants +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function handles = definehandles +handles.output = []; +handles.sqd_load_flag = false; +handles.mri_load_flag = false; +handles.NullChannel = 0; +handles.MagnetoMeter = 1; +handles.AxialGradioMeter = 2; +handles.PlannerGradioMeter = 3; +handles.RefferenceChannelMark = hex2dec('0100'); +handles.RefferenceMagnetoMeter = bitor( handles.RefferenceChannelMark, handles.MagnetoMeter ); +handles.RefferenceAxialGradioMeter = bitor( handles.RefferenceChannelMark, handles.AxialGradioMeter ); +handles.RefferencePlannerGradioMeter = bitor( handles.RefferenceChannelMark, handles.PlannerGradioMeter ); +handles.TriggerChannel = -1; +handles.EegChannel = -2; +handles.EcgChannel = -3; +handles.EtcChannel = -4; +handles.NonMegChannelNameLength = 32; +handles.DefaultMagnetometerSize = (4.0/1000.0); % Square of 4.0mm in length +handles.DefaultAxialGradioMeterSize = (15.5/1000.0); % Circle of 15.5mm in diameter +handles.DefaultPlannerGradioMeterSize = (12.0/1000.0); % Square of 12.0mm in length +handles.AcqTypeContinuousRaw = 1; +handles.AcqTypeEvokedAve = 2; +handles.AcqTypeEvokedRaw = 3; +handles.sqd = []; +handles.sqd.selected_start = []; +handles.sqd.selected_end = []; +handles.sqd.axialgradiometer_ch_no = []; +handles.sqd.axialgradiometer_ch_info = []; +handles.sqd.axialgradiometer_data = []; +handles.sqd.plannergradiometer_ch_no = []; +handles.sqd.plannergradiometer_ch_info = []; +handles.sqd.plannergradiometer_data = []; +handles.sqd.eegchannel_ch_no = []; +handles.sqd.eegchannel_data = []; +handles.sqd.nullchannel_ch_no = []; +handles.sqd.nullchannel_data = []; +handles.sqd.selected_time = []; +handles.sqd.sample_rate = []; +handles.sqd.sample_count = []; +handles.sqd.pretrigger_length = []; +handles.sqd.matching_info = []; +handles.sqd.source_info = []; +handles.sqd.mri_info = []; +handles.mri = []; diff --git a/external/fieldtrip/fileio/private/read_ricoh_event.m b/external/fieldtrip/fileio/private/read_ricoh_event.m new file mode 100644 index 00000000..011f9f5d --- /dev/null +++ b/external/fieldtrip/fileio/private/read_ricoh_event.m @@ -0,0 +1,149 @@ +function [event] = read_ricoh_event(filename, varargin) + +% READ_RICOH_EVENT reads event information from continuous, +% epoched or averaged MEG data that has been generated by the Ricoh +% MEG system and software and allows those events to be used in +% combination with FieldTrip. +% +% Use as +% [event] = read_ricoh_event(filename) +% +% See also READ_RICOH_HEADER, READ_RICOH_DATA + +event = []; +handles = definehandles; + +% get the options, the default is set below +trigindx = ft_getopt(varargin, 'trigindx'); +threshold = ft_getopt(varargin, 'threshold'); +detectflank = ft_getopt(varargin, 'detectflank'); + +% ensure that the required toolbox is on the path +if ft_hastoolbox('ricoh_meg_reader'); + + % read the dataset header + hdr = read_ricoh_header(filename); + ch_info = hdr.orig.channel_info.channel; + type = [ch_info.type]; + % determine the trigger channels (if not specified by the user) + if isempty(trigindx) + trigindx = find(type==handles.TriggerChannel); + end + % As for trial selection, refer to the documentation "Getting started with Ricoh data". + if hdr.orig.acq_type==handles.AcqTypeEvokedAve + % make an event for the average + acq_cond_tmp = []; + acq_cond_tmp = getRHdrAcqCond(filename); + event(1).type = 'average'; + event(1).sample = 1; + event(1).offset = -acq_cond_tmp.pretrigger_length; + event(1).duration = acq_cond_tmp.frame_length; + event(1).value = acq_cond_tmp.multi_trigger.list.name; + clear acq_cond_tmp; + elseif hdr.orig.acq_type==handles.AcqTypeContinuousRaw + %% read the trigger id from all trials and the events annotated by users + % Events detected through the trigger channels + event_tmp = []; + event_tmp = getRHdrEvent(filename); + if ~isempty(event_tmp) + for i = 1:length(event_tmp) + event(end+1).sample = event_tmp(i).sample_no + 1; + event(end ).type = 'triginfo'; + if ~isempty(event_tmp(i).name) + event(end ).value = event_tmp(i).name; + elseif isempty(event_tmp(i).name) && ~isempty(event_tmp(i).code) + event(end ).value = event_tmp(i).code; % 1--16 + else + event(end ).value = 'unknown'; + end + end + end + % Events annotated by users during measurements + annotation_tmp = []; + annotation_tmp = getRHdrAnnotation(filename); + if ~isempty(annotation_tmp) + for i = 1:length(annotation_tmp) + event(end+1).sample = annotation_tmp(i).sample_no + 1; + event(end ).type = 'annotations'; + if ~isempty(annotation_tmp(i).annotationCategory) + event(end ).value = annotation_tmp(i).annotationCategory; + %% 0:'undefined', 10:'epileptic', 20: 'others', 30:'noise', 40:'text' + else + event(end ).value = 99; + end + end + end + clear event_tmp; + clear annotation_tmp; + trigger_tmp = read_trigger(filename, 'header', hdr, 'denoise', false, 'chanindx', trigindx, 'detectflank', detectflank, 'threshold', threshold); + trigger = []; + for i = 1:length(trigger_tmp) + trigger(end+1).sample = trigger_tmp(i).sample; + trigger(end ).value = trigger_tmp(i).type; % channel label + trigger(end ).type = 'analogtrig'; + end + clear trigger_tmp; + % combine the triggers and the other events + event = appendevent(event, trigger);% search for "trigger" events according to 'trigchannel' defined outside the function + if isfield(event, 'offset') + event = rmfield(event,'offset'); + end + if isfield(event, 'duration') + event = rmfield(event,'duration'); + end + end +else + ft_error('cannot determine, whether Ricoh toolbox is present'); +end + +if isempty(event) + ft_warning('no events were detected'); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% this defines some usefull constants +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function handles = definehandles +handles.output = []; +handles.sqd_load_flag = false; +handles.mri_load_flag = false; +handles.NullChannel = 0; +handles.MagnetoMeter = 1; +handles.AxialGradioMeter = 2; +handles.PlannerGradioMeter = 3; +handles.RefferenceChannelMark = hex2dec('0100'); +handles.RefferenceMagnetoMeter = bitor( handles.RefferenceChannelMark, handles.MagnetoMeter ); +handles.RefferenceAxialGradioMeter = bitor( handles.RefferenceChannelMark, handles.AxialGradioMeter ); +handles.RefferencePlannerGradioMeter = bitor( handles.RefferenceChannelMark, handles.PlannerGradioMeter ); +handles.TriggerChannel = -1; +handles.EegChannel = -2; +handles.EcgChannel = -3; +handles.EtcChannel = -4; +handles.NonMegChannelNameLength = 32; +handles.DefaultMagnetometerSize = (4.0/1000.0); % Square of 4.0mm in length +handles.DefaultAxialGradioMeterSize = (15.5/1000.0); % Circle of 15.5mm in diameter +handles.DefaultPlannerGradioMeterSize = (12.0/1000.0); % Square of 12.0mm in length +handles.AcqTypeContinuousRaw = 1; +handles.AcqTypeEvokedAve = 2; +handles.AcqTypeEvokedRaw = 3; +handles.sqd = []; +handles.sqd.selected_start = []; +handles.sqd.selected_end = []; +handles.sqd.axialgradiometer_ch_no = []; +handles.sqd.axialgradiometer_ch_info = []; +handles.sqd.axialgradiometer_data = []; +handles.sqd.plannergradiometer_ch_no = []; +handles.sqd.plannergradiometer_ch_info = []; +handles.sqd.plannergradiometer_data = []; +handles.sqd.eegchannel_ch_no = []; +handles.sqd.eegchannel_data = []; +handles.sqd.nullchannel_ch_no = []; +handles.sqd.nullchannel_data = []; +handles.sqd.selected_time = []; +handles.sqd.sample_rate = []; +handles.sqd.sample_count = []; +handles.sqd.pretrigger_length = []; +handles.sqd.matching_info = []; +handles.sqd.source_info = []; +handles.sqd.mri_info = []; +handles.mri = []; diff --git a/external/fieldtrip/fileio/private/read_ricoh_header.m b/external/fieldtrip/fileio/private/read_ricoh_header.m new file mode 100644 index 00000000..342304d0 --- /dev/null +++ b/external/fieldtrip/fileio/private/read_ricoh_header.m @@ -0,0 +1,193 @@ +function hdr = read_ricoh_header(filename) + +%% READ_RICOH_HEADER reads the header information from continuous +%% or averaged MEG data generated by the Ricoh MEG system and software +%% and allows the data to be used in FieldTrip. +%% +%% Use as +%% [hdr] = read_ricoh_header(filename) +%% +%% This is a wrapper function around the functions +%% getRHdrSystem +%% getRHdrChannel +%% getRHdrAcqCond +%% getRHdrCoregist +%% getRHdrDigitize +%% getRHdrSource +%% +%% See also READ_RICOH_DATA, READ_RICOH_EVENT + +if ~ft_hastoolbox('ricoh_meg_reader') + ft_error('cannot determine whether Ricoh toolbox is present'); +end + +% define several constants listed below +handles = definehandles; + +sys_info = getRHdrSystem(filename); +id = sys_info.system_id; +ver = sys_info.version; +rev = sys_info.revision; +sys_name = sys_info.system_name; +model_name = sys_info.model_name; +clear('sys_info'); % remove structure as local variables are collected in the end + +channel_info = getRHdrChannel(filename); +channel_count = channel_info.channel_count; + +acq_cond = getRHdrAcqCond(filename); +acq_type = acq_cond.acq_type; + +% these depend on the data type +sample_rate = []; +sample_count = []; +pretrigger_length = []; +averaged_count = []; +actual_epoch_count = []; + +switch acq_type + case handles.AcqTypeContinuousRaw + sample_rate = acq_cond.sample_rate; + sample_count = acq_cond.sample_count; + if isempty(sample_rate) || isempty(sample_count) + ft_error('invalid sample rate or sample count in ', filename); + return; + end + pretrigger_length = 0; + averaged_count = 1; + + case handles.AcqTypeEvokedAve + sample_rate = acq_cond.sample_rate; + sample_count = acq_cond.frame_length; + pretrigger_length = acq_cond.pretrigger_length; + averaged_count = acq_cond.average_count; + if isempty(sample_rate) || isempty(sample_count) || isempty(pretrigger_length) || isempty(averaged_count) + ft_error('invalid sample rate or sample count or pre-trigger length or average count in ', filename); + return; + end + + otherwise + ft_error('unknown data type'); +end +clear('acq_cond'); % remove structure as local variables are collected in the end + +coregist = getRHdrCoregist(filename); +digitize = getRHdrDigitize(filename); +source = getRHdrSource(filename); + +% put all local variables into a structure, this is a bit unusual matlab programming style +tmp = whos; +orig = []; +for i=1:length(tmp) + if isempty(strmatch(tmp(i).name, {'tmp', 'ans', 'handles'})) + orig = setfield(orig, tmp(i).name, eval(tmp(i).name)); + end +end + +% convert the original header information into something that FieldTrip understands +hdr = []; +hdr.orig = orig; % also store the original full header information +hdr.Fs = orig.sample_rate; % sampling frequency +hdr.nChans = orig.channel_count; % number of channels +hdr.nSamples = []; % number of samples per trial +hdr.nSamplesPre = []; % number of pre-trigger samples in each trial +hdr.nTrials = []; % number of trials + +switch orig.acq_type + case handles.AcqTypeEvokedAve + hdr.nSamples = orig.sample_count; + hdr.nSamplesPre = orig.pretrigger_length; + hdr.nTrials = 1; % only the average, which can be considered as a single trial + case handles.AcqTypeContinuousRaw + hdr.nSamples = orig.sample_count; + hdr.nSamplesPre = 0; % there is no fixed relation between triggers and data + hdr.nTrials = 1; % the continuous data can be considered as a single very long trial + otherwise + ft_error('unknown acquisition type'); +end + +% construct a cell-array with labels of each channel +% this should be consistent with the predefined list in ft_senslabel, +% with ricoh2grad and with ft_channelselection +for i=1:hdr.nChans + if hdr.orig.channel_info.channel(i).type == handles.NullChannel + prefix = ''; + elseif hdr.orig.channel_info.channel(i).type == handles.MagnetoMeter + prefix = 'M'; + elseif hdr.orig.channel_info.channel(i).type == handles.AxialGradioMeter + prefix = 'AG'; + elseif hdr.orig.channel_info.channel(i).type == handles.PlannerGradioMeter + prefix = 'PG'; + elseif hdr.orig.channel_info.channel(i).type == handles.RefferenceMagnetoMeter + prefix = 'RM'; + elseif hdr.orig.channel_info.channel(i).type == handles.RefferenceAxialGradioMeter + prefix = 'RAG'; + elseif hdr.orig.channel_info.channel(i).type == handles.RefferencePlannerGradioMeter + prefix = 'RPG'; + elseif hdr.orig.channel_info.channel(i).type == handles.TriggerChannel + prefix = 'TRIG'; + elseif hdr.orig.channel_info.channel(i).type == handles.EegChannel + prefix = 'EEG'; + elseif hdr.orig.channel_info.channel(i).type == handles.EcgChannel + prefix = 'ECG'; + elseif hdr.orig.channel_info.channel(i).type == handles.EtcChannel + prefix = 'ETC'; + end + hdr.label{i} = sprintf('%s%03d', prefix, i); + + % overwrite EEG-channel labels + if hdr.orig.channel_info.channel(i).type == handles.EegChannel + if ~isempty(hdr.orig.channel_info.channel(i).data.name) + hdr.label{i} = hdr.orig.channel_info.channel(i).data.name; + end + end + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% this defines several useful constants +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function handles = definehandles +handles.output = []; +handles.sqd_load_flag = false; +handles.mri_load_flag = false; +handles.NullChannel = 0; +handles.MagnetoMeter = 1; +handles.AxialGradioMeter = 2; +handles.PlannerGradioMeter = 3; +handles.RefferenceChannelMark = hex2dec('0100'); +handles.RefferenceMagnetoMeter = bitor( handles.RefferenceChannelMark, handles.MagnetoMeter ); +handles.RefferenceAxialGradioMeter = bitor( handles.RefferenceChannelMark, handles.AxialGradioMeter ); +handles.RefferencePlannerGradioMeter = bitor( handles.RefferenceChannelMark, handles.PlannerGradioMeter ); +handles.TriggerChannel = -1; +handles.EegChannel = -2; +handles.EcgChannel = -3; +handles.EtcChannel = -4; +handles.NonMegChannelNameLength = 32; +handles.DefaultMagnetometerSize = (4.0/1000.0); % Square of 4.0mm in length +handles.DefaultAxialGradioMeterSize = (15.5/1000.0); % Circle of 15.5mm in diameter +handles.DefaultPlannerGradioMeterSize = (12.0/1000.0); % Square of 12.0mm in length +handles.AcqTypeContinuousRaw = 1; +handles.AcqTypeEvokedAve = 2; +handles.AcqTypeEvokedRaw = 3; +handles.sqd = []; +handles.sqd.selected_start = []; +handles.sqd.selected_end = []; +handles.sqd.axialgradiometer_ch_no = []; +handles.sqd.axialgradiometer_ch_info = []; +handles.sqd.axialgradiometer_data = []; +handles.sqd.plannergradiometer_ch_no = []; +handles.sqd.plannergradiometer_ch_info = []; +handles.sqd.plannergradiometer_data = []; +handles.sqd.eegchannel_ch_no = []; +handles.sqd.eegchannel_data = []; +handles.sqd.nullchannel_ch_no = []; +handles.sqd.nullchannel_data = []; +handles.sqd.selected_time = []; +handles.sqd.sample_rate = []; +handles.sqd.sample_count = []; +handles.sqd.pretrigger_length = []; +handles.sqd.matching_info = []; +handles.sqd.source_info = []; +handles.sqd.mri_info = []; +handles.mri = []; diff --git a/external/fieldtrip/fileio/private/read_tobii_tsv.m b/external/fieldtrip/fileio/private/read_tobii_tsv.m index 9f819097..69959716 100644 --- a/external/fieldtrip/fileio/private/read_tobii_tsv.m +++ b/external/fieldtrip/fileio/private/read_tobii_tsv.m @@ -25,7 +25,7 @@ [key, val] = strtok(line, ':'); key = fixname(key); val = val(2:end); % skip the ':' - val = deblank2(val); + val = strtrim(val); tsv.(key) = val; end diff --git a/external/fieldtrip/fileio/private/read_trigger.m b/external/fieldtrip/fileio/private/read_trigger.m index 3daefdb4..53616cad 100644 --- a/external/fieldtrip/fileio/private/read_trigger.m +++ b/external/fieldtrip/fileio/private/read_trigger.m @@ -134,12 +134,17 @@ % fix suggested by Ralph Huonker to deal with triggers that need to be % interpreted as unsigned integers, rather than signed if strncmpi(dataformat, 'neuromag', 8) && ~fixneuromag - if any(dat<0) - tmpdat = zeros(size(dat)); - for k = 1:size(dat,1) - tmpdat(k,:) = double(typecast(int16(dat(k,:)), 'uint16')); + for k = 1:size(dat,1) + switch hdr.chantype{chanindx(1)} + case 'binary trigger' + if any(dat(k,:)<0) + dat(k,:) = double(typecast(int16(dat(k,:)), 'uint16')); + end + case 'analog trigger' + % keep it as it is + case 'other trigger' + % keep it as it is end - dat = tmpdat; clear tmpdat; end end diff --git a/external/fieldtrip/fileio/private/read_video.m b/external/fieldtrip/fileio/private/read_video.m new file mode 100644 index 00000000..9aa69ebc --- /dev/null +++ b/external/fieldtrip/fileio/private/read_video.m @@ -0,0 +1,118 @@ +function out = read_video(filename, varargin) + +% READ_VIDEO +% +% Use as +% hdr = read_video(filename) +% or +% dat = read_video(filename, hdr, begsample, endsample) +% +% See also READ_VIDEOMEG_VID, LOAD_VIDEO123 + +% Copyright (C) 2015 Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +needhdr = numel(varargin)==0; +needdat = numel(varargin)>0; + +video = VideoReader(filename); + +switch video.VideoFormat + case 'RGB24' + % there are three values for each pixel in each frame + pixval = 3; + otherwise + ft_error('unsupported video format %s', video.VideoFormat); +end % switch + +if needhdr + % convert it into a FieldTrip header + hdr = []; + hdr.Fs = video.FrameRate; + + hdr.nChans = video.Width * video.Height * pixval; + + % create artificial channels for each pixel + hdr.label = cellstr(num2str((1:hdr.nChans)')); + hdr.chantype = repmat({'video'} , size(hdr.label)); + hdr.chanunit = repmat({'unknown'}, size(hdr.label)); + + if isfield(video, 'NumberOfFrames') + hdr.nSamples = video.NumberOfFrames; + else + hdr.nSamples = floor(video.Duration*video.FrameRate); + end + hdr.nTrials = 1; + hdr.nSamplesPre = 0; + + % store it as a structure + hdr.orig = struct(video); + + % return the header as output + out = hdr; + +elseif needdat + hdr = varargin{1}; + begsample = varargin{2}; + endsample = varargin{3}; + nsamples = endsample - begsample + 1; + + if nargin>3 + chanindx = varargin{4}; + chanindx = chanindx(:); % ensure it to be a column + else + chanindx = []; + end + + % read the first frame to determine the type (probably uint8) + f = readFrame(video); + + % give some information, but not too often + ft_info timeout 60 + ft_info once + ft_info('the original size of individual video frames is [%d %d %d]', size(f)); + + if ~isempty(chanindx) + f = f(chanindx); + else + f = f(:); % ensure it to be a column + end + + % allocate enough memory for all frames + dat = repmat(f, 1, nsamples); + + % we cannot skip frames when reading + for frame=2:endsample + f = readFrame(video); + if ~isempty(chanindx) + f = f(chanindx); + else + f = f(:); % ensure it to be a column + end + dat(:,frame) = f; + end + dat = dat(:, begsample:endsample); + + % return the data as output + out = dat; + +end + +delete(video) diff --git a/external/fieldtrip/fileio/private/read_yokogawa_event.m b/external/fieldtrip/fileio/private/read_yokogawa_event.m index 4095c372..35cdadb9 100644 --- a/external/fieldtrip/fileio/private/read_yokogawa_event.m +++ b/external/fieldtrip/fileio/private/read_yokogawa_event.m @@ -40,88 +40,98 @@ % ensure that the required toolbox is on the path if ft_hastoolbox('yokogawa_meg_reader'); - - % read the dataset header - hdr = read_yokogawa_header_new(filename); - ch_info = hdr.orig.channel_info.channel; - type = [ch_info.type]; - - % determine the trigger channels (if not specified by the user) - if isempty(trigindx) - trigindx = find(type==handles.TriggerChannel); + % read the dataset header + hdr = read_yokogawa_header_new(filename); + ch_info = hdr.orig.channel_info.channel; + type = [ch_info.type]; + + % determine the trigger channels (if not specified by the user) + if isempty(trigindx) + trigindx = find(type==handles.TriggerChannel); + end + + % Use the MEG Reader documentation if more detailed support is required. + if hdr.orig.acq_type==handles.AcqTypeEvokedRaw + % read the trigger id from all trials + event = getYkgwHdrEvent(filename); + % use the standard FieldTrip header for trial events + % make an event for each trial as defined in the header + for i=1:hdr.nTrials + event(end+1).type = 'trial'; + event(end ).sample = (i-1)*hdr.nSamples + 1; + event(end ).offset = -hdr.nSamplesPre; + event(end ).duration = hdr.nSamples; + if ~isempty(value) + event(end ).value = event(i).code; + end end - - % Use the MEG Reader documentation if more detailed support is - % required. - if hdr.orig.acq_type==handles.AcqTypeEvokedRaw - % read the trigger id from all trials - event = getYkgwHdrEvent(filename); - % use the standard FieldTrip header for trial events - % make an event for each trial as defined in the header - for i=1:hdr.nTrials - event(end+1).type = 'trial'; - event(end ).sample = (i-1)*hdr.nSamples + 1; - event(end ).offset = -hdr.nSamplesPre; - event(end ).duration = hdr.nSamples; - - if ~isempty(value) - event(end ).value = event(i).code; + + % Use the MEG Reader documentation if more detailed support is required. + elseif hdr.orig.acq_type==handles.AcqTypeEvokedAve + % make an event for the average + event(1).type = 'average'; + event(1).sample = 1; + event(1).offset = -hdr.nSamplesPre; + event(1).duration = hdr.nSamples; + + elseif hdr.orig.acq_type==handles.AcqTypeContinuousRaw + % Events annotated by users during measurements + bookmark_tmp = []; + bookmark_tmp = getYkgwHdrBookmark(filename); + if ~isempty(bookmark_tmp) + for i = 1:length(bookmark_tmp) + event(end+1).sample = bookmark_tmp(i).sample_no + 1; + event(end ).type = 'bookmarks'; + if ~isempty(bookmark_tmp(i).label) + event(end ).value = bookmark_tmp(i).label; + %% 0 in default + else + event(end ).value = 99; end end - - % Use the MEG Reader documentation if more detailed support is required. - elseif hdr.orig.acq_type==handles.AcqTypeEvokedAve - % make an event for the average - event(1).type = 'average'; - event(1).sample = 1; - event(1).offset = -hdr.nSamplesPre; - event(1).duration = hdr.nSamples; - - elseif hdr.orig.acq_type==handles.AcqTypeContinuousRaw - % the data structure does not contain events, but flank detection on the trigger channel might reveal them - % this is done below for all formats end - + clear bookmark_tmp; + end + elseif ft_hastoolbox('yokogawa'); - - % read the dataset header - hdr = read_yokogawa_header(filename); - % determine the trigger channels (if not specified by the user) - if isempty(trigindx) - trigindx = find(hdr.orig.channel_info(:,2)==handles.TriggerChannel); - end + % read the dataset header + hdr = read_yokogawa_header(filename); + + % determine the trigger channels (if not specified by the user) + if isempty(trigindx) + trigindx = find(hdr.orig.channel_info(:,2)==handles.TriggerChannel); + end - if hdr.orig.acq_type==handles.AcqTypeEvokedRaw - % read the trigger id from all trials - fid = fopen(filename, 'r'); - value = GetMeg160TriggerEventM(fid); - fclose(fid); - % use the standard FieldTrip header for trial events - % make an event for each trial as defined in the header - for i=1:hdr.nTrials - event(end+1).type = 'trial'; - event(end ).sample = (i-1)*hdr.nSamples + 1; - event(end ).offset = -hdr.nSamplesPre; - event(end ).duration = hdr.nSamples; - - if ~isempty(value) - event(end ).value = value(i); - end + if hdr.orig.acq_type==handles.AcqTypeEvokedRaw + % read the trigger id from all trials + fid = fopen(filename, 'r'); + value = GetMeg160TriggerEventM(fid); + fclose(fid); + % use the standard FieldTrip header for trial events + % make an event for each trial as defined in the header + for i=1:hdr.nTrials + event(end+1).type = 'trial'; + event(end ).sample = (i-1)*hdr.nSamples + 1; + event(end ).offset = -hdr.nSamplesPre; + event(end ).duration = hdr.nSamples; + if ~isempty(value) + event(end ).value = value(i); end - - elseif hdr.orig.acq_type==handles.AcqTypeEvokedAve - % make an event for the average - event(1).type = 'average'; - event(1).sample = 1; - event(1).offset = -hdr.nSamplesPre; - event(1).duration = hdr.nSamples; - - elseif hdr.orig.acq_type==handles.AcqTypeContinuousRaw - % the data structure does not contain events, but flank detection on the trigger channel might reveal them - % this is done below for all formats end + elseif hdr.orig.acq_type==handles.AcqTypeEvokedAve + % make an event for the average + event(1).type = 'average'; + event(1).sample = 1; + event(1).offset = -hdr.nSamplesPre; + event(1).duration = hdr.nSamples; + + elseif hdr.orig.acq_type==handles.AcqTypeContinuousRaw + % the data structure does not contain events, but flank detection on the trigger channel might reveal them + % this is done below + end + else ft_error('cannot determine, whether Yokogawa toolbox is present'); end diff --git a/external/fieldtrip/fileio/private/read_yokogawa_header_new.m b/external/fieldtrip/fileio/private/read_yokogawa_header_new.m index 95f57704..546a35dc 100644 --- a/external/fieldtrip/fileio/private/read_yokogawa_header_new.m +++ b/external/fieldtrip/fileio/private/read_yokogawa_header_new.m @@ -18,7 +18,7 @@ % % See also READ_YOKOGAWA_DATA_NEW, READ_YOKOGAWA_EVENT -% ** +% ** % Copyright (C) 2005, Robert Oostenveld and 2010, Tilmann Sander-Thoemmes % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org @@ -85,12 +85,12 @@ sample_rate = acq_cond.sample_rate; sample_count = acq_cond.frame_length; pretrigger_length = acq_cond.pretrigger_length; - averaged_count = acq_cond.average_count; + averaged_count = acq_cond.average_count; if isempty(sample_rate) || isempty(sample_count) || isempty(pretrigger_length) || isempty(averaged_count) ft_error('invalid sample rate or sample count or pretrigger length or average count in ', filename); return; end - if acq_cond.multi_trigger.enable + if acq_cond.multi_trigger.enable ft_error('multi trigger mode not supported for ', filename); return; end @@ -98,12 +98,12 @@ sample_rate = acq_cond.sample_rate; sample_count = acq_cond.frame_length; pretrigger_length = acq_cond.pretrigger_length; - actual_epoch_count = acq_cond.average_count; + actual_epoch_count = acq_cond.average_count; if isempty(sample_rate) || isempty(sample_count) || isempty(pretrigger_length) || isempty(actual_epoch_count) ft_error('invalid sample rate or sample count or pretrigger length or epoch count in ', filename); return; end - if acq_cond.multi_trigger.enable + if acq_cond.multi_trigger.enable ft_error('multi trigger mode not supported for ', filename); return; end @@ -180,6 +180,14 @@ prefix = 'ETC'; end hdr.label{i} = sprintf('%s%03d', prefix, i); + + % overwrite EEG-channel labels + if hdr.orig.channel_info.channel(i).type == handles.EegChannel + if ~isempty(hdr.orig.channel_info.channel(i).data.name) + hdr.label{i} = hdr.orig.channel_info.channel(i).data.name; + end + end + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/fileio/private/readmarkerfile.m b/external/fieldtrip/fileio/private/readmarkerfile.m index 1c9f0989..49de915f 100644 --- a/external/fieldtrip/fileio/private/readmarkerfile.m +++ b/external/fieldtrip/fileio/private/readmarkerfile.m @@ -38,11 +38,11 @@ i = strmatch('NUMBER OF SAMPLES:', markfile, 'exact') + 1; nsamples = str2num(char(markfile(i))); -for i = 1:length(nsamples) - if nsamples(i) == 0 - ft_warning('marker %s in %s has zero samples', names{i}, folder); - end -end +% for i = 1:length(nsamples) +% if nsamples(i) == 0 +% ft_warning('marker %s in %s has zero samples', names{i}, folder); +% end +% end % Get the samples. Each is trial and time in seconds. j = strmatch('LIST OF SAMPLES:', markfile, 'exact') + 2; @@ -57,4 +57,3 @@ marker = struct('number_markers', {nmarkers}, 'number_samples', {nsamples}, ... 'marker_names', {names}, 'trial_times', {marks}); - diff --git a/external/fieldtrip/fileio/private/refine.m b/external/fieldtrip/fileio/private/refine.m index 92548ba5..075e6c05 100755 --- a/external/fieldtrip/fileio/private/refine.m +++ b/external/fieldtrip/fileio/private/refine.m @@ -1,12 +1,12 @@ -function [pntr, trir, texr] = refine(pnt, tri, method, texture, varargin) +function [posr, trir, texr] = refine(pos, tri, method, varargin) % REFINE a 3D surface that is described by a triangulation % % Use as -% [pnt, tri] = refine(pnt, tri) -% [pnt, tri] = refine(pnt, tri, 'banks') -% [pnt, tri] = refine(pnt, tri, 'updown', numtri) -% [pnt, tri, texture] = refine(pnt, tri, 'banks', texture) +% [pos, tri] = refine(pos, tri) +% [pos, tri] = refine(pos, tri, 'banks') +% [pos, tri, texture] = refine(pos, tri, 'banks', texture) +% [pos, tri] = refine(pos, tri, 'updown', numtri) % % If no method is specified, the default is to refine the mesh globally by bisecting % each edge according to the algorithm described in Banks, 1983. @@ -45,39 +45,45 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id$ if nargin<3 method = 'banks'; end +texture = []; +numtri = []; - -if strcmp(method, 'banks') && nargin==5 - % FIXME this is work in progress - keyboard +if nargin>3 + switch lower(method) + case 'banks' + texture = varargin{1}; + case 'updown' + numtri = varargin{1}; + end % switch end switch lower(method) case 'banks' - if exist('texture') - npnt = size(pnt,1); + if ~isempty(texture) + npnt = size(pos,1); ntri = size(tri,1); ntex = size(texture,1); + + assert(ntex==ntri, 'invalid size of texture'); + insert = spalloc(3*npnt,3*npnt,3*ntri); - trir = zeros(4*ntri,3); % allocate memory for the new triangles - pntr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices + posr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices texr = zeros(ntex+3*ntri,2); - pntr(1:npnt,:) = pnt; % insert the original vertices + posr(1:npnt,:) = pos; % insert the original vertices texr(1:ntex,:) = texture; current = npnt; - - for i=1:ntri + for i=1:ntri + if ~insert(tri(i,1),tri(i,2)) current = current + 1; - pntr(current,:) = (pnt(tri(i,1),:) + pnt(tri(i,2),:))/2; + posr(current,:) = (pos(tri(i,1),:) + pos(tri(i,2),:))/2; texr(current,:) = (texture(tri(i,1),:) + texture(tri(i,2),:))/2; insert(tri(i,1),tri(i,2)) = current; insert(tri(i,2),tri(i,1)) = current; @@ -85,10 +91,10 @@ else v12 = insert(tri(i,1),tri(i,2)); end - + if ~insert(tri(i,2),tri(i,3)) current = current + 1; - pntr(current,:) = (pnt(tri(i,2),:) + pnt(tri(i,3),:))/2; + posr(current,:) = (pos(tri(i,2),:) + pos(tri(i,3),:))/2; texr(current,:) = (texture(tri(i,2),:) + texture(tri(i,3),:))/2; insert(tri(i,2),tri(i,3)) = current; insert(tri(i,3),tri(i,2)) = current; @@ -96,10 +102,10 @@ else v23 = insert(tri(i,2),tri(i,3)); end - + if ~insert(tri(i,3),tri(i,1)) current = current + 1; - pntr(current,:) = (pnt(tri(i,3),:) + pnt(tri(i,1),:))/2; + posr(current,:) = (pos(tri(i,3),:) + pos(tri(i,1),:))/2; texr(current,:) = (texture(tri(i,3),:) + texture(tri(i,1),:))/2; insert(tri(i,3),tri(i,1)) = current; insert(tri(i,1),tri(i,3)) = current; @@ -107,80 +113,82 @@ else v31 = insert(tri(i,3),tri(i,1)); end - + % add the 4 new triangles with the correct indices trir(4*(i-1)+1, :) = [tri(i,1) v12 v31]; trir(4*(i-1)+2, :) = [tri(i,2) v23 v12]; trir(4*(i-1)+3, :) = [tri(i,3) v31 v23]; trir(4*(i-1)+4, :) = [v12 v23 v31]; - + end - pntr = pntr(1:current, :); + posr = posr(1:current, :); texr = texr(1:current, :); - - else - - npnt = size(pnt,1); + + else + % there is no texture + + npnt = size(pos,1); ntri = size(tri,1); insert = spalloc(3*npnt,3*npnt,3*ntri); - + trir = zeros(4*ntri,3); % allocate memory for the new triangles - pntr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices - pntr(1:npnt,:) = pnt; % insert the original vertices + posr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices + posr(1:npnt,:) = pos; % insert the original vertices current = npnt; - - for i=1:ntri + for i=1:ntri + if ~insert(tri(i,1),tri(i,2)) current = current + 1; - pntr(current,:) = (pnt(tri(i,1),:) + pnt(tri(i,2),:))/2; + posr(current,:) = (pos(tri(i,1),:) + pos(tri(i,2),:))/2; insert(tri(i,1),tri(i,2)) = current; insert(tri(i,2),tri(i,1)) = current; v12 = current; else v12 = insert(tri(i,1),tri(i,2)); end - + if ~insert(tri(i,2),tri(i,3)) current = current + 1; - pntr(current,:) = (pnt(tri(i,2),:) + pnt(tri(i,3),:))/2; + posr(current,:) = (pos(tri(i,2),:) + pos(tri(i,3),:))/2; insert(tri(i,2),tri(i,3)) = current; insert(tri(i,3),tri(i,2)) = current; v23 = current; else v23 = insert(tri(i,2),tri(i,3)); end - + if ~insert(tri(i,3),tri(i,1)) current = current + 1; - pntr(current,:) = (pnt(tri(i,3),:) + pnt(tri(i,1),:))/2; + posr(current,:) = (pos(tri(i,3),:) + pos(tri(i,1),:))/2; insert(tri(i,3),tri(i,1)) = current; insert(tri(i,1),tri(i,3)) = current; v31 = current; else v31 = insert(tri(i,3),tri(i,1)); end - - % add the 4 new triangles with the correct indices + + % add the 4 new triangles with the correct indices trir(4*(i-1)+1, :) = [tri(i,1) v12 v31]; trir(4*(i-1)+2, :) = [tri(i,2) v23 v12]; trir(4*(i-1)+3, :) = [tri(i,3) v31 v23]; - trir(4*(i-1)+4, :) = [v12 v23 v31]; + trir(4*(i-1)+4, :) = [v12 v23 v31]; + end + % remove the space for the vertices that was not used + posr = posr(1:current, :); end - % remove the space for the vertices that was not used - pntr = pntr(1:current, :); - end + case 'updown' ntri = size(tri,1); - while ntri= d6to7 % break between e8 and e9 - GridDim(1) = 6; GridDim(2) = 8; - elseif d6to7 > d8to9 % break between e6 and e7 - GridDim(1) = 8; GridDim(2) = 6; - end - elseif isequal(numel(Grid2Elec{g}), 32) - e4 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(4)]),:); - e5 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(5)]),:); - e8 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(8)]),:); - e9 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(9)]),:); - d4to5 = sqrt(sum((e4-e5).^2)); % distance of elec 4 to 5 - d8to9 = sqrt(sum((e8-e9).^2)); % distance of elec 8 to 9 - if d8to9 >= d4to5 % break between e8 and e9 - GridDim(1) = 4; GridDim(2) = 8; - elseif d4to5 > d8to9 % break between e4 and e5 - GridDim(1) = 8; GridDim(2) = 4; - end - elseif isequal(numel(Grid2Elec{g}), 24) - e4 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(4)]),:); - e5 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(5)]),:); - e6 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(6)]),:); - e7 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(7)]),:); - d4to5 = sqrt(sum((e4-e5).^2)); % distance of elec 4 to 5 - d6to7 = sqrt(sum((e6-e7).^2)); % distance of elec 6 to 7 - if d6to7 >= d4to5 % break between e6 and e7 - GridDim(1) = 4; GridDim(2) = 6; - elseif d4to5 > d6to7 % break between e4 and e5 - GridDim(1) = 6; GridDim(2) = 4; - end - elseif isequal(numel(Grid2Elec{g}), 20) - e4 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(4)]),:); - e5 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(5)]),:); - d4to5 = sqrt(sum((e4-e5).^2)); % distance of elec 4 to 5 - d5to6 = sqrt(sum((e5-e6).^2)); % distance of elec 5 to 6 - if d5to6 >= d4to5 % break between e5 and e6 - GridDim(1) = 4; GridDim(2) = 5; - elseif d4to5 > d5to6 % break between e4 and e5 - GridDim(1) = 5; GridDim(2) = 4; - end - elseif isequal(numel(Grid2Elec{g}), 16) - e4 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(4)]),:); - e5 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(5)]),:); - e8 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(8)]),:); - e9 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(9)]),:); - d4to5 = sqrt(sum((e4-e5).^2)); % distance of elec 4 to 5 - d8to9 = sqrt(sum((e8-e9).^2)); % distance of elec 8 to 9 - d4to8 = sqrt(sum((e4-e8).^2)); % distance of elec 4 to 8 - if d8to9 > 2*d4to5 % break between e8 and e9 - GridDim(1) = 2; GridDim(2) = 8; - elseif d4to5 > 2*d4to8 % break between e4 and e5 - GridDim(1) = 4; GridDim(2) = 4; - else - GridDim(1) = 1; GridDim(2) = 16; - end - elseif isequal(numel(Grid2Elec{g}), 12) - e4 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(4)]),:); - e5 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(5)]),:); - e6 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(6)]),:); - e7 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(7)]),:); - d4to5 = sqrt(sum((e4-e5).^2)); % distance of elec 5 to 6 - d5to6 = sqrt(sum((e5-e6).^2)); % distance of elec 5 to 6 - d6to7 = sqrt(sum((e6-e7).^2)); % distance of elec 6 to 7 - if d4to5 > 2*d5to6 % break between e4 and e5 - GridDim(1) = 3; GridDim(2) = 4; % 4x3 unsuppported - elseif d6to7 > 2*d5to6 % break between e6 and e7 - GridDim(1) = 2; GridDim(2) = 6; - else - GridDim(1) = 1; GridDim(2) = 12; - end - elseif isequal(numel(Grid2Elec{g}), 8) - e3 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(3)]),:); - e4 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(4)]),:); - e5 = elec.elecpos(match_str(elec.label, [GridDescript{g} num2str(5)]),:); - d3to4 = sqrt(sum((e3-e4).^2)); % distance of elec 3 to 4 - d4to5 = sqrt(sum((e4-e5).^2)); % distance of elec 4 to 5 - if d4to5 > 2*d3to4 % break between e4 and e5 - GridDim(1) = 2; GridDim(2) = 4; - else - GridDim(1) = 1; GridDim(2) = 8; - end - else - GridDim(1) = 1; GridDim(2) = numel(Grid2Elec{g}); - % ft_error('At least one of the electrode tracts or grids has dimensions that are not supported by write_bioimage_mgrid. If electrodes are missing from a grid, enter NaN(1,3) for electrode position'); - end - fprintf('assuming %s has %d x %d dimensions\n', GridDescript{g}, GridDim(1), GridDim(2)); % feedback of the grid dimensions + elec_g = elec; + elec_g.label = elec.label(Grid2Elec{g}); + elec_g.elecpos = elec.elecpos(Grid2Elec{g}, :); + GridDim = determine_griddim(elec_g); fprintf(fid, [' ' num2str(GridDim(1)) ' ' num2str(GridDim(2)) '\n']); fprintf(fid, '#Electrode Spacing\n'); diff --git a/external/fieldtrip/fileio/private/write_gdf.m b/external/fieldtrip/fileio/private/write_gdf.m index afa79f41..8606e8d5 100644 --- a/external/fieldtrip/fileio/private/write_gdf.m +++ b/external/fieldtrip/fileio/private/write_gdf.m @@ -77,6 +77,12 @@ function write_gdf(filename, hdr, data) % will be terrible for appending data... digMin = double(min(data,[],2)); digMax = double(max(data,[],2)); + +% adjust the the digital min/max a bit, otherwise the biosig reading code +% will return NaNs for the most extreme values +digMin = digMin - 1e5.*eps(digMin); +digMax = digMax + 1e5.*eps(digMax); + physMin = digMin; physMax = digMax; diff --git a/external/fieldtrip/fileio/private/yokogawa2grad_new.m b/external/fieldtrip/fileio/private/yokogawa2grad_new.m index 5025361c..f80b8ac5 100644 --- a/external/fieldtrip/fileio/private/yokogawa2grad_new.m +++ b/external/fieldtrip/fileio/private/yokogawa2grad_new.m @@ -2,12 +2,12 @@ % YOKOGAWA2GRAD_NEW converts the position and weights of all coils that % compromise a gradiometer system into a structure that can be used -% by FieldTrip. This implementation uses the new "yokogawa_meg_reader" +% by FieldTrip. This implementation uses the new "yokogawa_meg_reader" % toolbox. % % See also FT_READ_HEADER, CTF2GRAD, BTI2GRAD, FIF2GRAD, YOKOGAWA2GRAD -% Copyright (C) 2005-2012, Robert Oostenveld +% Copyright (C) 2005-2012, Robert Oostenveld % Copyright (C) 2010, Tilmann Sander-Thoemmes % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org @@ -42,8 +42,8 @@ hdr = hdr.orig; % use the original header, not the FieldTrip header end -% The "channel_info.channel(i)" structure contains, s. sepcifications in -% Yokogawa MEG Reader Toolbox 1.4 specifications.pdf. +% The "channel_info.channel(i)" structure contains the details of coils. +% For the sepcifications, see Yokogawa MEG Reader Toolbox 1.5 specifications.pdf. % type, type of sensor % data.x (in m, inner coil) % data.y (in m, inner coil) @@ -57,10 +57,10 @@ % data.zdir orientation of inner coil (theta in deg: angle to z-axis) % data.xdir orientation of inner coil (phi in deg: angle to x-axis) -% for planar gradiometers +% for planar gradiometers % Note, that Yokogawa planar gradiometers contain two coils perpendicular to -% the sphere, i.e. the planar gradiometers normal is the spheres tangential. -% Therefore the definition of an inner coil makes sense here in contrast to +% the sphere, i.e. the planar gradiometers normal is the spheres tangential. +% Therefore the definition of an inner coil makes sense here in contrast to % planar gradiometers from Neuromag, where the gradiometer normal is radial. % data.zdir1 orientation of inner coil (theta in deg: angle to z-axis) % data.xdir1 orientation of inner coil (phi in deg: angle to x-axis) @@ -76,16 +76,23 @@ type = [ch_info.type]; handles = definehandles; -% get all axial grads, planar grads, and magnetometers. -% reference channels without position information are excluded. + +%% get all axial grads, planar grads, and magnetometers. +%% reference channels without position information are excluded. +%grad_ind = [1:hdr.channel_count]; +%isgrad = (type==handles.AxialGradioMeter | type==handles.PlannerGradioMeter | ... +% type==handles.MagnetoMeter); +%isref = (type==handles.RefferenceAxialGradioMeter | type==handles.RefferencePlannerGradioMeter | ... +% type==handles.RefferenceMagnetoMeter); +%for i = 1: hdr.channel_count +% if isref(i) && sum( ch_info( i ).data.x^2 + ch_info( i ).data.y^2 + ch_info( i ).data.z^2 ) > 0.0, isgrad(i) = 1; end; +%end + + +% get all axial grads, planar grads, and magnetometers. +% All reference channels are excluded even if some of them have position information. grad_ind = [1:hdr.channel_count]; -isgrad = (type==handles.AxialGradioMeter | type==handles.PlannerGradioMeter | ... - type==handles.MagnetoMeter); -isref = (type==handles.RefferenceAxialGradioMeter | type==handles.RefferencePlannerGradioMeter | ... - type==handles.RefferenceMagnetoMeter); -for i = 1: hdr.channel_count - if isref(i) && sum( ch_info( i ).data.x^2 + ch_info( i ).data.y^2 + ch_info( i ).data.z^2 ) > 0.0, isgrad(i) = 1; end; -end +isgrad = (type==handles.AxialGradioMeter | type==handles.PlannerGradioMeter | type==handles.MagnetoMeter); grad_ind = grad_ind(isgrad); grad_nr = size(grad_ind,2); @@ -94,38 +101,38 @@ grad.coilpos = zeros(2*grad_nr,3); grad.coilori = zeros(2*grad_nr,3); -% define gradiometer and magnetometer +% define gradiometer and magnetometer for i = 1:grad_nr ch_ind = grad_ind(i); grad.coilpos(i,1) = ch_info(ch_ind).data.x*100; % cm grad.coilpos(i,2) = ch_info(ch_ind).data.y*100; % cm grad.coilpos(i,3) = ch_info(ch_ind).data.z*100; % cm grad.chanpos = grad.coilpos(1:grad_nr,:); - - if ch_info(ch_ind).type==handles.AxialGradioMeter || ch_info(ch_ind).type==handles.RefferenceAxialGradioMeter + + if ch_info(ch_ind).type==handles.AxialGradioMeter || ch_info(ch_ind).type==handles.RefferenceAxialGradioMeter baseline = ch_info(ch_ind).data.baseline; - + ori_1st = [ch_info(ch_ind).data.zdir ch_info(ch_ind).data.xdir ]; % polar to x,y,z coordinates ori_1st = ... [sin(ori_1st(:,1)/180*pi).*cos(ori_1st(:,2)/180*pi) ... sin(ori_1st(:,1)/180*pi).*sin(ori_1st(:,2)/180*pi) ... cos(ori_1st(:,1)/180*pi)]; - + grad.coilori(i,:) = ori_1st; - + grad.coilpos(i+grad_nr,:) = [grad.coilpos(i,:)+ori_1st*baseline*100]; grad.coilori(i+grad_nr,:) = -ori_1st; elseif ch_info(ch_ind).type==handles.PlannerGradioMeter || ch_info(ch_ind).type==handles.RefferencePlannerGradioMeter baseline = ch_info(ch_ind).data.baseline; - + ori_1st = [ch_info(ch_ind).data.zdir1 ch_info(ch_ind).data.xdir1 ]; % polar to x,y,z coordinates ori_1st = ... [sin(ori_1st(:,1)/180*pi).*cos(ori_1st(:,2)/180*pi) ... sin(ori_1st(:,1)/180*pi).*sin(ori_1st(:,2)/180*pi) ... cos(ori_1st(:,1)/180*pi)]; - + grad.coilori(i,:) = ori_1st; ori_1st_to_2nd = [ch_info(ch_ind).data.zdir2 ch_info(ch_ind).data.xdir2 ]; @@ -134,10 +141,10 @@ [sin(ori_1st_to_2nd(:,1)/180*pi).*cos(ori_1st_to_2nd(:,2)/180*pi) ... sin(ori_1st_to_2nd(:,1)/180*pi).*sin(ori_1st_to_2nd(:,2)/180*pi) ... cos(ori_1st_to_2nd(:,1)/180*pi)]; - + grad.coilpos(i+grad_nr,:) = [grad.coilpos(i,:)+ori_1st_to_2nd*baseline*100]; grad.coilori(i+grad_nr,:) = -ori_1st; - else % magnetometer + else % magnetometer ori_1st = [ch_info(ch_ind).data.zdir ch_info(ch_ind).data.xdir ]; % polar to x,y,z coordinates ori_1st = ... @@ -148,11 +155,11 @@ grad.coilori(i,:) = ori_1st; grad.coilpos(i+grad_nr,:) = [0 0 0]; - grad.coilori(i+grad_nr,:) = [0 0 0]; - end - + grad.coilori(i+grad_nr,:) = [0 0 0]; + end + grad.chanori = grad.coilori(1:grad_nr,:); - + end % Define the pair of 1st and 2nd coils for each gradiometer @@ -161,7 +168,7 @@ % for mangetometers change tra as there is no second coil for i = 1:grad_nr ch_ind = grad_ind(i); - if ch_info(ch_ind).type==handles.MagnetoMeter + if ch_info(ch_ind).type==handles.MagnetoMeter grad.tra(i,grad_nr+i) = 0; end end @@ -169,9 +176,9 @@ % the gradiometer labels should be consistent with the channel labels in % read_yokogawa_header, the predefined list of channel names in ft_senslabel % and with ft_channelselection: -% but it is ONLY consistent with read_yokogawa_header as NO FIXED relation -% between channel index and type of channel exists for Yokogawa systems. -% Therefore all have individual label sequences: Support in ft_senslabel +% but it is ONLY consistent with read_yokogawa_header as NO FIXED relation +% between channel index and type of channel exists for Yokogawa systems. +% Therefore all have individual label sequences: Support in ft_senslabel % is only partial. if ~isempty(label) grad.label = label(grad_ind)'; @@ -181,7 +188,7 @@ for i=1:length(label) label{i,1} = sprintf('AG%03d', i); end - grad.label = label; + grad.label = label; end grad.unit = 'cm'; @@ -206,9 +213,9 @@ handles.EcgChannel = -3; handles.EtcChannel = -4; handles.NonMegChannelNameLength = 32; -handles.DefaultMagnetometerSize = (4.0/1000.0); % ????4.0mm???????` -handles.DefaultAxialGradioMeterSize = (15.5/1000.0); % ???a15.5mm???~?? -handles.DefaultPlannerGradioMeterSize = (12.0/1000.0); % ????12.0mm???????` +handles.DefaultMagnetometerSize = (4.0/1000.0); % Square of 4.0mm in length +handles.DefaultAxialGradioMeterSize = (15.5/1000.0); % Circle of 15.5mm in diameter +handles.DefaultPlannerGradioMeterSize = (12.0/1000.0); % Square of 12.0mm in length handles.AcqTypeContinuousRaw = 1; handles.AcqTypeEvokedAve = 2; handles.AcqTypeEvokedRaw = 3; @@ -231,4 +238,3 @@ handles.sqd.source_info = []; handles.sqd.mri_info = []; handles.mri = []; - diff --git a/external/fieldtrip/forward/README b/external/fieldtrip/forward/README index dd97c7fa..b104ba41 100644 --- a/external/fieldtrip/forward/README +++ b/external/fieldtrip/forward/README @@ -2,15 +2,25 @@ This is the FieldTrip FORWARD module. It contains functions for forward modeling of the EEG and MEG field distributions with a choise of volume conduction models, including spherical and realistic models. -For more information please visit http://www.ru.nl/neuroimaging/fieldtrip - -The FieldTrip software is free but copyrighted software, distributed -under the terms of the GNU General Public Licence as published by -the Free Software Foundation (either version 2, or at your option -any later version). See the file COPYING for more details. +For more information please visit http://www.fieldtriptoolbox.org/development/modules +------------------------------------------------------------------------------- Copyright (C) 2008-2009, Donders Institute for Brain, Cognition and Behaviour, The Netherlands (DCCN, DCC, DCN) Copyright (C) 2003-2008, F.C. Donders Centre for Cognitive Neuroimaging, Nijmegen, The Netherlands (FCDC) Copyright (C) 2003-2005, Center for Sensory-Motor Interaction, Aalborg University, Denmark (SMI) Copyright (C) 1999-2003, Department of Medical Physics, Radboud University Nijmegen, The Netherlands (MBFYS) +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see 0 && isfield(input, 'trial') % check for raw data now only cfg = []; cfg.channel = addlabel; + cfg.showcallinfo = showcallinfo; data_unused = ft_selectdata(cfg, input); % use an anonymous function to test for the presence of NaNs in the input data hasnan = @(x) any(isnan(x(:))); diff --git a/external/fieldtrip/forward/ft_average_sens.m b/external/fieldtrip/forward/ft_average_sens.m index bda90bda..c7d957cf 100644 --- a/external/fieldtrip/forward/ft_average_sens.m +++ b/external/fieldtrip/forward/ft_average_sens.m @@ -46,7 +46,8 @@ for i=1:nsens newsens(i) = ft_datatype_sens(sens(i)); end -sens = newsens; clear newsens +sens = newsens; +clear newsens; fiducials = fixpos(fiducials); @@ -74,16 +75,16 @@ end pos = detrend(sens(1).chanpos, 'constant'); -[u s v]= svd(pos'*pos); +[u, s, v]= svd(pos'*pos); % starting with the second PC works a little better (e.g. on BTi) x1 = pos*u(:, 2); x2 = pos*u(:, 1); % detemine the indices of three reference sensors that are close to the three principal axes -[m ind1] = min(x1); -[m ind2] = max(x1); -[m ind3] = max(abs(x2 - mean(x2([ind1 ind2])))); +[dum, ind1] = min(x1); +[dum, ind2] = max(x1); +[dum, ind3] = max(abs(x2 - mean(x2([ind1 ind2])))); % compute the mean of the three reference sensors for the principal axes mean1 = [0 0 0]; @@ -206,15 +207,15 @@ switch afiducials.unit case 'mm' c = 0.1; - case 'cm'; + case 'cm' c = 1; - case 'dm'; + case 'dm' c = 10; case 'm' c = 100; end - [upos, ind] = unique(round(c*afiducials.pos/tolerance), 'rows'); + [dum, ind] = unique(round(c*afiducials.pos/tolerance), 'rows'); afiducials.pos = afiducials.pos(ind, :); diff --git a/external/fieldtrip/forward/ft_compute_leadfield.m b/external/fieldtrip/forward/ft_compute_leadfield.m index 3b9c1703..280cc5a2 100644 --- a/external/fieldtrip/forward/ft_compute_leadfield.m +++ b/external/fieldtrip/forward/ft_compute_leadfield.m @@ -264,31 +264,11 @@ end case 'openmeeg' - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % use code from OpenMEEG - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - ft_hastoolbox('openmeeg', 1); - - if isfield(headmodel,'mat') - % switch the non adaptive algorithm on - nonadaptive = true; % HACK : this is hardcoded at the moment - dsm = openmeeg_dsm(dippos, headmodel, nonadaptive); - [h2mm, s2mm]= openmeeg_megm(dippos, headmodel, sens); - - %if isfield(headmodel, 'mat') - lf = s2mm+h2mm*(headmodel.mat*dsm); - %else - % ft_error('No system matrix is present, BEM head model not calculated yet') - %end - if isfield(sens, 'tra') - % compute the leadfield for each gradiometer (linear combination of coils) - lf = sens.tra * lf; - end - else - ft_warning('No system matrix is present, Calling the Nemo Lab pipeline') - lf = leadfield_openmeeg(dippos, headmodel, sens); - end - + % OpenMEEG lead field already computed in ft_prepare_leadfield; + % load here so any post-processing options (e.g. normalization) may + % be applied + lf = ft_getopt(varargin, 'lf'); + case {'infinite_magneticdipole', 'infinite'} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % magnetic dipole instead of electric (current) dipole in an infinite vacuum @@ -443,17 +423,11 @@ lf = eeg_leadfieldb(dippos, sens.elecpos, headmodel); case 'openmeeg' - ft_hastoolbox('openmeeg', 1) - if isfield(headmodel, 'mat') - % switch the non adaptive algorithm on - nonadaptive = true; % HACK this is hardcoded at the moment - dsm = openmeeg_dsm(dippos, headmodel, nonadaptive); - lf = headmodel.mat*dsm; - else - disp('No system matrix is present, calling the Nemo Lab pipeline...') - lf = leadfield_openmeeg(dippos, headmodel, sens); - end - + % OpenMEEG lead field already computed in ft_prepare_leadfield; + % load here so any post-processing options (e.g. normalization) may + % be applied + lf = ft_getopt(varargin, 'lf'); + case 'metufem' p3 = zeros(Ndipoles * 3, 6); for i = 1:Ndipoles @@ -551,7 +525,7 @@ lf(:, (3*ii-2):(3*ii)) = u * s * v'; else % if not backprojected, the new leadfield has a different dimension - if ii==1, + if ii==1 newlf = zeros(size(lf,1), Ndipoles*reducerank); origrank = size(lf,2)./Ndipoles; end @@ -559,7 +533,7 @@ end end - if ~istrue(backproject), + if ~istrue(backproject) lf = newlf; end clear newlf; diff --git a/external/fieldtrip/forward/ft_convert_units.m b/external/fieldtrip/forward/ft_convert_units.m index 47279027..b1ac7b89 100644 --- a/external/fieldtrip/forward/ft_convert_units.m +++ b/external/fieldtrip/forward/ft_convert_units.m @@ -46,9 +46,6 @@ % 2) determine the requested scaling factor to obtain the output units % 3) try to apply the scaling to the known geometrical elements in the input object -% ensure the correct number of input and output arguments -% narginchk(2,inf); % see below -% nargoutchk(0,1); % the "target" input argument has been made required in Aug 2017 % prior to that it was also possible to use this function to estimate units diff --git a/external/fieldtrip/forward/ft_determine_units.m b/external/fieldtrip/forward/ft_determine_units.m index 8c5864a3..37a49ff5 100644 --- a/external/fieldtrip/forward/ft_determine_units.m +++ b/external/fieldtrip/forward/ft_determine_units.m @@ -38,9 +38,6 @@ % % $Id$ -% ensure the correct number of input and output arguments -% narginchk(1,1); -% nargoutchk(0,1); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/forward/ft_headmodel_concentricspheres.m b/external/fieldtrip/forward/ft_headmodel_concentricspheres.m index c7e39907..8867c4b5 100644 --- a/external/fieldtrip/forward/ft_headmodel_concentricspheres.m +++ b/external/fieldtrip/forward/ft_headmodel_concentricspheres.m @@ -18,14 +18,14 @@ % Use as % headmodel = ft_headmodel_concentricspheres(mesh, ...) % -% Optional input arguments should be specified in key-value pairs and can -% include +% Optional input arguments should be specified in key-value pairs and can include % conductivity = vector with the conductivity of each compartment % fitind = vector with indices of the surfaces to use in fitting the center of the spheres +% order = number of iterations in series expansion (default = 60) % % See also FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD -% Copyright (C) 2012-2013, Donders Centre for Cognitive Neuroimaging, Nijmegen, NL +% Copyright (C) 2012-2018, Donders Centre for Cognitive Neuroimaging, Nijmegen, NL % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -48,6 +48,7 @@ % get the optional input arguments conductivity = ft_getopt(varargin, 'conductivity'); % default is determined below fitind = ft_getopt(varargin, 'fitind', 'all'); +order = ft_getopt(varargin, 'order', 60); if any(strcmp(varargin(1:2:end), 'unit')) || any(strcmp(varargin(1:2:end), 'units')) % the geometrical units should be specified in the input mesh @@ -58,7 +59,7 @@ % assume that it is a Nx3 array with vertices % convert it to a structure, this is needed to determine the units further down mesh = struct('pos', mesh); -elseif isstruct(mesh) && isfield(mesh,'bnd') +elseif isstruct(mesh) && isfield(mesh, 'bnd') % take the triangulated surfaces from the input structure mesh = mesh.bnd; end @@ -129,5 +130,26 @@ end for i=1:numel(mesh) - fprintf('concentric sphere %d: radius = %.1f, conductivity = %f\n', i, headmodel.r(i), headmodel.cond(i)); + fprintf('concentric sphere %d: radius = %f, conductivity = %f\n', i, headmodel.r(i), headmodel.cond(i)); end + +% replicate the spheres if there are fewer than four +switch numel(mesh) + case 1 + headmodel.r = [headmodel.r(1) headmodel.r(1) headmodel.r(1) headmodel.r(1)]; + headmodel.cond = [headmodel.cond(1) headmodel.cond(1) headmodel.cond(1) headmodel.cond(1)]; + case 2 + headmodel.r = [headmodel.r(1) headmodel.r(2) headmodel.r(2) headmodel.r(2)]; + headmodel.cond = [headmodel.cond(1) headmodel.cond(2) headmodel.cond(2) headmodel.cond(2)]; + case 3 + headmodel.r = [headmodel.r(1) headmodel.r(2) headmodel.r(3) headmodel.r(3)]; + headmodel.cond = [headmodel.cond(1) headmodel.cond(2) headmodel.cond(3) headmodel.cond(3)]; + case 4 + headmodel.r = [headmodel.r(1) headmodel.r(2) headmodel.r(3) headmodel.r(4)]; + headmodel.cond = [headmodel.cond(1) headmodel.cond(2) headmodel.cond(3) headmodel.cond(4)]; + otherwise + error('not more than 4 spheres are supported'); +end + +% precompute the parameters for the series expansion +headmodel.t = eeg_leadfield4_prepare(headmodel, order); diff --git a/external/fieldtrip/forward/ft_headmodel_localspheres.m b/external/fieldtrip/forward/ft_headmodel_localspheres.m index 3bed7ba4..12a03c4c 100644 --- a/external/fieldtrip/forward/ft_headmodel_localspheres.m +++ b/external/fieldtrip/forward/ft_headmodel_localspheres.m @@ -67,7 +67,7 @@ % replace pnt with pos mesh = fixpos(mesh); -if ~isstruct(mesh) || ~isfield(mesh, 'pos') +if ~isstruct(mesh) || numel(mesh)>1 || ~isfield(mesh, 'pos') ft_error('the input mesh should be a set of points or a single triangulated surface') end diff --git a/external/fieldtrip/forward/ft_headmodel_openmeeg.m b/external/fieldtrip/forward/ft_headmodel_openmeeg.m index 2b810e1a..b7468eee 100644 --- a/external/fieldtrip/forward/ft_headmodel_openmeeg.m +++ b/external/fieldtrip/forward/ft_headmodel_openmeeg.m @@ -1,4 +1,4 @@ -function headmodel = ft_headmodel_openmeeg(mesh, varargin) +function headmodel = ft_headmodel_openmeeg(bnd, varargin) % FT_HEADMODEL_OPENMEEG creates a volume conduction model of the % head using the boundary element method (BEM). This function takes @@ -6,7 +6,7 @@ % returns as output a volume conduction model which can be used to % compute leadfields. % -% This function implements +% This function implements % Gramfort et al. OpenMEEG: opensource software for quasistatic % bioelectromagnetics. Biomedical engineering online (2010) vol. 9 (1) pp. 45 % http://www.biomedical-engineering-online.com/content/9/1/45 @@ -15,25 +15,31 @@ % Kybic et al. Generalized head models for MEG/EEG: boundary element method % beyond nested volumes. Phys. Med. Biol. (2006) vol. 51 pp. 1333-1346 % doi:10.1088/0031-9155/51/5/021 -% -% The implementation in this function is derived from the the OpenMEEG project -% and uses external command-line executables. See http://gforge.inria.fr/projects/openmeeg -% and http://gforge.inria.fr/frs/?group_id=435. +% +% This link with FieldTrip is derived from the OpenMEEG project +% with contributions from Daniel Wong and Sarang Dalal, and uses external +% command-line executables. See http://openmeeg.github.io/ % % Use as -% headmodel = ft_headmodel_openmeeg(mesh, ...) +% headmodel = ft_headmodel_openmeeg(bnd, ...) % % Optional input arguments should be specified in key-value pairs and can % include % conductivity = vector, conductivity of each compartment +% tissue = tissue labels for each compartment % -% See also FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD +% See also FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD, FT_SYSMAT_OPENMEEG, +% FT_SENSINTERP_OPENMEEG %$Id$ ft_hastoolbox('openmeeg', 1); % add to path (if not yet on path) openmeeg_license; % show the license (only once) prefix = om_checkombin; % check the installation of the binaries +if(~ispc) % if Linux/Mac, set number of threads + omp_num_threads = feature('numCores'); + prefix = ['export OMP_NUM_THREADS=' num2str(omp_num_threads) ' && ' prefix]; +end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % the first part is largely shared with the dipoli and bemcp implementation @@ -41,53 +47,71 @@ % get the optional arguments conductivity = ft_getopt(varargin, 'conductivity'); +tissue = ft_getopt(varargin, 'tissue'); % copy the boundaries from the mesh into the volume conduction model -if isfield(mesh, 'bnd') - mesh = mesh.bnd; +if isfield(bnd, 'bnd') + bnd = bnd.bnd; end % rename pnt into pos -mesh = fixpos(mesh); +bnd = fixpos(bnd); % start with an empty volume conductor headmodel = []; -headmodel.bnd = mesh; % determine the number of compartments -numboundaries = length(headmodel.bnd); +numboundaries = length(bnd); % determine the desired nesting of the compartments -order = surface_nesting(headmodel.bnd, 'outsidefirst'); +order = surface_nesting(bnd, 'outsidefirst'); % rearrange boundaries and conductivities -if numel(headmodel.bnd)>1 - fprintf('reordering the boundaries to: '); - fprintf('%d ', order); - fprintf('\n'); - % update the order of the compartments - headmodel.bnd = headmodel.bnd(order); +if numel(bnd)>1 + fprintf('reordering the boundaries to: '); + fprintf('%d ', order); + fprintf('\n'); + % update the order of the compartments + bnd = bnd(order); end if isempty(conductivity) - ft_warning('No conductivity is declared, Assuming standard values\n') - if numboundaries == 1 - conductivity = 1; - elseif numboundaries == 3 - % skin/skull/brain - conductivity = [1 1/80 1] * 0.33; - else - ft_error('Conductivity values are required for 2 shells. More than 3 shells not allowed') - end - headmodel.cond = conductivity; + ft_warning('No conductivity is declared, using default values\n') + if numboundaries == 1 + conductivity = 1; + elseif numboundaries == 3 + % skin/skull/brain + conductivity = [0.33 0.0042 0.33] + elseif numboundaries == 4 + conductivity = [0.33 0.0042 1 0.33] + else + ft_error(['Conductivity values are required for ' num2str(numboundaries) ' shells']) + end + headmodel.cond = conductivity; else - if numel(conductivity)~=numboundaries - ft_error('a conductivity value should be specified for each compartment'); - end - % update the order of the compartments - headmodel.cond = conductivity(order); + if numel(conductivity)~=numboundaries + ft_error('a conductivity value should be specified for each compartment'); + end + % update the order of the compartments + headmodel.cond = conductivity(order); end +% assign default tissue labels if none provided +if(isempty(tissue)) + switch(numboundaries) + case 3 + tissue = {'scalp','skull','brain'}; + case 4 + tissue = {'scalp','skull','CSF','brain'}; + otherwise + tissue = strcat({'domain'},num2str((1:numboundaries)')); + end +else + tissue = tissue(order); +end +headmodel.tissue = tissue; + + headmodel.skin_surface = 1; headmodel.source = numboundaries; @@ -95,136 +119,92 @@ % this uses an implementation that was contributed by INRIA Odyssee Team %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% store the current path and change folder to the temporary one -tmpfolder = cd; -bndom = headmodel.bnd; +workdir = fullfile(tempdir,['ft_om_' datestr(now,'ddmmyyHHMMSSFFF')]); +mkdir(workdir); try - cd(tempdir) - - % write the triangulations to file - bndfile = {}; - - for ii=1:length(bndom) - % check if vertices' normals are inward oriented - ok = checknormals(bndom(ii)); - if ~ok - % Flip faces for openmeeg convention (inwards normals) - fprintf('flipping normals'' direction\n') - bndom(ii).tri = fliplr(bndom(ii).tri); + % Write the triangulations to file, named after tissue type. + % OpenMEEG v2.3 and up internally adjusts the convention for surface + % normals, but OpenMEEG v2.2 expects surface normals to point inwards; + % this checks and corrects if needed + bndfile = fullfile(workdir,strcat(tissue,'.tri')); + for ii=1:length(bnd) + ok = checknormals(bnd(ii)); + if ~ok + % Flip faces for openmeeg convention (inwards normals) + bndtmp = bnd(ii); + bndtmp.tri = fliplr(bnd(ii).tri); + + ok = checknormals(bndtmp); + if(ok) + fprintf('flipping normals for OpenMEEG''s convention\n') + bnd(ii).tri = bndtmp.tri; + else + % if still not ok after flip, throw a warning + ft_warning('neither orientation of surface normals passes check... leaving as is.') + end + clear bndtmp + end + + om_save_tri(bndfile{ii}, bnd(ii).pos, bnd(ii).tri); + end + + % retain surfaces in headmodel structure (after possible normal flip) + headmodel.bnd = bnd; + + + condfile = fullfile(workdir,'om.cond'); + geomfile = fullfile(workdir,'om.geom'); + hmfile = fullfile(workdir,'hm.bin'); + hminvfile = fullfile(workdir,'hminv.bin'); + + % write conductivity and mesh files + bndlabel = {}; + for i=1:length(bnd) + [dum,bndlabel{i}] = fileparts(bndfile{i}); end - end - - for ii=1:length(headmodel.bnd) - [junk,tname] = fileparts(tempname); - bndfile{ii} = [tname '.tri']; - om_save_tri(bndfile{ii}, bndom(ii).pos, bndom(ii).tri); - end - - % these will hold the shell script and the inverted system matrix - [tmp,tname] = fileparts(tempname); - if ~ispc - exefile = [tname '.sh']; - else - exefile = [tname '.bat']; - end - - [tmp,tname] = fileparts(tempname); - condfile = [tname '.cond']; - [tmp,tname] = fileparts(tempname); - geomfile = [tname '.mesh']; - [tmp,tname] = fileparts(tempname); - hmfile = [tname '.bin']; - [tmp,tname] = fileparts(tempname); - hminvfile = [tname '.bin']; - - % write conductivity and mesh files - om_write_geom(geomfile,bndfile); - om_write_cond(condfile,headmodel.cond); - - % Exe file - efid = fopen(exefile, 'w'); - omp_num_threads = feature('numCores'); - if ~ispc - fprintf(efid,'#!/usr/bin/env bash\n'); - fprintf(efid,['export OMP_NUM_THREADS=',num2str(omp_num_threads),'\n']); - fprintf(efid,[prefix 'om_assemble -HM ./' geomfile ' ./' condfile ' ./' hmfile ' 2>&1 > /dev/null\n']); - fprintf(efid,[prefix 'om_minverser ./' hmfile ' ./' hminvfile ' 2>&1 > /dev/null\n']); - else - fprintf(efid,[prefix 'om_assemble -HM ./' geomfile ' ./' condfile ' ./' hmfile '\n']); - fprintf(efid,[prefix 'om_minverser ./' hmfile ' ./' hminvfile '\n']); - end - - fclose(efid); - - if ~ispc - dos(sprintf('chmod +x %s', exefile)); - end -catch - cd(tmpfolder) - rethrow(lasterror) -end -try - % execute OpenMEEG and read the resulting file - if ispc - dos(exefile); - else - version = om_getgccversion; - if version>3 - dos(['./' exefile]); - else - ft_error('non suitable GCC compiler version (must be superior to gcc3)'); + om_write_geom(geomfile,bndfile,bndlabel); + om_write_cond(condfile,headmodel.cond,bndlabel); + + om_status = system([prefix 'om_assemble -HM ' geomfile ' ' condfile ' ' hmfile]); + if(om_status ~= 0) % status = 0 if successful + ft_error('Aborting OpenMEEG pipeline due to above error.'); end - end - headmodel.mat = om_load_sym(hminvfile,'binary'); - cleaner(headmodel,bndfile,condfile,geomfile,hmfile,hminvfile,exefile) - cd(tmpfolder) + + headmodel.mat = inv(om_load_sym(hmfile,'binary')); + + rmdir(workdir,'s'); % remove workdir with intermediate files catch - ft_warning('an error ocurred while running OpenMEEG'); - disp(lasterr); - cleaner(headmodel,bndfile,condfile,geomfile,hmfile,hminvfile,exefile) - cd(tmpfolder) + disp(lasterr); + rmdir(workdir,'s'); % remove workdir with intermediate files + ft_error('an error occurred while running OpenMEEG'); end % remember the type of volume conduction model headmodel.type = 'openmeeg'; - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function cleaner(headmodel,bndfile,condfile,geomfile,hmfile,hminvfile,exefile) -% delete the temporary files -for i=1:length(headmodel.bnd) - delete(bndfile{i}) -end -delete(condfile); -delete(geomfile); -delete(hmfile); -delete(hminvfile); -delete(exefile); - function ok = checknormals(bnd) +points = bnd.pos; +faces = bnd.tri; + % FIXME: this method is rigorous only for star shaped surfaces ok = 0; -pos = bnd.pos; -tri = bnd.tri; % translate to the center -org = mean(pos,1); -pos(:,1) = pos(:,1) - org(1); -pos(:,2) = pos(:,2) - org(2); -pos(:,3) = pos(:,3) - org(3); +org = mean(points,1); +points(:,1) = points(:,1) - org(1); +points(:,2) = points(:,2) - org(2); +points(:,3) = points(:,3) - org(3); -w = sum(solid_angle(pos, tri)); +w = sum(solid_angle(points, faces)); if w<0 && (abs(w)-4*pi)<1000*eps - ok = 0; - ft_warning('your normals are outwards oriented\n') + ok = 0; + disp('Your surface normals are outwards oriented...') elseif w>0 && (abs(w)-4*pi)<1000*eps - ok = 1; -% ft_warning('your normals are inwards oriented') + ok = 1; + % ft_warning('your normals are inwards oriented') else - ft_error('your surface probably is irregular\n') - ok = 0; -end + ft_error('your surface probably is irregular\n') + ok = 0; +end \ No newline at end of file diff --git a/external/fieldtrip/forward/ft_headmodel_singleshell.m b/external/fieldtrip/forward/ft_headmodel_singleshell.m index e9bb8441..d308ef92 100644 --- a/external/fieldtrip/forward/ft_headmodel_singleshell.m +++ b/external/fieldtrip/forward/ft_headmodel_singleshell.m @@ -3,24 +3,27 @@ % FT_HEADMODEL_SINGLESHELL creates a volume conduction model of the % head for MEG based on a realistic shaped surface of the inside of % the skull. -% +% % The method implemented in this function allows for a simple and % fast method for the MEG forward calculation for one shell of arbitrary % shape, based on a correction of the lead field for a spherical % volume conductor by a superposition of basis functions, gradients % of harmonic functions constructed from spherical harmonics. -% +% % This function implements % G. Nolte, "The magnetic lead field theorem in the quasi-static % approximation and its use for magnetoencephalography forward calculation % in realistic volume conductors", Phys Med Biol. 2003 Nov 21;48(22):3637-52. -% +% % Use as % headmodel = ft_headmodel_singleshell(mesh, ...) % +% Optional input arguments should be specified in key-value pairs and can include +% order = number of iterations in series expansion (default = 10) +% % See also FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD -% Copyright (C) 2012, Donders Centre for Cognitive Neuroimaging, Nijmegen, NL +% Copyright (C) 2012-2018, Donders Centre for Cognitive Neuroimaging, Nijmegen, NL % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -40,6 +43,9 @@ % % $Id$ +% order of spherical spherical harmonics +order = ft_getopt(varargin, 'order', 10); + if isnumeric(mesh) && size(mesh,2)==3 % assume that it is a Nx3 array with vertices % convert it to a structure, this is needed to determine the units further down @@ -52,14 +58,15 @@ % replace pnt with pos mesh = fixpos(mesh); -if ~isstruct(mesh) || ~isfield(mesh, 'pos') +if ~isstruct(mesh) || numel(mesh)>1 || ~isfield(mesh, 'pos') ft_error('the input mesh should be a set of points or a single triangulated surface') end % represent the mesh in a headmodel strucure % the computational parameters will be added later on by ft_prepare_vol_sens -headmodel = []; -headmodel.bnd = mesh; -headmodel.type = 'singleshell'; -headmodel = ft_determine_units(headmodel); +headmodel = []; +headmodel.bnd = mesh; +headmodel.type = 'singleshell'; +headmodel.order = order; +headmodel = ft_determine_units(headmodel); diff --git a/external/fieldtrip/forward/ft_headmodel_singlesphere.m b/external/fieldtrip/forward/ft_headmodel_singlesphere.m index 48468457..ae861eeb 100644 --- a/external/fieldtrip/forward/ft_headmodel_singlesphere.m +++ b/external/fieldtrip/forward/ft_headmodel_singlesphere.m @@ -16,7 +16,7 @@ % % See also FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD -% FIXME document the EEG case +% FIXME document both EEG and MEG case % Copyright (C) 2012-2013, Donders Centre for Cognitive Neuroimaging, Nijmegen, NL % @@ -58,7 +58,7 @@ % replace pnt with pos mesh = fixpos(mesh); -if ~isstruct(mesh) || ~isfield(mesh, 'pos') +if ~isstruct(mesh) || numel(mesh)>1 || ~isfield(mesh, 'pos') ft_error('the input mesh should be a set of points or a single triangulated surface') end diff --git a/external/fieldtrip/forward/ft_prepare_vol_sens.m b/external/fieldtrip/forward/ft_prepare_vol_sens.m index 3976a994..e222ed01 100644 --- a/external/fieldtrip/forward/ft_prepare_vol_sens.m +++ b/external/fieldtrip/forward/ft_prepare_vol_sens.m @@ -17,7 +17,6 @@ % % Additional options should be specified in key-value pairs and can be % 'channel' cell-array with strings (default = 'all') -% 'order' number, for single shell "Nolte" model (default = 10) % % The detailed behaviour of this function depends on whether the input % consists of EEG or MEG and furthermoree depends on the type of volume @@ -69,7 +68,6 @@ % get the optional input arguments % fileformat = ft_getopt(varargin, 'fileformat'); channel = ft_getopt(varargin, 'channel', sens.label); % cell-array with channel labels, default is all -order = ft_getopt(varargin, 'order', 10); % order of expansion for Nolte method; 10 should be enough for real applications; in simulations it makes sense to go higher % ensure that the sensor description is up-to-date (Aug 2011) sens = ft_datatype_sens(sens); @@ -285,7 +283,14 @@ end % estimate center and radius - [center,radius] = fitsphere(headmodel.bnd.pos); + [center, radius] = fitsphere(headmodel.bnd.pos); + + % order of spherical spherical harmonics, for 'real' realistic volume conductors order=10 is o.k + if isfield(headmodel, 'order') + order = headmodel.order; + else + order = 10; + end % initialize the forward calculation (only if coils are available) if size(sens.coilpos,1)>0 && ~isfield(headmodel, 'forwpar') @@ -294,11 +299,9 @@ headmodel.forwpar.scale = s; end - case 'openmeeg' - if isfield(headmodel,'mat') && ~isempty(headmodel.mat) - ft_warning('MEG with openmeeg only supported with NEMO lab pipeline. Please omit the mat matrix from the headmodel structure.'); - end - + case 'openmeeg' + % don't do anything, h2em or h2mm generated later in ft_prepare_leadfield + case 'simbio' ft_error('MEG not yet supported with simbio'); @@ -419,13 +422,12 @@ end sens.elecpos = pos; - case {'bem', 'dipoli', 'asa', 'bemcp', 'openmeeg'} + case {'bem', 'dipoli', 'asa', 'bemcp'} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % do postprocessing of volume and electrodes in case of BEM model %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % project the electrodes on the skin and determine the bilinear interpolation matrix - % HACK - use NEMO lab pipeline if mat field is absent for openmeeg (i.e. don't do anything) if ~isfield(headmodel, 'tra') && (isfield(headmodel, 'mat') && ~isempty(headmodel.mat)) % determine boundary corresponding with skin and inner_skull_surface if ~isfield(headmodel, 'skin_surface') @@ -466,23 +468,16 @@ % this speeds up the subsequent repeated leadfield computations fprintf('combining electrode transfer and system matrix\n'); - if strcmp(ft_voltype(headmodel), 'openmeeg') - % check that the external toolbox is present - ft_hastoolbox('openmeeg', 1); - nb_points_external_surface = size(headmodel.bnd(headmodel.skin_surface).pos,1); - headmodel.mat = headmodel.mat((end-nb_points_external_surface+1):end,:); - headmodel.mat = interp(:,1:nb_points_external_surface) * headmodel.mat; - - else - % convert to sparse matrix to speed up the subsequent multiplication - interp = sparse(interp); - headmodel.mat = interp * headmodel.mat; - % ensure that the model potential will be average referenced - avg = mean(headmodel.mat, 1); - headmodel.mat = headmodel.mat - repmat(avg, size(headmodel.mat,1), 1); - end + % convert to sparse matrix to speed up the subsequent multiplication + interp = sparse(interp); + headmodel.mat = interp * headmodel.mat; + % ensure that the model potential will be average referenced + avg = mean(headmodel.mat, 1); + headmodel.mat = headmodel.mat - repmat(avg, size(headmodel.mat,1), 1); end end + case 'openmeeg' + % don't do anything, h2em or h2mm generated later in ft_prepare_leadfield case 'fns' if isfield(headmodel,'bnd') diff --git a/external/fieldtrip/forward/ft_sensinterp_openmeeg.m b/external/fieldtrip/forward/ft_sensinterp_openmeeg.m new file mode 100644 index 00000000..2f2fcac1 --- /dev/null +++ b/external/fieldtrip/forward/ft_sensinterp_openmeeg.m @@ -0,0 +1,127 @@ +function [h2sens, ds2sens] = ft_sensinterp_openmeeg(pos, headmodel, sens) + +% FT_SENSINTERP_OPENMEEG creates a volume conduction model of the +% head using the boundary element method (BEM). This function takes +% as input the triangulated surfaces that describe the boundaries and +% returns as output a volume conduction model which can be used to +% compute leadfields. +% +% This function implements +% Gramfort et al. OpenMEEG: opensource software for quasistatic +% bioelectromagnetics. Biomedical engineering online (2010) vol. 9 (1) pp. 45 +% http://www.biomedical-engineering-online.com/content/9/1/45 +% doi:10.1186/1475-925X-9-45 +% and +% Kybic et al. Generalized head models for MEG/EEG: boundary element method +% beyond nested volumes. Phys. Med. Biol. (2006) vol. 51 pp. 1333-1346 +% doi:10.1088/0031-9155/51/5/021 +% +% This link with FieldTrip is derived from the OpenMEEG project +% with contributions from Daniel Wong and Sarang Dalal, and uses external +% command-line executables. See http://openmeeg.github.io/ +% +% Use as +% headmodel = ft_headmodel_openmeeg(mesh, ...) +% +% Optional input arguments should be specified in key-value pairs and can +% include +% conductivity = vector, conductivity of each compartment +% tissue = tissue labels for each compartment +% +% See also FT_HEADMODEL_OPENMEEG, FT_SYSMAT_OPENMEEG, FT_PREPARE_LEADFIELD + +%$Id$ + +ft_hastoolbox('openmeeg', 1); % add to path (if not yet on path) +openmeeg_license; % show the license (only once) +prefix = om_checkombin; % check the installation of the binaries +if(~ispc) % if Linux/Mac, set number of threads + omp_num_threads = feature('numCores'); + prefix = ['export OMP_NUM_THREADS=' num2str(omp_num_threads) ' && ' prefix]; +end + +ecog = ft_getopt(headmodel,'ecog','no'); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% this uses an implementation that was contributed by INRIA Odyssee Team +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% store the current path and change folder to the temporary one +cwd = pwd; +bndom = headmodel.bnd; + +workdir = fullfile(tempdir,['ft_om_' datestr(now,'ddmmyyHHMMSSFFF')]); +mkdir(workdir); + +try + % Write the triangulations to file, named after tissue type. + % OpenMEEG v2.3 and up internally adjusts the convention for surface + % normals, but OpenMEEG v2.2 expects surface normals to point inwards; + % this checks and corrects if needed + bndfile = fullfile(workdir,strcat(headmodel.tissue,'.tri')); + for ii=1:length(bndom) + om_save_tri(bndfile{ii}, bndom(ii).pos, bndom(ii).tri); + end + + condfile = fullfile(workdir,'om.cond'); + geomfile = fullfile(workdir,'om.geom'); + dipfile = fullfile(workdir,'dip.txt'); + sensorposfile = fullfile(workdir,'sensorpos.txt'); + h2sensfile = fullfile(workdir,'h2sens.bin'); + ds2sensfile = fullfile(workdir,'ds2sens.bin'); + + % write conductivity and mesh files + bndlabel = {}; + for i=1:length(bndom) + [dum,bndlabel{i}] = fileparts(bndfile{i}); + end + + om_write_geom(geomfile,bndfile,bndlabel); + om_write_cond(condfile,headmodel.cond,bndlabel); + + % handle dipole file + ndip = size(pos,1); + pos = [kron(pos,ones(3,1)),kron(ones(ndip,1),eye(3))]; % save pos with each 3D orientation + om_save_full(pos,dipfile,'ascii'); + + % infer sensor type from presence of coilpos structure + % and write sensor coordinates to disk + if(isfield(sens,'coilpos')) + om_save_full([sens.coilpos,sens.coilori],sensorposfile,'ascii'); + senstype = 'MEG'; + else + om_save_full(sens.chanpos,sensorposfile,'ascii'); + if(strcmp(ecog,'yes')) + senstype = 'iEEG'; + else + senstype = 'EEG'; + end + end + + switch senstype + case 'EEG' % scalp EEG + om_status = system([prefix 'om_assemble' ' -h2em ' geomfile ' ' condfile ' ' sensorposfile ' ' h2sensfile]); + h2sens = om_load_sparse(h2sensfile,'binary'); + ds2sens = sparse(size(h2sens,1),3*ndip); % zero for EEG; use sparse matrix to save memory + case 'iEEG' % intracranial EEG + om_status = system([prefix 'om_assemble -h2ecogm ' geomfile ' ' condfile ' ' sensorposfile ' ' h2sensfile]); + om_status = system([prefix 'om_assemble -ds2ipm ' dipfile ' ' sensorposfile ' ' ds2sensfile]); + h2sens = om_load_full(h2sensfile,'binary'); % untested: might need om_load_sparse? + ds2sens = om_load_full(ds2sensfile,'binary'); + case 'MEG' + om_status = system([prefix 'om_assemble -h2mm ' geomfile ' ' condfile ' ' sensorposfile ' ' h2sensfile]); + om_status = system([prefix 'om_assemble -ds2mm ' dipfile ' ' sensorposfile ' ' ds2sensfile]); + h2sens = om_load_full(h2sensfile,'binary'); + ds2sens = om_load_full(ds2sensfile,'binary'); + end + if(om_status ~= 0) % status = 0 if successful + ft_error(['Aborting OpenMEEG pipeline due to above error.']); + end + + rmdir(workdir,'s'); % remove workdir with intermediate files +catch + disp(lasterr); + rmdir(workdir,'s'); % remove workdir with intermediate files + ft_error('an error occurred while running OpenMEEG'); +end \ No newline at end of file diff --git a/external/fieldtrip/forward/ft_senstype.m b/external/fieldtrip/forward/ft_senstype.m index b6753338..09712abe 100644 --- a/external/fieldtrip/forward/ft_senstype.m +++ b/external/fieldtrip/forward/ft_senstype.m @@ -156,7 +156,6 @@ isnirs = isa(input, 'struct') && isfield(input, 'label') && isfield(input, 'transceiver'); islabel = isa(input, 'cell') && ~isempty(input) && isa(input{1}, 'char'); haslabel = isa(input, 'struct') && isfield(input, 'label'); -haschantype = isa(input, 'struct') && isfield(input, 'chantype'); if ~(isdata || isheader || isgrad || iselec || isnirs || islabel || haslabel) && isfield(input, 'hdr') input = input.hdr; @@ -228,6 +227,7 @@ sens = []; end +haschantype = isfield(sens, 'chantype'); if isfield(sens, 'type') % preferably the structure specifies its own type diff --git a/external/fieldtrip/forward/ft_sysmat_openmeeg.m b/external/fieldtrip/forward/ft_sysmat_openmeeg.m new file mode 100644 index 00000000..84b79604 --- /dev/null +++ b/external/fieldtrip/forward/ft_sysmat_openmeeg.m @@ -0,0 +1,107 @@ +function [dsm] = ft_sysmat_openmeeg(pos, headmodel, sens, NAflag) + +% FT_SYSMAT_OPENMEEG creates a volume conduction model of the +% head using the boundary element method (BEM). This function takes +% as input the triangulated surfaces that describe the boundaries and +% returns as output a volume conduction model which can be used to +% compute leadfields. +% +% This function implements +% Gramfort et al. OpenMEEG: opensource software for quasistatic +% bioelectromagnetics. Biomedical engineering online (2010) vol. 9 (1) pp. 45 +% http://www.biomedical-engineering-online.com/content/9/1/45 +% doi:10.1186/1475-925X-9-45 +% and +% Kybic et al. Generalized head models for MEG/EEG: boundary element method +% beyond nested volumes. Phys. Med. Biol. (2006) vol. 51 pp. 1333-1346 +% doi:10.1088/0031-9155/51/5/021 +% +% This link with FieldTrip is derived from the OpenMEEG project +% with contributions from Daniel Wong and Sarang Dalal, and uses external +% command-line executables. See http://openmeeg.github.io/ +% +% Use as +% dsm = ft_sysmat_openmeeg(pos, headmodel, sens, nonadaptive_flag) +% +% +% See also FT_HEADMODEL_OPENMEEG, FT_SENSINTERP_OPENMEEG, FT_PREPARE_LEADFIELD + +%$Id$ + +ft_hastoolbox('openmeeg', 1); % add to path (if not yet on path) +openmeeg_license; % show the license (only once) +prefix = om_checkombin; % check the installation of the binaries +if(~ispc) % if Linux/Mac, set number of threads + omp_num_threads = feature('numCores'); + prefix = ['export OMP_NUM_THREADS=' num2str(omp_num_threads) ';' prefix]; +end + +ecog = ft_getopt(headmodel,'ecog','no'); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% this uses an implementation that was contributed by INRIA Odyssee Team +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% store the current path and change folder to the temporary one +cwd = pwd; +bndom = headmodel.bnd; + +workdir = fullfile(tempdir,['ft_om_' datestr(now,'ddmmyyHHMMSSFFF')]); +mkdir(workdir); + +try + % Write the triangulations to file, named after tissue type. + % OpenMEEG v2.3 and up internally adjusts the convention for surface + % normals, but OpenMEEG v2.2 expects surface normals to point inwards; + % this checks and corrects if needed + bndfile = fullfile(workdir,strcat(headmodel.tissue,'.tri')); + for ii=1:length(bndom) + om_save_tri(bndfile{ii}, bndom(ii).pos, bndom(ii).tri); + end + + condfile = fullfile(workdir,'om.cond'); + geomfile = fullfile(workdir,'om.geom'); + dipfile = fullfile(workdir,'dip.txt'); + dsmfile = fullfile(workdir,'dsm.bin'); % extension added directly in om_assemble function call below + + % write conductivity and mesh files + bndlabel = {}; + for i=1:length(bndom) + [dum,bndlabel{i}] = fileparts(bndfile{i}); + end + + om_write_geom(geomfile,bndfile,bndlabel); + om_write_cond(condfile,headmodel.cond,bndlabel); + + % handle dipole file + ndip = size(pos,1); + pos = [kron(pos,ones(3,1)),kron(ones(ndip,1),eye(3))]; % save pos with each 3D orientation + om_save_full(pos,dipfile,'ascii'); + + omp_num_threads = feature('numCores'); + + if(strcmp(NAflag,'yes')) + str = ' -DSMNA'; + else + str = ' -DSM'; + end + + om_status = system([prefix 'om_assemble' str ' ' geomfile ' ' condfile ' ' dipfile ' ' dsmfile]); + + if(om_status ~= 0) % status = 0 if successful + ft_error(['Aborting OpenMEEG pipeline due to above error.']); + end + + dsm = om_load_full(dsmfile,'binary'); +catch + rethrow(lasterror) +end + +try + rmdir(workdir,'s'); % remove workdir with intermediate files +catch + disp(lasterr); + rmdir(workdir,'s'); % remove workdir with intermediate files + ft_error('an error occurred while running OpenMEEG'); +end diff --git a/external/fieldtrip/forward/private/channelposition.m b/external/fieldtrip/forward/private/channelposition.m index 1eecb56a..2952c3f2 100644 --- a/external/fieldtrip/forward/private/channelposition.m +++ b/external/fieldtrip/forward/private/channelposition.m @@ -5,11 +5,11 @@ % % Use as % [pos, ori, lab] = channelposition(sens) -% where sens is an electrode or gradiometer array. +% where sens is an electrode, gradiometer or optode array. % % See also FT_DATATYPE_SENS -% Copyright (C) 2009-2012, Robert Oostenveld & Vladimir Litvak +% Copyright (C) 2009-2018, Robert Oostenveld & Vladimir Litvak % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -49,6 +49,8 @@ sens.coilori = nan(size(sens.coilpos)); elseif ~isfield(sens, 'coilori') && isfield(sens, 'elecpos') sens.coilori = nan(size(sens.elecpos)); +elseif ~isfield(sens, 'coilori') && isfield(sens, 'optopos') + sens.coilori = nan(size(sens.optopos)); end switch ft_senstype(sens) @@ -261,6 +263,8 @@ nelec = size(sens.elecpos,1); % these are the electrodes elseif isfield(sens, 'coilpos') ncoil = size(sens.coilpos,1); % these are the coils + elseif isfield(sens, 'optopos') + nopto = size(sens.optopos,1); % these are the optodes end if ~isfield(sens, 'tra') && isfield(sens, 'elecpos') && nchan==nelec @@ -286,11 +290,18 @@ pnt = sens.coilpos; ori = sens.coilori; lab = sens.label; + + elseif ~isfield(sens, 'tra') && isfield(sens, 'optopos') && nchan==nopto + % there is one optode per channel, which means that the channel position is identical to the coil position + pnt = sens.optopos; + ori = nan(size(pnt)); + lab = sens.label; elseif isfield(sens, 'tra') % each channel depends on multiple sensors (electrodes or coils), compute a weighted position for the channel % for MEG gradiometer channels this means that the position is in between the two coils % for bipolar EEG channels this means that the position is in between the two electrodes + % for NIRS channels this means that the position is in between the transmit and receive optode pnt = nan(nchan,3); ori = nan(nchan,3); if isfield(sens, 'coilpos') @@ -306,14 +317,18 @@ weight = weight ./ sum(weight); pnt(i,:) = weight * sens.elecpos; end + elseif isfield(sens, 'optopos') + for i=1:nchan + weight = abs(sens.tra(i,:)); + weight = weight ./ sum(weight); + pnt(i,:) = weight * sens.optopos; + end end lab = sens.label; - end - end % switch senstype -n = size(lab,2); +n = size(lab,2); % this is to fix the planar layouts, which cannot be plotted anyway if n>1 && size(lab, 1)>1 % this is to prevent confusion when lab happens to be a row array pnt = repmat(pnt, n, 1); diff --git a/external/fieldtrip/forward/private/defaultId.m b/external/fieldtrip/forward/private/defaultId.m index f4e1ee99..e7569675 100644 --- a/external/fieldtrip/forward/private/defaultId.m +++ b/external/fieldtrip/forward/private/defaultId.m @@ -40,7 +40,9 @@ % remove the non-FieldTrip functions from the path, these should not be part of the default message identifier keep = true(size(stack)); -[v, p] = ft_version; +p = fileparts(mfilename('fullpath')); +% strip away '/utilities/private' where this function is located +p = p(1:end-18); for i=1:numel(stack) keep(i) = strncmp(p, stack(i).file, length(p)); end diff --git a/external/fieldtrip/forward/private/eeg_leadfield4_prepare.m b/external/fieldtrip/forward/private/eeg_leadfield4_prepare.m index d8255d32..66019c66 100644 --- a/external/fieldtrip/forward/private/eeg_leadfield4_prepare.m +++ b/external/fieldtrip/forward/private/eeg_leadfield4_prepare.m @@ -1,4 +1,4 @@ -function [lut_t, cuf_t] = eeg_leadfield4_prepare(vol, Nmax) +function [lut_t, cuf_t] = eeg_leadfield4_prepare(vol, order) % EEG_LEADFIELD4_PREPARE computes constant factors for series expansion % for the 4 concentric sphere electric leadfield computation. Calling @@ -6,7 +6,7 @@ % factors "t" do not have to be computed each time in eeg_leadfield4. % % Use as -% vol.t = eeg_leadfield4_prepare(vol, N); +% vol.t = eeg_leadfield4_prepare(vol, order); % where % vol.r radius of the 4 spheres % vol.cond conductivity of the 4 spheres @@ -50,8 +50,8 @@ r3 = vol.r(3); c3 = vol.cond(3); r4 = vol.r(4); c4 = vol.cond(4); -if nargin==1 - Nmax = 60; +if nargin==1 || isempty(order) + order = 60; end % these are the constants of cuffin1979 @@ -59,7 +59,7 @@ k2 = c2/c3; k3 = c3/c4; -for n=1:Nmax +for n=1:order % according to lutkenhoner1992 the constant C is % lut_t(n) = ((n*c1/c2+n+1)*(n*c2/c3+n+1)+n*(n+1)*(c1/c2-1)*(c2/c3-1)*(r1/r2)^(2*n+1)) * ... % ((n*c3/c4+n+1)+(n+1)*(c3/c4-1)*(r3/r4)^(2*n+1)) + ... @@ -82,7 +82,7 @@ % according to cuffin1979 the constant Tau is (re-entered on 25 sept 2002) % but this requires also slightly other constants in the eeg_leadfield4 function - for n=1:Nmax + for n=1:order cuf_t(n) = d^(2*n+1) * (b^(2*n+1)*n*(k1-1)*(k2-1)*(n+1)... + c^(2*n+1)*(k1*n+n+1)*(k2*n+n+1))... *((k3*n+n+1)+(n+1)*(k3-1)*d^(2*n+1))... diff --git a/external/fieldtrip/forward/private/ft_datatype_sens.m b/external/fieldtrip/forward/private/ft_datatype_sens.m index a3c1d2ec..b0c304be 100644 --- a/external/fieldtrip/forward/private/ft_datatype_sens.m +++ b/external/fieldtrip/forward/private/ft_datatype_sens.m @@ -161,7 +161,8 @@ sens = fixoldorg(sens, false); % ensure that all numbers are represented in double precision - sens = ft_struct2double(sens); + % this only affects the top-level fields and does not recurse + sens = ft_struct2double(sens, 1); % in version 2011v2 this was optional, now it is required if ~isfield(sens, 'chantype') || all(strcmp(sens.chantype, 'unknown')) diff --git a/external/fieldtrip/forward/private/ft_getopt.m b/external/fieldtrip/forward/private/ft_getopt.m index 0d433115..5f2f387b 100644 --- a/external/fieldtrip/forward/private/ft_getopt.m +++ b/external/fieldtrip/forward/private/ft_getopt.m @@ -9,7 +9,7 @@ % s = structure or cell-array % key = string % default = any valid MATLAB data type (optional, default = []) -% emptymeaningful = boolean value (optional, default = 0) +% emptymeaningful = boolean value (optional, default = false) % % If the key is present as field in the structure, or as key-value pair in the % cell-array, the corresponding value will be returned. @@ -49,7 +49,7 @@ end if nargin < 4 - emptymeaningful = 0; + emptymeaningful = false; end if isa(opt, 'struct') || isa(opt, 'config') diff --git a/external/fieldtrip/forward/private/ft_getopt.mexw32 b/external/fieldtrip/forward/private/ft_getopt.mexw32 index 59a9ff8e..fd5aed41 100755 Binary files a/external/fieldtrip/forward/private/ft_getopt.mexw32 and b/external/fieldtrip/forward/private/ft_getopt.mexw32 differ diff --git a/external/fieldtrip/forward/private/ft_getopt.mexw64 b/external/fieldtrip/forward/private/ft_getopt.mexw64 index 343089e8..741ebeda 100755 Binary files a/external/fieldtrip/forward/private/ft_getopt.mexw64 and b/external/fieldtrip/forward/private/ft_getopt.mexw64 differ diff --git a/external/fieldtrip/forward/private/ft_hastoolbox.m b/external/fieldtrip/forward/private/ft_hastoolbox.m index 055d238f..cc8883dc 100644 --- a/external/fieldtrip/forward/private/ft_hastoolbox.m +++ b/external/fieldtrip/forward/private/ft_hastoolbox.m @@ -52,7 +52,7 @@ if isdeployed % it is not possible to check the presence of functions or change the path in a compiled application - status = 1; + status = true; return end @@ -78,7 +78,8 @@ 'MATLAB2BESA' 'see http://www.besa.de/downloads/matlab/ and get the "MATLAB to BESA Export functions"' 'EEPROBE' 'see http://www.ant-neuro.com, or contact Maarten van der Velde' 'YOKOGAWA' 'this is deprecated, please use YOKOGAWA_MEG_READER instead' - 'YOKOGAWA_MEG_READER' 'see http://www.yokogawa.com/me/me-login-en.htm' + 'YOKOGAWA_MEG_READER' 'contact Ricoh engineers' + 'RICOH_MEG_READER' 'contact Ricoh engineers' 'BEOWULF' 'see http://robertoostenveld.nl, or contact Robert Oostenveld' 'MENTAT' 'see http://robertoostenveld.nl, or contact Robert Oostenveld' 'SON2' 'see http://www.kcl.ac.uk/depsta/biomedical/cfnr/lidierth.html, or contact Malcolm Lidierth' @@ -131,7 +132,7 @@ 'MYSQL' 'see http://www.mathworks.com/matlabcentral/fileexchange/8663-mysql-database-connector' 'ISO2MESH' 'see http://iso2mesh.sourceforge.net/cgi-bin/index.cgi?Home or contact Qianqian Fang' 'DATAHASH' 'see http://www.mathworks.com/matlabcentral/fileexchange/31272' - 'IBTB' 'see http://www.ibtb.org' + 'IBTB' 'see https://github.com/selimonat/InformationBreakdownToolbox' 'ICASSO' 'see http://www.cis.hut.fi/projects/ica/icasso' 'XUNIT' 'see http://www.mathworks.com/matlabcentral/fileexchange/22846-matlab-xunit-test-framework' 'PLEXON' 'available from http://www.plexon.com/assets/downloads/sdk/ReadingPLXandDDTfilesinMatlab-mexw.zip' @@ -143,8 +144,8 @@ 'BRAINSUITE' 'see http://brainsuite.bmap.ucla.edu/processing/additional-tools/' 'BRAINVISA' 'see http://brainvisa.info' 'FILEEXCHANGE' 'see http://www.mathworks.com/matlabcentral/fileexchange/' - 'NEURALYNX_V6' 'see http://neuralynx.com/research_software/file_converters_and_utilities/ and take the version from Neuralynx (windows only)' - 'NEURALYNX_V3' 'see http://neuralynx.com/research_software/file_converters_and_utilities/ and take the version from Ueli Rutishauser' + 'NEURALYNX_V6' 'see https://neuralynx.com/software/category/matlab-netcom-utilities/ and take the version from Neuralynx' + 'NEURALYNX_V3' 'see http://www.urut.ch/new/serendipity/index.php?/pages/nlxtomatlab.html' 'NPMK' 'see https://github.com/BlackrockMicrosystems/NPMK' 'VIDEOMEG' 'see https://github.com/andreyzhd/VideoMEG' 'WAVEFRONT' 'see http://mathworks.com/matlabcentral/fileexchange/27982-wavefront-obj-toolbox' @@ -152,6 +153,9 @@ 'BREWERMAP' 'see https://nl.mathworks.com/matlabcentral/fileexchange/45208-colorbrewer--attractive-and-distinctive-colormaps' 'CELLFUNCTION' 'see https://github.com/schoffelen/cellfunction' 'MARS' 'see http://www.parralab.org/mars' + 'JSONLAB' 'see https://se.mathworks.com/matlabcentral/fileexchange/33381-jsonlab--a-toolbox-to-encode-decode-json-files' + 'MFFMATLABIO' 'see https://github.com/arnodelorme/mffmatlabio' + 'JSONIO' 'see https://github.com/gllmflndn/JSONio' }; if nargin<2 @@ -238,7 +242,9 @@ case 'YOKOGAWA16BITBETA6' dependency = @()hasyokogawa('16bitBeta6'); case 'YOKOGAWA_MEG_READER' - dependency = @()hasyokogawa('1.4'); + dependency = @()hasyokogawa('1.5'); + case 'RICOH_MEG_READER' + dependency = @()hasricoh('1.0'); case 'BEOWULF' dependency = {'evalwulf', 'evalwulf', 'evalwulf'}; case 'MENTAT' @@ -309,6 +315,8 @@ dependency = {'macaque71.mat', 'motif4funct_wei'}; case 'CCA' dependency = {'ccabss'}; + case 'MFFMATLABIO' + dependency = {'eegplugin_mffmatlabio', 'mff_getobj'}; case 'EGI_MFF' dependency = {'mff_getObject', 'mff_getSummaryInfo'}; case 'TOOLBOX_GRAPH' @@ -327,7 +335,7 @@ case 'DATAHASH' dependency = {'DataHash'}; case 'IBTB' - dependency = {'make_ibtb','binr'}; + dependency = {'binr','information'}; case 'ICASSO' dependency = {'icassoEst'}; case 'XUNIT' @@ -371,6 +379,12 @@ dependency = {'ghdf5read' 'ghdf5fileimport'}; case 'MARS' dependency = {'spm_mars_mrf'}; + case 'JSONLAB' + dependency = {'loadjson' 'savejson'}; + case 'PLOTLY' + dependency = {'fig2plotly' 'savejson'}; + case 'JSONIO' + dependency = {'jsonread', 'jsonwrite', 'jsonread.mexa64'}; % the following are FieldTrip modules/toolboxes case 'FILEIO' @@ -442,19 +456,19 @@ % for linux computers in the Donders Centre for Cognitive Neuroimaging prefix = '/home/common/matlab'; - if ~status && isdir(prefix) + if ~status && isfolder(prefix) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end % for windows computers in the Donders Centre for Cognitive Neuroimaging prefix = 'h:\common\matlab'; - if ~status && isdir(prefix) + if ~status && isfolder(prefix) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end % use the MATLAB subdirectory in your homedirectory, this works on linux and mac prefix = fullfile(getenv('HOME'), 'matlab'); - if ~status && isdir(prefix) + if ~status && isfolder(prefix) status = myaddpath(fullfile(prefix, lower(toolbox)), silent); end @@ -490,9 +504,10 @@ % helper function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function status = myaddpath(toolbox, silent) +global ft_default if isdeployed ft_warning('cannot change path settings for %s in a compiled application', toolbox); - status = 1; + status = true; elseif exist(toolbox, 'dir') if ~silent ft_warning('off','backtrace'); @@ -500,16 +515,24 @@ ft_warning('on','backtrace'); end if any(~cellfun(@isempty, regexp(toolbox, {'spm2', 'spm5', 'spm8', 'spm12'}))) - % SPM needs to be added with the subdirectories + % SPM needs to be added with all its subdirectories addpath(genpath(toolbox)); + % check whether the mex files are compatible + check_spm_mex; else addpath(toolbox); end - status = 1; + % remember the toolbox that was just added to the path, it will be cleaned up by FT_POSTAMBLE_HASTOOLBOX + if ~isfield(ft_default, 'toolbox') || ~isfield(ft_default.toolbox, 'cleanup') + ft_default.toolbox.cleanup = {}; + end + ft_default.toolbox.cleanup{end+1} = toolbox; + status = true; elseif (~isempty(regexp(toolbox, 'spm2$', 'once')) || ~isempty(regexp(toolbox, 'spm5$', 'once')) || ~isempty(regexp(toolbox, 'spm8$', 'once')) || ~isempty(regexp(toolbox, 'spm12$', 'once'))) && exist([toolbox 'b'], 'dir') + % the final release version of SPM is not available, add the beta version instead status = myaddpath([toolbox 'b'], silent); else - status = 0; + status = false; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -582,10 +605,31 @@ token = regexp(version_str,'(\d*)','tokens'); v = str2num([token{:}{:}]); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function status = check_spm_mex() +status = true; +try + % this will always result in an error + spm_conv_vol +catch + me = lasterror; + % any error is ok, except when due to an invalid MEX file + status = ~isequal(me.identifier, 'MATLAB:mex:ErrInvalidMEXFile'); +end +if ~status + % SPM8 mex file issues are common on macOS with recent MATLAB versions + ft_warning('the SPM mex files are incompatible with your platform, see http://bit.ly/2OGF6US'); +end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % helper function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function status = has_license(toolbox_name) +% NOTE: this explicitly checks out a license, which may be suboptimal in +% terms of license use. Consider using the option 'test', but this needs to +% be checked with respect to backward compatibility first status = license('checkout', toolbox_name)==1; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -607,7 +651,6 @@ assert(false,'this should not happen'); end - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % helper function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -616,3 +659,10 @@ % must be in path and not a variable status = ~isempty(w) && ~isequal(w, 'variable'); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% ISFOLDER is needed for versions prior to 2017b +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function tf = isfolder(dirpath) +tf = exist(dirpath,'dir') == 7; + diff --git a/external/fieldtrip/forward/private/ft_notification.m b/external/fieldtrip/forward/private/ft_notification.m index dc9b7369..a74c4052 100644 --- a/external/fieldtrip/forward/private/ft_notification.m +++ b/external/fieldtrip/forward/private/ft_notification.m @@ -337,7 +337,7 @@ ft_default.notification.(level) = s; % the remainder is fully handled by the ERROR function if ~isempty(msgId) - error(msgId, varargin{:}); + error(state); else error(varargin{:}); end diff --git a/external/fieldtrip/forward/private/ft_platform_supports.m b/external/fieldtrip/forward/private/ft_platform_supports.m index c0676b62..34639722 100644 --- a/external/fieldtrip/forward/private/ft_platform_supports.m +++ b/external/fieldtrip/forward/private/ft_platform_supports.m @@ -41,6 +41,28 @@ % % See also FT_VERSION, VERSION, VER, VERLESSTHAN +% Copyright (C) 2006, Robert Oostenveld +% Copyright (C) 2010, Eelke Spaak +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + if ~ischar(what) error('first argument must be a string'); end @@ -48,6 +70,9 @@ switch what case 'matlabversion' tf = is_matlab() && matlabversion(varargin{:}); + + case 'octaveversion' + tf = is_octave() && octaveversion(varargin{:}); case 'exists-in-private-directory' tf = is_matlab(); @@ -206,11 +231,31 @@ end % function + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function [inInterval] = matlabversion(min, max) +function [inInterval] = octaveversion(min, max) +% the version does not change, making it persistent speeds up the subsequent calls +persistent curVer + +if nargin<2 + max = min; +end +if isempty(curVer) + curVer = OCTAVE_VERSION; +end + +% perform comparison with respect to version number +[major, minor] = parseMatlabVersion(curVer); +[minMajor, minMinor] = parseMatlabVersion(min); +[maxMajor, maxMinor] = parseMatlabVersion(max); + +inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); + +end % function + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MATLABVERSION checks if the current MATLAB version is within the interval % specified by min and max. % @@ -229,27 +274,8 @@ % etc. % % See also VERSION, VER, VERLESSTHAN - -% Copyright (C) 2006, Robert Oostenveld -% Copyright (C) 2010, Eelke Spaak -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [inInterval] = matlabversion(min, max) % the version does not change, making it persistent speeds up the subsequent calls persistent curVer @@ -311,8 +337,8 @@ minor = int8((ver - floor(ver)) * 10); else % ver is string (e.g. '7.10'), parse accordingly [major, rest] = strtok(ver, '.'); - major = str2num(major); - minor = str2num(strtok(rest, '.')); + major = str2double(major); + minor = str2double(strtok(rest, '.')); end end % function diff --git a/external/fieldtrip/forward/private/hasyokogawa.m b/external/fieldtrip/forward/private/hasyokogawa.m index cf309aab..e013ca78 100644 --- a/external/fieldtrip/forward/private/hasyokogawa.m +++ b/external/fieldtrip/forward/private/hasyokogawa.m @@ -7,7 +7,7 @@ % Use as % string = hasyokogawa; % which returns a string describing the toolbox version, e.g. "12bitBeta3", -% "16bitBeta3", or "16bitBeta6" for preliminary versions, or '1.4' for the +% "16bitBeta3", or "16bitBeta6" for preliminary versions, or '1.5' for the % official Yokogawa MEG Reader Toolbox. An empty string is returned if the toolbox % is not installed. The string "unknown" is returned if it is installed but % the version is unknown. @@ -43,9 +43,9 @@ % there are a few versions of the old preliminary implementation, such as % 12bitBeta3, 16bitBeta3 and 16bitBeta6. In 2011 a completely new -% implementation was officially released, which contains functions with -% other names. At the time of writing this, the new implementation is -% version 1.4. +% implementation was officially released, which contains functions with other names. +% At the time of writing this [2018.06.08], +% the new implementation, Yokogawa MEG Reader, is version 1.5, in which EEG data are supported. if exist('getYkgwVersion', 'file') res = getYkgwVersion(); diff --git a/external/fieldtrip/forward/private/icosahedron.m b/external/fieldtrip/forward/private/icosahedron.m index b6d65c34..663cb7d5 100644 --- a/external/fieldtrip/forward/private/icosahedron.m +++ b/external/fieldtrip/forward/private/icosahedron.m @@ -1,11 +1,11 @@ -function [pos, tri] = icosahedron() +function [pos, tri] = icosahedron(varargin) -% ICOSAHEDRON creates an icosahedron +% ICOSAHEDRON creates an icosahedron with 12 vertices and 20 triangles % -% [pos, tri] = icosahedron -% creates an icosahedron with 12 vertices and 20 triangles -% -% See also OCTAHEDRON, ICOSAHEDRON42, ICOSAHEDRON162, ICOSAHEDRON642, ICOSAHEDRON2562 +% Use as +% [pos, tri] = icosahedron +% +% See also TETRAHEDRON, OCTAHEDRON, ICOSAHEDRON42, ICOSAHEDRON162, ICOSAHEDRON642, ICOSAHEDRON2562 % Copyright (C) 2002, Robert Oostenveld % @@ -27,27 +27,27 @@ % tri = [ - 1 2 3 - 1 3 4 - 1 4 5 - 1 5 6 - 1 6 2 - 2 8 3 - 3 9 4 - 4 10 5 - 5 11 6 - 6 7 2 - 7 8 2 - 8 9 3 - 9 10 4 - 10 11 5 - 11 7 6 + 1 2 3 + 1 3 4 + 1 4 5 + 1 5 6 + 1 6 2 + 2 8 3 + 3 9 4 + 4 10 5 + 5 11 6 + 6 7 2 + 7 8 2 + 8 9 3 + 9 10 4 + 10 11 5 + 11 7 6 12 8 7 12 9 8 12 10 9 12 11 10 12 7 11 -]; + ]; pos = zeros(12, 3); @@ -66,3 +66,12 @@ pos(12, :) = [0 0 -1]; % bottom point +if nargin>0 + n = varargin{1}; + % perform an n-fold refinement + for i=1:n + [pos, tri] = refine(pos, tri); + % scale all vertices to the unit sphere + pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); + end +end diff --git a/external/fieldtrip/forward/private/icosahedron162.m b/external/fieldtrip/forward/private/icosahedron162.m index ae3bfe69..457969da 100644 --- a/external/fieldtrip/forward/private/icosahedron162.m +++ b/external/fieldtrip/forward/private/icosahedron162.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron162() -% ICOSAHEDRON162 creates a 2-fold refined icosahedron +% ICOSAHEDRON162 creates a 2-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron162; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,9 +26,6 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); - -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +[pos, tri] = icosahedron(2); diff --git a/external/fieldtrip/forward/private/icosahedron2562.m b/external/fieldtrip/forward/private/icosahedron2562.m index 11752a0c..c9164f12 100644 --- a/external/fieldtrip/forward/private/icosahedron2562.m +++ b/external/fieldtrip/forward/private/icosahedron2562.m @@ -1,6 +1,12 @@ -function [pnt, tri] = icosahedron2562() +function [pos, tri] = icosahedron2562() -% ICOSAHEDRON2562 creates a 4-fold refined icosahedron +% ICOSAHEDRON2562 creates a 4-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron2562; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -22,10 +28,4 @@ % % $Id$ -[pnt, tri] = icosahedron; -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); - -pnt = pnt ./ repmat(sqrt(sum(pnt.^2,2)), 1,3); +[pos, tri] = icosahedron(4); diff --git a/external/fieldtrip/forward/private/icosahedron42.m b/external/fieldtrip/forward/private/icosahedron42.m index 833c11eb..3f8ccae0 100644 --- a/external/fieldtrip/forward/private/icosahedron42.m +++ b/external/fieldtrip/forward/private/icosahedron42.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron42() -% ICOSAHEDRON42 creates a 1-fold refined icosahedron +% ICOSAHEDRON42 creates a 1-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron42; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,8 +26,6 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); - -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +[pos, tri] = icosahedron(1); diff --git a/external/fieldtrip/forward/private/icosahedron642.m b/external/fieldtrip/forward/private/icosahedron642.m index 4d932e5c..76e0d149 100644 --- a/external/fieldtrip/forward/private/icosahedron642.m +++ b/external/fieldtrip/forward/private/icosahedron642.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron642() -% ICOSAHEDRON642 creates a 3-fold refined icosahedron +% ICOSAHEDRON642 creates a 3-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron642; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,10 +26,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); +[pos, tri] = icosahedron(3); -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); diff --git a/external/fieldtrip/forward/private/leadfield_openmeeg.m b/external/fieldtrip/forward/private/leadfield_openmeeg.m deleted file mode 100644 index e47f7b66..00000000 --- a/external/fieldtrip/forward/private/leadfield_openmeeg.m +++ /dev/null @@ -1,420 +0,0 @@ -function [lp, voxels_in] = leadfield_openmeeg ( voxels, vol, sens, varargin ) - -% FT_OM_COMPUTE_LEAD uses OpenMEEG to compute the lead fields / potentials -% using the boundary element method (BEM). -% The inputs are as follows: -% voxels = an [Nx3] array of voxel locations. -% vol = the volume structure containing bnd and cond fields. In -% order to save the matrices computed by OpenMEEG, the -% fields 'path' and 'basefile' should also be provided. -% Matrices will be stored under the directory specified by -% 'path' and 'basefile' will be used to generate the -% filename. If FT_OM_COMPUTE_LEAD is run with the same -% 'path' and 'basefile' parameters and detects the -% corresponding files from the OpenMEEG process, it will -% use those files for further processing rather than -% creating/calculating them again. -% sens = the sens structure (elec for EEG or grad for MEG) -% -% Additional parameters can be specified by setting the following vol -% fields: -% vol.ecog = "yes"/["no"] allows the computation of lead potentials -% for ECoG grids. (sens must be an EEG elec structure) -% vol.method = ["hminv"]/"adjoint" to use the adjoint method instead of -% the inverse head matrix. -% -% By default, the BEM will be computed using the inverse head matrix -% method. This is slower than the adjoint method, but more efficient if the -% BEM needs to be computed multiple times when sensor positions have -% moved relative to fixed head coordinates. To implement this type of -% computation: -% 1) vol.path and vol.basefile should be specified so that OpenMEEG -% matrices will be saved. -% 2) Once the first BEM is computed, copy the following files to a second -% working directory: -% - *_hm.bin -% - *_hminv.bin -% - *_dsm#.bin (where # is a number) -% 3) vol.path should be changed to the second working directory and -% vol.basefile should remain the same. -% 4) Recompute the BEM with the new set of sensor positions and/or voxels. -% -% Copyright (C) 2013, Daniel D.E. Wong, Sarang S. Dalal -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% - -vol = fixpos(vol); % renames old subfield 'pnt' to 'pos', if necessary - -% Variable declarations -CPU_LIM = feature('numCores'); -VOXCHUNKSIZE = 30000; % if OpenMEEG uses too much memory for a given computer, try reducing VOXCHUNKSIZE -om_format = 'binary'; % note that OpenMEEG's mat-file supported is limited in file size (2GB?) -switch(om_format) - case 'matlab' - om_ext = '.mat'; - case 'binary' - om_ext = '.bin'; - otherwise - ft_error('invalid OpenMEEG output type requested'); -end - -OPENMEEG_PATH = []; % '/usr/local/bin/'; % In case OpenMEEG executables omitted from PATH variable - -persistent ldLibraryPath0; - -if ispc - ft_warning('Sorry, Windows is not yet tested'); -elseif isunix - setenv('OMP_NUM_THREADS',num2str(CPU_LIM)); - if(~ismac) % MacOS doesn't use LD_LIBRARY_PATH; in case of problems, look into "DYLD_LIBRARY_PATH" - if isempty(ldLibraryPath0) - ldLibraryPath0 = getenv('LD_LIBRARY_PATH'); % We'll restore this at the end - end - UNIX_LDLIBRARYPATH = '/usr/lib:/usr/local/lib'; - setenv('LD_LIBRARY_PATH',UNIX_LDLIBRARYPATH); % MATLAB changes the default LD_LIBRARY_PATH variable - end -end - -[om_status,om_errmsg] = system(fullfile(OPENMEEG_PATH,'om_assemble')); % returns 0 if om_assemble is not happy -if(om_status ~= 0) - ft_error([om_errmsg 'Unable to properly execute OpenMEEG. Please configure variable declarations and paths in this file as needed.']); -else - clear om_status -end - -% Extra options -method = ft_getopt(vol,'method','hminv'); -ecog = ft_getopt(vol,'ecog','no'); - -% Use basefile and basepath for saving files -if isfield(vol,'basefile') - basefile = vol.basefile; -else - basefile = tempname; -end -if isfield(vol,'path') - path = vol.path; - cleanup_flag = false; -else - path = fullfile(tempdir,'ft-om'); - cleanup_flag = true; -end -mkdir(path); - -sensorFile = fullfile(path, [basefile '_sensorcoords.txt']); -if exist(sensorFile,'file') - disp('Sensor coordinate file already exists. Skipping...') -else - disp('Writing sensor coordinates...') - fid = fopen(sensorFile,'w'); - if ft_senstype(sens, 'eeg') - for ii=1:size(sens.chanpos,1) - fprintf(fid,'%s\t%.15f\t%.15f\t%.15f\n', sens.label{ii}, sens.chanpos(ii,:)); - end - else % MEG - % Find channel labels for each coil -- non-trivial for MEG gradiometers! - % Note that each coil in a gradiometer pair will receive the same label - [chanlabel_idx,coilpos_idx]=find(abs(sens.tra)==1); - - newchanlabelmethod = true - if(newchanlabelmethod) - for ii=1:size(sens.coilpos,1) - fprintf(fid,'%.15f\t%.15f\t%.15f\t%.15f\t%.15f\t%.15f\n',sens.coilpos(ii,:),sens.coilori(ii,:)); - end - else - if(size(sens.tra,1) < max(chanlabel_idx) || size(sens.tra,2) ~= length(coilpos_idx) || length(coilpos_idx) ~= size(sens.coilpos,1)) - % These dimensions should match; if not, some channels may have been - % removed, or there's unexpected handling of MEG reference coils - ft_error('Mismatch between number of rows in sens.tra and number of channels... possibly some channels removed or unexpected MEG reference coil configuration'); - end - - for ii=1:length(coilpos_idx) - coilpair_idx = find(chanlabel_idx(ii) == chanlabel_idx); - - if(length(coilpair_idx)==2) - whichcoil = find(ii == coilpair_idx); - - switch(whichcoil) - case 1 - labelsuffix = 'A'; - case 2 - labelsuffix = 'B'; - end - else - labelsuffix = ''; - end - label = [sens.label{chanlabel_idx(ii)} labelsuffix]; - fprintf(fid,'%s\t%.15f\t%.15f\t%.15f\t%.15f\t%.15f\t%.15f\n',label,sens.coilpos(ii,:),sens.coilori(ii,:)); - end - end - end - fclose(fid); -end - -condFile = fullfile(path, [basefile '.cond']); -if exist(condFile,'file') - disp('Conductivity file already exists. Skipping...') -else - disp('Writing conductivity file...') - write_cond(vol,condFile); -end - -geomFile = fullfile(path, [basefile '.geom']); -if exist(geomFile,'file') - disp('Geometry descriptor file already exists. Skipping...') -else - disp('Writing geometry descriptor file...') - write_geom(vol,geomFile,basefile); -end - -disp('Writing OpenMEEG mesh files...') - -write_mesh(vol,path,basefile); - -disp('Validating mesh...') -[om_status om_msg] = system([fullfile(OPENMEEG_PATH, 'om_check_geom'), ' -g ', geomFile]) -if(om_status ~= 0) % status = 0 if successful - ft_error([om_msg, 'Aborting OpenMEEG pipeline due to above error.']); -end - -disp('Writing dipole file...') -chunks = ceil(size(voxels,1)/VOXCHUNKSIZE); -dipFile = cell(chunks,1); -for ii = 1:chunks - dipFile{ii} = fullfile(path, [basefile '_voxels' num2str(ii) om_ext]); - if exist(dipFile{ii},'file') - fprintf('\t%s already exists. Skipping...\n', dipFile{ii}); - else - voxidx = ((ii-1)*VOXCHUNKSIZE + 1) : (min((ii)*VOXCHUNKSIZE,size(voxels,1))); - writevoxels = [kron(voxels(voxidx,:),ones(3,1)) , kron(ones(length(voxidx),1),eye(3))]; - om_save_full(writevoxels,dipFile{ii},om_format); - end -end - - -hmFile = fullfile(path, [basefile '_hm' om_ext]); -if exist(hmFile,'file') - disp('Head matrix already exists. Skipping...') -else - disp('Building head matrix') - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_assemble'), ' -hm ', geomFile, ' ', condFile, ' ', hmFile]) - if(om_status ~= 0) % status = 0 if successful - ft_error([om_msg, 'Aborting OpenMEEG pipeline due to above error.']); - end -end - -if strcmp(method,'hminv') - hminvFile = fullfile(path, [basefile '_hminv' om_ext]); - if exist(hminvFile,'file') - disp('Inverse head matrix already exists. Skipping...'); - else - disp('Computing inverse head matrix'); - if(CPU_LIM >= 4) % Matlab's inverse function is multithreaded and performs faster with at least 4 cores - om_save_sym(inv(om_load_sym(hmFile,om_format)),hminvFile,om_format); - else - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_minverser'), ' ', hmFile, ' ', hminvFile]) - if(om_status ~= 0) % status = 0 if successful - ft_error([om_msg, 'Aborting OpenMEEG pipeline due to above error.']); - end - end - end -end - -dsmFile = cell(chunks,1); -for ii = 1:chunks - dsmFile{ii} = fullfile(path, [basefile '_dsm' num2str(ii) om_ext]); - if exist(dsmFile{ii},'file') - fprintf('\t%s already exists. Skipping...\n', dsmFile{ii}); - else - disp('Assembling source matrix'); - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_assemble'), ' -dsm ', geomFile, ' ', condFile, ' ', dipFile{ii}, ' ' dsmFile{ii}]) - if(om_status ~= 0) % status = 0 if successful - ft_error([om_msg, 'Aborting OpenMEEG pipeline due to above error. If 4-layer BEM attempted, try 3-layer BEM (scalp, skull, brain).']); - end - end -end - - -disp('--------------------------------------') - - -if ft_senstype(sens, 'eeg') - if strcmp(ecog,'yes') - ohmicFile = fullfile(path, [basefile '_h2ecogm']); - cmd = '-h2ecogm'; - else - ohmicFile = fullfile(path, [basefile '_h2em' om_ext]); - cmd = '-h2em'; - end -else - ohmicFile = fullfile(path, [basefile '_h2mm' om_ext]); - cmd = '-h2mm'; -end -if exist(ohmicFile,'file') - disp('Ohmic current file already exists. Skipping...') -else - disp('Calculating Contribution of Ohmic Currents') - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_assemble'), ' ', cmd, ' ', geomFile, ' ', condFile, ' ' , sensorFile, ' ' , ohmicFile]) - if(om_status ~= 0) % status = 0 if successful - ft_error([om_msg, 'Aborting OpenMEEG pipeline due to above error.']); - end -end - -if ft_senstype(sens, 'meg') - disp('Contribution of all sources to the MEG sensors') - scFile = cell(chunks,1); - for ii = 1:chunks - scFile{ii} = fullfile(path, [basefile '_ds2mm' num2str(ii) om_ext]); - if exist(scFile{ii},'file') - fprintf('\t%s already exists. Skipping...\n',scFile{ii}) - else - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_assemble'), ' -ds2mm ', dipFile{ii} ,' ', sensorFile, ' ' , scFile{ii}]) - if(om_status ~= 0) % status = 0 if successful - ft_error([om_msg, 'Aborting OpenMEEG pipeline due to above error.']); - end - end - end -end - -disp('Putting it all together.') -bemFile = cell(chunks,1); -for ii = 1:chunks - if ft_senstype(sens, 'eeg') - bemFile{ii} = fullfile(path, [basefile '_eeggain' num2str(ii) om_ext]); - else - bemFile{ii} = fullfile(path, [basefile '_meggain' num2str(ii) om_ext]); - end - - if exist(bemFile{ii},'file') - fprintf('/t%s already exists. Skipping...\n', bemFile{ii}); - continue; - end - - if strcmp(method,'hminv') - if ft_senstype(sens, 'eeg') - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_gain'), ' -EEG ', hminvFile, ' ', dsmFile{ii}, ' ', ohmicFile, ' ', bemFile{ii}]); - else - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_gain'), ' -MEG ', hminvFile, ' ', dsmFile{ii}, ' ', ohmicFile,' ', scFile{ii}, ' ',bemFile{ii}]); - end - else % Adjoint method - if ft_senstype(sens, 'eeg') - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_gain'), ' -EEGadjoint ', geomFile, ' ', condFile, ' ', dipFile{ii},' ', hmFile, ' ', ohmicFile, ' ', bemFile{ii}]); - else - [om_status, om_msg] = system([fullfile(OPENMEEG_PATH, 'om_gain'), ' -MEGadjoint ', geomFile, ' ', condFile, ' ', dipFile{ii},' ', hmFile, ' ', ohmicFile, ' ', scFile{ii}, ' ',bemFile{ii}]); - end - end - if(om_status ~= 0) % status = 0 if successful - ft_error([om_msg, 'Aborting OpenMEEG pipeline due to above error.']); - end -end - -% Import lead field/potential -[g, voxels_in] = import_gain(path, basefile, ft_senstype(sens, 'eeg')); -if (voxels_in ~= voxels) && (nargout == 1); ft_warning('Imported voxels from OpenMEEG process not the same as function input.'); end; - -lp = sens.tra*g; % Mchannels x (3 orientations x Nvoxels) - -% Cleanup -if cleanup_flag - rmdir(basepath,'s') -end -if (isunix && ~ismac) - setenv('LD_LIBRARY_PATH',ldLibraryPath0); -end - - - -function write_cond(vol,filename) -fid=fopen(filename,'w'); -fprintf(fid,'# Properties Description 1.0 (Conductivities)\n'); -tissues = {'Scalp\t%f\n'; 'Skull\t%f\n'; 'CSF\t%f\n'; 'Brain\t%f\n'}; -if length(vol.cond)==3; tissues = tissues([1 2 4]); end; -fprintf(fid,'Air\t0\n'); -for ii=1:length(vol.cond) - fprintf(fid,tissues{ii},vol.cond(ii)); -end -fclose(fid); - - -function write_geom(vol,filename,basepathfile) -fid=fopen(filename,'w'); -fprintf(fid,'# Domain Description 1.0\n'); -fprintf(fid,'Interfaces %i Mesh\n',length(vol.cond)); -tissues={'_scalp.tri\n'; '_skull.tri\n'; '_csf.tri\n'; '_brain.tri\n'}; -if length(vol.cond)==3; tissues = tissues([1 2 4]); end; -for ii = 1:length(vol.cond) - fprintf(fid,[basepathfile tissues{ii}]); -end -fprintf('\n'); -fprintf(fid,'Domains %i\n',length(vol.cond)+1); -domains={'Scalp'; 'Skull'; 'CSF'; 'Brain'}; -if length(vol.cond)==3; domains = domains([1 2 4]); end; -fprintf(fid,'Domain Air %i\n',1); -for ii = 1:length(vol.cond) - if ii < length(vol.cond) - fprintf(fid,['Domain ' domains{ii} ' %i -%i\n'],ii+1,ii); - else - fprintf(fid,['Domain ' domains{ii} ' -%i\n'],ii); - end -end -fclose(fid); - - -function write_mesh(vol,path,basefile) -tissues={'_scalp'; '_skull'; '_csf'; '_brain'}; -if length(vol.cond)==3; tissues = tissues([1 2 4]); end; -for ii = 1:length(vol.cond) - meshFile = fullfile(path, [basefile tissues{ii} '.tri']); - if exist(meshFile,'file') - fprintf('\t%s already exists. Skipping...\n', meshFile); - break; - else - om_save_tri(fullfile(path, [basefile tissues{ii} '.tri']),vol.bnd(ii).pos,vol.bnd(ii).tri); - %savemesh([path basefile tissues{ii} '.mesh'],vol.bnd(ii).vertices/1000,vol.bnd(ii).faces-1,-vol.bnd(ii).normals); - end -end - - -function [g, voxels] = import_gain(path, basefile, eegflag) -om_format = 'binary'; -switch(om_format) - case 'matlab' - om_ext = '.mat'; - case 'binary' - om_ext = '.bin'; - otherwise - ft_error('invalid OpenMEEG output type requested'); -end - -if eegflag - omgainfiles = dir(fullfile(path, [basefile '_eeggain*' om_ext])); -else - omgainfiles = dir(fullfile(path, [basefile '_meggain*' om_ext])); -end -omvoxfiles = dir(fullfile(path, [basefile '_voxels*' om_ext])); - -g=[]; -voxels=[]; - -% join gain/voxel files -% [openmeeg calculation may have been split for memory reasons] -for ii=1:length(omgainfiles) - g = [g om_load_full(fullfile(path, omgainfiles(ii).name),om_format)]; - voxels = [voxels;om_load_full(fullfile(path, omvoxfiles(ii).name),om_format)]; -end -voxels = voxels(1:3:end,1:3); diff --git a/external/fieldtrip/forward/private/leadsphere_all.m b/external/fieldtrip/forward/private/leadsphere_all.m index 345df226..5051d646 100644 --- a/external/fieldtrip/forward/private/leadsphere_all.m +++ b/external/fieldtrip/forward/private/leadsphere_all.m @@ -2,6 +2,7 @@ % usage: out=leadsphere_all(xloc,sensorloc,sensorori) % Copyright (C) 2003, Guido Nolte +% Copyright (C) 2018, Jan-Mathijs Schoffelen % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -21,36 +22,84 @@ % % $Id$ -[n,nsens]=size(sensorloc); %n=3 m=? -[n,ndip]=size(xloc); - - -xlocrep=reshape(repmat(xloc,1,nsens),3,ndip,nsens); -sensorlocrep=reshape(repmat(sensorloc,ndip,1),3,ndip,nsens); -sensororirep=reshape(repmat(sensorori,ndip,1),3,ndip,nsens); - -r2=norms(sensorlocrep); -veca=sensorlocrep-xlocrep; -a=norms(veca); -adotr2=dotproduct(veca,sensorlocrep); - -gradf1=scal2vec(1./r2.*(a.^2)+adotr2./a+2*a+2*r2); -gradf2=scal2vec(a+2*r2+adotr2./a); -gradf=gradf1.*sensorlocrep-gradf2.*xlocrep; - -F=a.*(r2.*a+adotr2); - -A1=scal2vec(1./F); -A2=A1.^2; - -A3=crossproduct(xlocrep,sensororirep); -A4=scal2vec(dotproduct(gradf,sensororirep)); -A5=crossproduct(xlocrep,sensorlocrep); - -out=1e-7*(A3.*A1-(A4.*A2).*A5); %%GRB change +% ---Here begins Guido's original implementation +% [n,nsens]=size(sensorloc); %n=3 m=? +% [n,ndip]=size(xloc); +% +% +% xlocrep=reshape(repmat(xloc,1,nsens),3,ndip,nsens); +% sensorlocrep=reshape(repmat(sensorloc,ndip,1),3,ndip,nsens); +% sensororirep=reshape(repmat(sensorori,ndip,1),3,ndip,nsens); +% +% r2=norms(sensorlocrep); +% veca=sensorlocrep-xlocrep; +% a=norms(veca); +% adotr2=dotproduct(veca,sensorlocrep); +% +% gradf1=scal2vec(1./r2.*(a.^2)+adotr2./a+2*a+2*r2); +% gradf2=scal2vec(a+2*r2+adotr2./a); +% gradf=gradf1.*sensorlocrep-gradf2.*xlocrep; +% +% F=a.*(r2.*a+adotr2); +% +% A1=scal2vec(1./F); +% A2=A1.^2; +% +% A3=crossproduct(xlocrep,sensororirep); +% A4=scal2vec(dotproduct(gradf,sensororirep)); +% A5=crossproduct(xlocrep,sensorlocrep); +% +% out=1e-7*(A3.*A1-(A4.*A2).*A5); %%GRB change +% +% return; + +% Here begins JMS' adjustment to Guido's implementation, which is much more +% memory friendly with large numbers of dipoles. Essentially, it does not +% work with massively repmatted sensorloc and xloc matrices +[dum,nsens] = size(sensorloc); %n=3 m=? +[dum, ndip] = size(xloc); + +r2 = repmat(sqrt(sum(sensorloc.^2)), ndip, 1); +veca = reshape(repmat(sensorloc,ndip,1),3,ndip,nsens)-reshape(repmat(xloc,1,nsens),3,ndip,nsens); +a = norms(veca); +adotr2 = dotproduct_light(veca,sensorloc'); + +gradf1 = (1./r2.*(a.^2)+adotr2./a+2*a+2*r2); +gradf2 = (a+2*r2+adotr2./a); + +gradf = zeros(3,ndip,nsens); +gradf(1,:,:) = gradf1*spdiags(sensorloc(1,:)',0,nsens,nsens) - spdiags(xloc(1,:)',0,ndip,ndip)*gradf2; +gradf(2,:,:) = gradf1*spdiags(sensorloc(2,:)',0,nsens,nsens) - spdiags(xloc(2,:)',0,ndip,ndip)*gradf2; +gradf(3,:,:) = gradf1*spdiags(sensorloc(3,:)',0,nsens,nsens) - spdiags(xloc(3,:)',0,ndip,ndip)*gradf2; + +A1 = shiftdim(1./(a.*(r2.*a+adotr2)),-1); +A42 = shiftdim(dotproduct_light(gradf,sensorori'),-1).*(A1.^2); % this is the element wise product of A2 and A4 in Guido's original + +A3 = crossproduct_light(xloc', sensorori'); +A5 = crossproduct_light(xloc',sensorloc'); + +out = zeros(size(A3)); +out(1,:,:) = 1e-7*(A3(1,:,:).*A1 - A5(1,:,:).*A42); +out(2,:,:) = 1e-7*(A3(2,:,:).*A1 - A5(2,:,:).*A42); +out(3,:,:) = 1e-7*(A3(3,:,:).*A1 - A5(3,:,:).*A42); +return; +function out=crossproduct_light(x,y) +% this should work because the original x is always repmatted along the +% third (original, i.e. sens) dimension, the y along the second (i.e. loc) +m=size(x,1); +k=size(y,1); +out=zeros(3,m,k); +out(1,:,:)=x(:,2)*y(:,3)'-x(:,3)*y(:,2)'; +out(2,:,:)=x(:,3)*y(:,1)'-x(:,1)*y(:,3)'; +out(3,:,:)=x(:,1)*y(:,2)'-x(:,2)*y(:,1)'; return; +function out=dotproduct_light(x,y) +k = size(y,1); +% this should work because the x is always 3xnlocxnsens, and y is nsensx3 +out=shiftdim(x(1,:,:))*spdiags(y(:,1),0,k,k)+shiftdim(x(2,:,:))*spdiags(y(:,2),0,k,k)+shiftdim(x(3,:,:))*spdiags(y(:,3),0,k,k); +return; function out=crossproduct(x,y) diff --git a/external/fieldtrip/forward/private/lmoutr.mexw32 b/external/fieldtrip/forward/private/lmoutr.mexw32 index d367b100..f2f1837a 100755 Binary files a/external/fieldtrip/forward/private/lmoutr.mexw32 and b/external/fieldtrip/forward/private/lmoutr.mexw32 differ diff --git a/external/fieldtrip/forward/private/lmoutr.mexw64 b/external/fieldtrip/forward/private/lmoutr.mexw64 index ab5adac3..01167e39 100755 Binary files a/external/fieldtrip/forward/private/lmoutr.mexw64 and b/external/fieldtrip/forward/private/lmoutr.mexw64 differ diff --git a/external/fieldtrip/forward/private/meg_ini.m b/external/fieldtrip/forward/private/meg_ini.m index e1f437f6..6a6a05de 100644 --- a/external/fieldtrip/forward/private/meg_ini.m +++ b/external/fieldtrip/forward/private/meg_ini.m @@ -49,22 +49,22 @@ % $Id$ if nargin==4 - if order>0; + if order>0 coeff_sens=getcoeffs(sens,vc,center,order); forwpar=struct('device_sens',sens,'coeff_sens',coeff_sens,'center',center,'order',order); else forwpar=struct('device_sens',sens,'center',center,'order',order); end elseif nargin==5 - if order>0; + if order>0 coeff_sens=getcoeffs(sens,vc,center,order); coeff_refs=getcoeffs(refs,vc,center,order); forwpar=struct('device_sens',sens,'device_ref',refs,'coeff_sens',coeff_sens,'coeff_ref',coeff_refs,'center',center,'order',order); else forwpar=struct('device_sens',sens,'device_ref',refs,'center',center,'order',order); end -elseif nargin==7; - if order>0; +elseif nargin==7 + if order>0 coeff_sens=getcoeffs(sens,vc,center,order); coeff_refs=getcoeffs(refs,vc,center,order); coeff_weights=getcoeffs(gradlocs,vc,center,order); diff --git a/external/fieldtrip/forward/private/meg_leadfield1.mexw32 b/external/fieldtrip/forward/private/meg_leadfield1.mexw32 index 02535914..46b4a498 100755 Binary files a/external/fieldtrip/forward/private/meg_leadfield1.mexw32 and b/external/fieldtrip/forward/private/meg_leadfield1.mexw32 differ diff --git a/external/fieldtrip/forward/private/meg_leadfield1.mexw64 b/external/fieldtrip/forward/private/meg_leadfield1.mexw64 index 637143e2..53217707 100755 Binary files a/external/fieldtrip/forward/private/meg_leadfield1.mexw64 and b/external/fieldtrip/forward/private/meg_leadfield1.mexw64 differ diff --git a/external/fieldtrip/forward/private/plgndr.mexw32 b/external/fieldtrip/forward/private/plgndr.mexw32 index 54c20c2d..cd8ee1f6 100755 Binary files a/external/fieldtrip/forward/private/plgndr.mexw32 and b/external/fieldtrip/forward/private/plgndr.mexw32 differ diff --git a/external/fieldtrip/forward/private/plgndr.mexw64 b/external/fieldtrip/forward/private/plgndr.mexw64 index 52d67c59..98ba9a87 100755 Binary files a/external/fieldtrip/forward/private/plgndr.mexw64 and b/external/fieldtrip/forward/private/plgndr.mexw64 differ diff --git a/external/fieldtrip/forward/private/ptriproj.mexw32 b/external/fieldtrip/forward/private/ptriproj.mexw32 index caf4b04a..74c929f1 100755 Binary files a/external/fieldtrip/forward/private/ptriproj.mexw32 and b/external/fieldtrip/forward/private/ptriproj.mexw32 differ diff --git a/external/fieldtrip/forward/private/ptriproj.mexw64 b/external/fieldtrip/forward/private/ptriproj.mexw64 index 0421d849..3bf9f947 100755 Binary files a/external/fieldtrip/forward/private/ptriproj.mexw64 and b/external/fieldtrip/forward/private/ptriproj.mexw64 differ diff --git a/external/fieldtrip/forward/private/routlm.mexw32 b/external/fieldtrip/forward/private/routlm.mexw32 index f32719c3..108a2287 100755 Binary files a/external/fieldtrip/forward/private/routlm.mexw32 and b/external/fieldtrip/forward/private/routlm.mexw32 differ diff --git a/external/fieldtrip/forward/private/routlm.mexw64 b/external/fieldtrip/forward/private/routlm.mexw64 index c48f4352..c2af5526 100755 Binary files a/external/fieldtrip/forward/private/routlm.mexw64 and b/external/fieldtrip/forward/private/routlm.mexw64 differ diff --git a/external/fieldtrip/forward/private/solid_angle.mexw32 b/external/fieldtrip/forward/private/solid_angle.mexw32 index 6ffabebf..c7231629 100755 Binary files a/external/fieldtrip/forward/private/solid_angle.mexw32 and b/external/fieldtrip/forward/private/solid_angle.mexw32 differ diff --git a/external/fieldtrip/forward/private/solid_angle.mexw64 b/external/fieldtrip/forward/private/solid_angle.mexw64 index 899195aa..54123674 100755 Binary files a/external/fieldtrip/forward/private/solid_angle.mexw64 and b/external/fieldtrip/forward/private/solid_angle.mexw64 differ diff --git a/external/fieldtrip/ft_anonimizedata.m b/external/fieldtrip/ft_anonimizedata.m index 5150cf4a..916426f5 100644 --- a/external/fieldtrip/ft_anonimizedata.m +++ b/external/fieldtrip/ft_anonimizedata.m @@ -278,19 +278,12 @@ function redraw_cb(h, eventdata) function keyboard_cb(h, eventdata) -% if isempty(eventdata) -% % determine the key that corresponds to the uicontrol element that was activated -% key = get(h, 'userdata'); -% else -% % determine the key that was pressed on the keyboard -% key = parseKeyboardEvent(eventdata); -% end if (isempty(eventdata) && ft_platform_supports('matlabversion',-Inf, '2014a')) || isa(eventdata, 'matlab.ui.eventdata.ActionData') % determine the key that corresponds to the uicontrol element that was activated key = get(h, 'userdata'); else % determine the key that was pressed on the keyboard - key = parseKeyboardEvent(eventdata); + key = parsekeyboardevent(eventdata); end h = getparent(h); @@ -330,33 +323,6 @@ function keyboard_cb(h, eventdata) redraw_cb(h) end % function -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; - -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character; - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end - -end % function parseKeyboardEvent - function sort_cb(h, eventdata) h = getparent(h); info = getappdata(h, 'info'); diff --git a/external/fieldtrip/ft_appenddata.m b/external/fieldtrip/ft_appenddata.m index bcf787a2..1e3e7bd7 100644 --- a/external/fieldtrip/ft_appenddata.m +++ b/external/fieldtrip/ft_appenddata.m @@ -5,7 +5,9 @@ % % Use as % data = ft_appenddata(cfg, data1, data2, data3, ...) -% where the configuration can be empty. +% +% The following configuration options are supported: +% cfg.keepsampleinfo = 'yes', 'no', 'ifmakessense' (default = 'ifmakessense') % % If the input datasets all have the same channels, the trials will be concatenated. % This is useful for example if you have different experimental conditions, which, @@ -19,6 +21,11 @@ % data that you want to analyze contains both MEG and EMG channels which require % different preprocessing options. % +% If you concatenate trials and the data originates from the same original datafile, +% the sampleinfo is consistent and you can specify cfg.keepsampleinfo='yes'. If the +% data originates from different datafiles, the sampleinfo is inconsistent and does +% not point to the same recording, hence you should specify cfg.keepsampleinfo='no'. +% % Occasionally, the data needs to be concatenated in the trial dimension while % there's a slight discrepancy in the channels in the input data (e.g. missing % channels in one of the data structures). The function will then return a data @@ -34,7 +41,7 @@ % cell array for this particular function. % % See also FT_PREPROCESSING, FT_DATAYPE_RAW, FT_APPENDTIMELOCK, FT_APPENDFREQ, -% FT_APPENDSENS, FT_APPENDSOURCE +% FT_APPENDSOURCE, FT_APPENDSENS % Copyright (C) 2005-2008, Robert Oostenveld % Copyright (C) 2009-2011, Jan-Mathijs Schoffelen @@ -75,10 +82,24 @@ return end -% check if the input data is valid for this function +% set the defaults +cfg.keepsampleinfo = ft_getopt(cfg, 'keepsampleinfo', 'ifmakessense'); + +try + % although not 100% robust, this could make some users becoming aware of the issue of overlapping trials + for i=1:numel(varargin) + dataset{i} = ft_findcfg(varargin{i}.cfg, 'dataset'); + hassampleinfo(i) = isfield(varargin{i}, 'sampleinfo'); + end + if ~all(strcmp(dataset, dataset{1})) && ~strcmp(cfg.keepsampleinfo, 'no') + ft_warning('the data originates from different recordings on disk'); + ft_warning('please consider specifying cfg.keepsampleinfo=''no''') + end +end % try + +% ensure that the input data is valid for this function for i=1:length(varargin) - % FIXME: raw+comp is not always dealt with correctly - varargin{i} = ft_checkdata(varargin{i}, 'datatype', {'raw', 'raw+comp'}, 'feedback', 'no'); + varargin{i} = ft_checkdata(varargin{i}, 'datatype', {'raw', 'raw+comp'}, 'feedback', 'no', 'hassampleinfo', cfg.keepsampleinfo); end % set the defaults diff --git a/external/fieldtrip/ft_appendfreq.m b/external/fieldtrip/ft_appendfreq.m index ca21bb32..f1052dd3 100644 --- a/external/fieldtrip/ft_appendfreq.m +++ b/external/fieldtrip/ft_appendfreq.m @@ -68,7 +68,6 @@ % check if the input data is valid for this function for i=1:length(varargin) - % FIXME: what about freq+comp? varargin{i} = ft_checkdata(varargin{i}, 'datatype', {'freq', 'freq+comp'}, 'feedback', 'yes'); end @@ -107,7 +106,7 @@ end end end -fprintf('concatenating over the "%s" dimension\n', cfg.appenddim); +ft_info('concatenating over the "%s" dimension\n', cfg.appenddim); if isempty(cfg.parameter) fn = fieldnames(varargin{1}); diff --git a/external/fieldtrip/ft_appendsens.m b/external/fieldtrip/ft_appendsens.m index 986d15fd..7feee5e3 100644 --- a/external/fieldtrip/ft_appendsens.m +++ b/external/fieldtrip/ft_appendsens.m @@ -14,7 +14,7 @@ % See also FT_ELECTRODEPLACEMENT, FT_ELECTRODEREALIGN, FT_DATAYPE_SENS, % FT_APPENDDATA, FT_APPENDTIMELOCK, FT_APPENDFREQ, FT_APPENDSOURCE -% Copyright (C) 2017, Arjen Stolk +% Copyright (C) 2017-2018, Arjen Stolk % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -148,16 +148,14 @@ % concatenate the main fields and remove duplicates sens.label = cat(1,label{:}); -[~, labidx] = unique(sens.label); -labidx = sort(labidx); +[dum, labidx] = unique(sens.label, 'stable'); if ~isequal(numel(labidx), numel(sens.label)) fprintf('removing duplicate labels\n') sens.label = sens.label(labidx); end sens.chanpos = cat(1,chanpos{:}); -[~, chanidx] = unique(sens.chanpos, 'rows'); -chanidx = sort(chanidx); +[dum, chanidx] = unique(sens.chanpos, 'rows', 'stable'); if ~isequal(numel(chanidx), size(sens.chanpos,1)) fprintf('removing duplicate channels\n') sens.chanpos = sens.chanpos(chanidx,:); @@ -186,8 +184,7 @@ if haselecpos sens.elecpos = cat(1,elecpos{:}); - [~, elecidx, elecidx2] = unique(sens.elecpos, 'rows'); - [elecidx, elecord] = sort(elecidx); % sort and keep track of the order + [dum, elecidx, elecidx2] = unique(sens.elecpos, 'rows', 'stable'); if ~isequal(numel(elecidx), size(sens.elecpos,1)) fprintf('removing duplicate electrodes\n') sens.elecpos = sens.elecpos(elecidx,:); @@ -195,12 +192,12 @@ if isfield(sens, 'tra') % shape duplicates into a single column, if necessary for idx = 1:numel(elecidx) - tmp(:, idx) = sum(sens.tra(chanidx, find(elecord(idx)==elecidx2)),2); + tmp(:, idx) = sum(sens.tra(chanidx, find(idx==elecidx2)),2); % find and take sum over duplicates end sens.tra = tmp; - % check for expected size and non-empty rows or columns + % check for expected size and non-empty rows if ~isequal(size(sens.tra,1), size(sens.chanpos,1)) || ~isequal(size(sens.tra,2), size(sens.elecpos,1)) ... - || any(any(sens.tra,1)==0) || any(any(sens.tra,2)==0) + || any(any(sens.tra,2)==0) % all channels need to be linked to their origins fprintf('removing inconsistent tra matrix\n') sens = rmfield(sens, 'tra'); end @@ -209,8 +206,7 @@ if hasoptopos sens.optopos = cat(1,optopos{:}); - [~, optoidx, optoidx2] = unique(sens.optopos, 'rows'); - [optoidx, optoord] = sort(optoidx); % sort and keep track of the order + [dum, optoidx, optoidx2] = unique(sens.optopos, 'rows', 'stable'); if ~isequal(numel(optoidx), size(sens.optopos,1)) fprintf('removing duplicate optodes\n') sens.optopos = sens.optopos(optoidx,:); @@ -218,12 +214,12 @@ if isfield(sens, 'tra') % shape duplicates into a single column, if necessary for idx = 1:numel(optoidx) - tmp(:, idx) = sum(sens.tra(chanidx, find(optoord(idx)==optoidx2)),2); + tmp(:, idx) = sum(sens.tra(chanidx, find(idx==optoidx2)),2); % find and take sum over duplicates end sens.tra = tmp; - % check for expected size and non-empty rows or columns + % check for expected size and non-empty rows if ~isequal(size(sens.tra,1), size(sens.chanpos,1)) || ~isequal(size(sens.tra,2), size(sens.optopos,1)) ... - || any(any(sens.tra,1)==0) || any(any(sens.tra,2)==0) + || any(any(sens.tra,2)==0) % all channels need to be linked to their origins fprintf('removing inconsistent tra matrix\n') sens = rmfield(sens, 'tra'); end @@ -232,8 +228,7 @@ if hascoilpos sens.coilpos = cat(1,coilpos{:}); - [~, coilidx, coilidx2] = unique(sens.coilpos, 'rows'); - [coilidx, coilord] = sort(coilidx); % sort and keep track of the order + [dum, coilidx, coilidx2] = unique(sens.coilpos, 'rows', 'stable'); if ~isequal(numel(coilidx), size(sens.coilpos,1)) fprintf('removing duplicate coils\n') sens.coilpos = sens.coilpos(coilidx,:); @@ -241,12 +236,12 @@ if isfield(sens, 'tra') % shape duplicates into a single column, if necessary for idx = 1:numel(coilidx) - tmp(:, idx) = sum(sens.tra(chanidx, find(coilord(idx)==coilidx2)),2); + tmp(:, idx) = sum(sens.tra(chanidx, find(idx==coilidx2)),2); % find and take sum over duplicates end sens.tra = tmp; - % check for expected size and non-empty rows or columns + % check for expected size and non-empty rows if ~isequal(size(sens.tra,1), size(sens.chanpos,1)) || ~isequal(size(sens.tra,2), size(sens.coilpos,1)) ... - || any(any(sens.tra,1)==0) || any(any(sens.tra,2)==0) + || any(any(sens.tra,2)==0) % all channels need to be linked to their origins fprintf('removing inconsistent tra matrix\n') sens = rmfield(sens, 'tra'); end @@ -260,7 +255,7 @@ end % keep the following fields only when identical across inputs -if haslabelold && all(strcmp(labelold{1}, labelold)) % labeloldmatch +if haslabelold && all(isequal(labelold{1}, labelold{:})) % labeloldmatch sens.labelold = labelold{1}; end if haschanposold && all(isequal(chanposold{1}, chanposold{:})) % chanposoldmatch diff --git a/external/fieldtrip/ft_appendsource.m b/external/fieldtrip/ft_appendsource.m index fcb9d153..fc887334 100644 --- a/external/fieldtrip/ft_appendsource.m +++ b/external/fieldtrip/ft_appendsource.m @@ -1,21 +1,22 @@ function [source] = ft_appendsource(cfg, varargin) -% FT_APPENDSOURCE concatenates multiple volumetric source reconstruction -% data structures that have been processed seperately. +% FT_APPENDSOURCE concatenates multiple volumetric source reconstruction data +% structures that have been processed seperately. % -% If the source reconstructions were computed for different ROIs or -% different slabs of a regular 3D grid (as indicated by the source -% positions), the data will be concatenated along the spatial dimension. +% If the source reconstructions were computed for different ROIs or different slabs +% of a regular 3D grid (as indicated by the source positions), the data will be +% concatenated along the spatial dimension. % -% If the source reconstructions were computed on the same source -% positions, but for different frequencies and/or latencies, e.g. for -% time-frequency spectrally decomposed data, the data will be concatenared -% along the frequency and/or time dimension. +% If the source reconstructions were computed on the same source positions, but for +% different frequencies and/or latencies, e.g. for time-frequency spectrally +% decomposed data, the data will be concatenared along the frequency and/or time +% dimension. % % Use as % combined = ft_appendsource(cfg, source1, source2, ...) % -% See also FT_SOURCEANALYSIS, FT_APPENDDATA, FT_APPENDFREQ, FT_APPENDSOURCE +% See also FT_SOURCEANALYSIS, FT_DATATYPE_SOURCE, FT_APPENDDATA, FT_APPENDTIMELOCK, +% FT_APPENDFREQ % Copyright (C) 2011, Robert Oostenveld % diff --git a/external/fieldtrip/ft_appendtimelock.m b/external/fieldtrip/ft_appendtimelock.m index fbb84cc6..bd492c1c 100644 --- a/external/fieldtrip/ft_appendtimelock.m +++ b/external/fieldtrip/ft_appendtimelock.m @@ -9,16 +9,19 @@ % Use as % combined = ft_appendtimelock(cfg, timelock1, timelock2, ...) % +% The following configuration options are supported: +% % The configuration can optionally contain -% cfg.appenddim = string, the dimension to concatenate over which to append, -% this can be 'chan' and 'rpt' (default is automatic) -% cfg.tolerance = scalar, tolerance to determine how different the time axes -% are allowed to still be considered compatible (default = 1e-5) +% cfg.appenddim = string, the dimension to concatenate over which to append, +% this can be 'chan' and 'rpt' (default is automatic) +% cfg.tolerance = scalar, tolerance to determine how different the time axes +% are allowed to still be considered compatible (default = 1e-5) +% cfg.keepsampleinfo = 'yes', 'no', 'ifmakessense' (default = 'ifmakessense') % % See also FT_TIMELOCKANALYSIS, FT_DATATYPE_TIMELOCK, FT_APPENDDATA, FT_APPENDFREQ, -% FT_APPENDSENS, FT_APPENDSOURCE +% FT_APPENDSOURCE, FT_APPENDSENS -% Copyright (C) 2011-2017, Robert Oostenveld +% Copyright (C) 2011-2018, Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -56,19 +59,31 @@ return end -% check if the input data is valid for this function +% set the defaults +cfg.channel = ft_getopt(cfg, 'channel', 'all'); +cfg.parameter = ft_getopt(cfg, 'parameter', []); +cfg.appenddim = ft_getopt(cfg, 'appenddim', []); +cfg.tolerance = ft_getopt(cfg, 'tolerance', 1e-5); +cfg.appendsens = ft_getopt(cfg, 'appendsens', 'no'); +cfg.keepsampleinfo = ft_getopt(cfg, 'keepsampleinfo', 'no'); + +try + % although not 100% robust, this could make some users becoming aware of the issue of overlapping trials + for i=1:numel(varargin) + dataset{i} = ft_findcfg(varargin{i}.cfg, 'dataset'); + hassampleinfo(i) = isfield(varargin{i}, 'sampleinfo'); + end + if ~all(strcmp(dataset, dataset{1})) && ~strcmp(cfg.keepsampleinfo, 'no') + ft_warning('the data originates from different recordings on disk'); + ft_warning('please consider specifying cfg.keepsampleinfo=''no''') + end +end % try + +% ensure that the input data is valid for this function for i=1:length(varargin) - % FIXME: what about timelock+comp? - varargin{i} = ft_checkdata(varargin{i}, 'datatype', {'timelock', 'timelock+comp'}, 'feedback', 'yes', 'hassampleinfo', 'ifmakessense'); + varargin{i} = ft_checkdata(varargin{i}, 'datatype', {'timelock', 'timelock+comp'}, 'feedback', 'yes', 'hassampleinfo', cfg.keepsampleinfo); end -% set the defaults -cfg.channel = ft_getopt(cfg, 'channel', 'all'); -cfg.parameter = ft_getopt(cfg, 'parameter', []); -cfg.appenddim = ft_getopt(cfg, 'appenddim', []); -cfg.tolerance = ft_getopt(cfg, 'tolerance', 1e-5); -cfg.appendsens = ft_getopt(cfg, 'appendsens', 'no'); - if isempty(cfg.appenddim) || strcmp(cfg.appenddim, 'auto') if checkchan(varargin{:}, 'identical') && checktime(varargin{:}, 'identical', cfg.tolerance) cfg.appenddim = 'rpt'; @@ -82,7 +97,7 @@ ft_error('cfg.appenddim should be specified'); end end -fprintf('concatenating over the "%s" dimension\n', cfg.appenddim); +ft_info('concatenating over the "%s" dimension\n', cfg.appenddim); if isempty(cfg.parameter) fn = fieldnames(varargin{1}); diff --git a/external/fieldtrip/ft_artifact_clip.m b/external/fieldtrip/ft_artifact_clip.m index 04e91052..8de2916d 100644 --- a/external/fieldtrip/ft_artifact_clip.m +++ b/external/fieldtrip/ft_artifact_clip.m @@ -14,6 +14,7 @@ % % Alternatively you can use it as % [cfg, artifact] = ft_artifact_clip(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % In both cases the configuration should also contain % cfg.artfctdef.clip.channel = Nx1 cell-array with selection of channels, see FT_CHANNELSELECTION for details diff --git a/external/fieldtrip/ft_artifact_ecg.m b/external/fieldtrip/ft_artifact_ecg.m index 3047bc48..9973b090 100644 --- a/external/fieldtrip/ft_artifact_ecg.m +++ b/external/fieldtrip/ft_artifact_ecg.m @@ -1,17 +1,24 @@ function [cfg, artifact] = ft_artifact_ecg(cfg, data) -% FT_ARTIFACT_ECG performs a peak-detection on the ECG-channel. The -% heart activity can be observed in the MEG data as an MCG artifact. +% FT_ARTIFACT_ECG performs a peak-detection on the ECG-channel and identifies the windows +% around the QRS peak as artifacts. Using FT_REJECTARTIFACT you can remove these windows from +% your data, or using FT_REMOVETEMPLATEARTIFACT you can subtract an averaged template artifact +% from your data. % % Use as % [cfg, artifact] = ft_artifact_ecg(cfg) % with the configuration options -% cfg.dataset -% cfg.headerfile -% cfg.datafile +% cfg.dataset = string with the filename +% or +% cfg.headerfile = string with the filename +% cfg.datafile = string with the filename +% and optionally +% cfg.headerformat +% cfg.dataformat % % Alternatively you can use it as % [cfg, artifact] = ft_artifact_ecg(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % In both cases the configuration should also contain % cfg.trl = structure that defines the data segments of interest. See FT_DEFINETRIAL @@ -35,8 +42,9 @@ % file on disk. This mat files should contain only a single variable named 'data', % corresponding to the input structure. % -% See also FT_REJECTARTIFACT, FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG, FT_ARTIFACT_EOG, -% FT_ARTIFACT_JUMP, FT_ARTIFACT_MUSCLE, FT_ARTIFACT_THRESHOLD, FT_ARTIFACT_ZVALUE +% See also FT_REJECTARTIFACT, FT_REMOVETEMPLATEARTIFACT, FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG, +% FT_ARTIFACT_EOG, FT_ARTIFACT_JUMP, FT_ARTIFACT_MUSCLE, FT_ARTIFACT_THRESHOLD, +% FT_ARTIFACT_ZVALUE % Copyright (C) 2005-2011, Jan-Mathijs Schoffelen % @@ -94,9 +102,9 @@ if ~isfield(cfg.artfctdef.ecg, 'pretim'), cfg.artfctdef.ecg.pretim = 0.05; end if ~isfield(cfg.artfctdef.ecg, 'psttim'), cfg.artfctdef.ecg.psttim = 0.3; end if ~isfield(cfg.artfctdef.ecg, 'mindist'), cfg.artfctdef.ecg.mindist = 0.5; end -if ~isfield(cfg.artfctdef.ecg, 'feedback'), cfg.artfctdef.ecg.feedback = 'yes'; end -if ~isfield(cfg, 'headerformat'), cfg.headerformat = []; end -if ~isfield(cfg, 'dataformat'), cfg.dataformat = []; end +if ~isfield(cfg.artfctdef.ecg, 'feedback'), cfg.artfctdef.ecg.feedback = 'yes'; end +if ~isfield(cfg, 'headerformat'), cfg.headerformat = []; end +if ~isfield(cfg, 'dataformat'), cfg.dataformat = []; end if ~strcmp(cfg.artfctdef.ecg.method, 'zvalue') ft_error('method "%s" is not applicable', cfg.artfctdef.ecg.method); @@ -111,7 +119,7 @@ hdr = ft_read_header(cfg.headerfile, 'headerformat', cfg.headerformat); trl = cfg.trl; else - data = ft_checkdata(data, 'hassampleinfo', 'yes'); + data = ft_checkdata(data, 'datatype', 'raw', 'hassampleinfo', 'yes'); cfg = ft_checkconfig(cfg, 'forbidden', {'dataset', 'headerfile', 'datafile'}); hdr = ft_fetch_header(data); if isfield(data, 'sampleinfo') @@ -125,7 +133,6 @@ end artfctdef = cfg.artfctdef.ecg; -padsmp = round(artfctdef.padding*hdr.Fs); ntrl = size(trl,1); artfctdef.trl = trl; artfctdef.channel = ft_channelselection(artfctdef.channel, hdr.label); @@ -151,7 +158,47 @@ % read in the ecg-channel and do demean and squaring if hasdata - tmpcfg = []; + % this list originates from ft_checkconfig + fieldname = { + 'reref' + 'refchannel' + 'implicitref' + 'detrend' + 'bpfiltdir' + 'bpfilter' + 'bpfiltord' + 'bpfilttype' + 'bpfreq' + 'bsfiltdir' + 'bsfilter' + 'bsfiltord' + 'bsfilttype' + 'bsfreq' + 'demean' + 'baselinewindow' + 'denoise' + 'dftfilter' + 'dftfreq' + 'hpfiltdir' + 'hpfilter' + 'hpfiltord' + 'hpfilttype' + 'hpfreq' + 'lpfiltdir' + 'lpfilter' + 'lpfiltord' + 'lpfilttype' + 'lpfreq' + 'medianfilter' + 'medianfiltord' + 'hilbert' + 'derivative' + 'rectify' + 'boxcar' + 'absdiff' + }; + + tmpcfg = keepfields(artfctdef, fieldname); tmpcfg.channel = artfctdef.channel; ecgdata = ft_preprocessing(tmpcfg, data); ecg = ecgdata.trial; diff --git a/external/fieldtrip/ft_artifact_eog.m b/external/fieldtrip/ft_artifact_eog.m index 9db3a292..0a786b4d 100644 --- a/external/fieldtrip/ft_artifact_eog.m +++ b/external/fieldtrip/ft_artifact_eog.m @@ -10,9 +10,13 @@ % or % cfg.headerfile = string with the filename % cfg.datafile = string with the filename +% and optionally +% cfg.headerformat +% cfg.dataformat % % Alternatively you can use it as % [cfg, artifact] = ft_artifact_eog(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % In both cases the configuration should also contain % cfg.trl = structure that defines the data segments of interest. See FT_DEFINETRIAL diff --git a/external/fieldtrip/ft_artifact_jump.m b/external/fieldtrip/ft_artifact_jump.m index 4ce6f5e0..c0bbe17f 100644 --- a/external/fieldtrip/ft_artifact_jump.m +++ b/external/fieldtrip/ft_artifact_jump.m @@ -10,9 +10,13 @@ % or % cfg.headerfile = string with the filename % cfg.datafile = string with the filename +% and optionally +% cfg.headerformat +% cfg.dataformat % % Alternatively you can use it as % [cfg, artifact] = ft_artifact_jump(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % In both cases the configuration should also contain % cfg.trl = structure that defines the data segments of interest. See FT_DEFINETRIAL diff --git a/external/fieldtrip/ft_artifact_muscle.m b/external/fieldtrip/ft_artifact_muscle.m index 8ee13e6c..aaad914c 100644 --- a/external/fieldtrip/ft_artifact_muscle.m +++ b/external/fieldtrip/ft_artifact_muscle.m @@ -10,9 +10,13 @@ % or % cfg.headerfile = string with the filename % cfg.datafile = string with the filename +% and optionally +% cfg.headerformat +% cfg.dataformat % % Alternatively you can use it as % [cfg, artifact] = ft_artifact_muscle(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % In both cases the configuration should also contain % cfg.trl = structure that defines the data segments of interest. See FT_DEFINETRIAL diff --git a/external/fieldtrip/ft_artifact_nan.m b/external/fieldtrip/ft_artifact_nan.m index d8c64689..f9fc67f1 100644 --- a/external/fieldtrip/ft_artifact_nan.m +++ b/external/fieldtrip/ft_artifact_nan.m @@ -1,9 +1,15 @@ function [cfg, artifact] = ft_artifact_nan(cfg, data) -% FT_ARTIFACT_NAN identifies artifacts that are indicated in the data as nan (not a number) values. +% FT_ARTIFACT_NAN identifies artifacts that are indicated in the data as nan (not a +% number) values. % % Use as -% [cfg, artifact] = ft_artifact_eog(cfg, data) +% [cfg, artifact] = ft_artifact_nan(cfg, data) +% where the input data is a structure as obtained from FT_REJECTARTIFACT with +% the option cfg.artfctdef.reject='nan'. +% +% The configuration can contain +% cfg.artfctdef.nan.channel = Nx1 cell-array with selection of channels, see FT_CHANNELSELECTION for details % % The output argument "artifact" is a Nx2 matrix comparable to the % "trl" matrix of FT_DEFINETRIAL. The first column of which specifying the @@ -57,12 +63,21 @@ return end +% set the default options +cfg.artfctdef = ft_getopt(cfg, 'artfctdef'); +cfg.artfctdef.nan = ft_getopt(cfg.artfctdef, 'nan'); +cfg.artfctdef.nan.channel = ft_getopt(cfg.artfctdef.nan, 'channel', {'all'}); + +% check if the input data is valid for this function, the input data must be raw data = ft_checkdata(data, 'datatype', 'raw', 'hassampleinfo', 'yes'); +cfg.artfctdef.nan.channel = ft_channelselection(cfg.artfctdef.nan.channel, data.label); +chansel = match_str(data.label, cfg.artfctdef.nan.channel); + artifact = zeros(0,2); for i=1:numel(data.trial) - tmp = any(isnan(data.trial{i}),1); + tmp = any(isnan(data.trial{i}(chansel,:)),1); if any(tmp) % there can be multiple segments with nans begsample = find(diff([0 tmp])>0); diff --git a/external/fieldtrip/ft_artifact_threshold.m b/external/fieldtrip/ft_artifact_threshold.m index 005555c4..5559aafa 100644 --- a/external/fieldtrip/ft_artifact_threshold.m +++ b/external/fieldtrip/ft_artifact_threshold.m @@ -1,8 +1,8 @@ function [cfg, artifact] = ft_artifact_threshold(cfg, data) -% FT_ARTIFACT_THRESHOLD scans for trials in which the range, i.e. the minimum, -% the maximum or the range (min-max difference) of the signal in any -% channel exceeds a specified threshold. +% FT_ARTIFACT_THRESHOLD scans for trials in which the range, i.e. the minimum, the +% maximum or the range (min-max difference) of the signal in any channel exceeds a +% specified threshold. % % Use as % [cfg, artifact] = ft_artifact_threshold(cfg) @@ -11,9 +11,13 @@ % or % cfg.headerfile = string with the filename % cfg.datafile = string with the filename +% and optionally +% cfg.headerformat +% cfg.dataformat % % Alternatively you can use it as % [cfg, artifact] = ft_artifact_threshold(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % In both cases the configuration should also contain % cfg.trl = structure that defines the data segments of interest, see FT_DEFINETRIAL @@ -21,23 +25,37 @@ % % The following configuration options can be specified % cfg.artfctdef.threshold.channel = cell-array with channel labels -% cfg.artfctdef.threshold.bpfilter = 'no' or 'yes' +% cfg.artfctdef.threshold.bpfilter = 'no' or 'yes' (default = 'yes') % cfg.artfctdef.threshold.bpfreq = [0.3 30] % cfg.artfctdef.threshold.bpfiltord = 4 % +% It is also possible to use other filter (lpfilter, hpfilter, +% bsfilter, dftfilter or medianfilter) instead of a bpfilter for +% preprocessing, see FT_PREPROCESSING +% % The detection of artifacts is done according to the following settings, % you should specify at least one of these thresholds -% cfg.artfctdef.threshold.range = value in uV/T, default inf -% cfg.artfctdef.threshold.min = value in uV/T, default -inf -% cfg.artfctdef.threshold.max = value in uV/T, default inf +% cfg.artfctdef.threshold.range = value in uV or T, default inf +% cfg.artfctdef.threshold.min = value in uV or T, default -inf +% cfg.artfctdef.threshold.max = value in uV or T, default inf +% cfg.artfctdef.threshold.onset = value in uV or T, default inf +% cfg.artfctdef.threshold.offset = value in uV or T, default inf +% +% When cfg.artfctdef.threshold.range is used, the within-channel peak-to-peak range +% is checked against the specified maximum range (so not the overall range across +% channels). In this case the whole trial will be marked as an artifact. +% +% When cfg.artfctdef.threshold.onset and offset are used, the rising and falling +% flank are thresholded with different values. In case onset and offset are both +% positive, the data will be thresholded above their values. In case both onset and +% offset are negative, the data will be thresholded below their values. % -% When cfg.artfctdef.threshold.range is used, the within-channel -% peak-to-peak range is checked against the specified maximum range (so not -% the overall range across channels). +% Note that this function does not support artifact- or filterpadding. % -% Contrary to the other artifact detection functions, this function -% will mark the whole trial as an artifact if the threshold is exceeded. -% Furthermore, this function does not support artifact- or filterpadding. +% The output argument "artifact" is a Nx2 matrix comparable to the +% "trl" matrix of FT_DEFINETRIAL. The first column of which specifying the +% beginsamples of an artifact period, the second column contains the +% endsamples of the artifactperiods. % % To facilitate data-handling and distributed computing you can use % cfg.inputfile = ... @@ -118,10 +136,16 @@ if ~isfield(artfctdef, 'range'), artfctdef.range = inf; end if ~isfield(artfctdef, 'min'), artfctdef.min = -inf; end if ~isfield(artfctdef, 'max'), artfctdef.max = inf; end +if ~isfield(artfctdef, 'onset'), artfctdef.onset = []; end +if ~isfield(artfctdef, 'offset'), artfctdef.offset = []; end % the data is either passed into the function by the user or read from file with cfg.inputfile hasdata = exist('data', 'var'); +if hasdata + data = ft_checkdata(data, 'datatype', 'raw', 'hassampleinfo', 'yes'); +end + % read the header, or get it from the input data if ~hasdata cfg = ft_checkconfig(cfg, 'dataset2files', 'yes'); @@ -145,7 +169,7 @@ if ~isfield(cfg, 'trl') % get it from the data itself - cfg.trl = data.trialinfo; + cfg.trl = data.sampleinfo; cfg.trl(:,3) = 0; end @@ -153,7 +177,20 @@ numtrl = size(cfg.trl,1); channel = ft_channelselection(artfctdef.channel, hdr.label); channelindx = match_str(hdr.label,channel); -artifact = []; +artifact = zeros(0,3); + +if ~isempty(artfctdef.onset) || ~isempty(artfctdef.offset) + if artfctdef.onset>0 && artfctdef.offset>0 + direction = 'up'; + elseif artfctdef.onset<0 && artfctdef.offset<0 + direction = 'down'; + else + error('incorrect specification of onset and offset'); + end +else + direction = 'none'; +end + for trlop = 1:numtrl if hasdata @@ -161,35 +198,80 @@ else dat = ft_read_data(cfg.datafile, 'header', hdr, 'begsample', cfg.trl(trlop,1), 'endsample', cfg.trl(trlop,2), 'chanindx', channelindx, 'checkboundary', strcmp(cfg.continuous, 'no'), 'dataformat', cfg.dataformat); end - dat = preproc(dat, channel, offset2time(cfg.trl(trlop,3), hdr.Fs, size(dat,2)), artfctdef); - % compute the min, max and range over all channels and samples - minval = min(dat(:)); - maxval = max(dat(:)); - % compute the range as the maximum of the peak-to-peak values for each - % channel - ptpval = max(dat, [], 2) - min(dat, [], 2); + % only do the preprocessing if there is an option that suggests to have an effect + status = struct2cell(artfctdef); + status = status(cellfun(@(x) ischar(x), status)); + if any(ismember(status, {'yes', 'abs', 'complex', 'real', 'imag', 'absreal', 'absimag', 'angle'})) + dat = preproc(dat, channel, offset2time(cfg.trl(trlop,3), hdr.Fs, size(dat,2)), artfctdef); + end - % track for bad trials for each channel - badChnInd = find(ptpval > artfctdef.range); + % make a vector that indicates for each sample whether there is an artifact + artval = false(1, size(dat,2)); + artval = artval | any(dat<=artfctdef.min,1); + artval = artval | any(dat>=artfctdef.max,1); + + % compute the range as the maximum of the peak-to-peak values for each channel + ptpval = max(dat, [], 2) - min(dat, [], 2); + if any(ptpval>=artfctdef.range) + artval(:) = true; % mark the whole segment as bad + end - % determine range and index of 'worst' channel - worstChanRange = max(ptpval); - worstChanInd = find(worstChanRange == ptpval); + % this is when a different onset and offset are specified + switch direction + case 'up' + onset = find(diff([0 any(dat>=artfctdef.onset,1)])>0); % find the rising flank + offset = nan(size(onset)); + for i=1:numel(onset) + rem = dat(:,onset(i):end); % this is the remaining data following the artifact onset + offset(i) = find(diff([any(rem<=artfctdef.offset,1) 0])>0, 1, 'first'); % find the falling flank + offset(i) = offset(i) + onset(i); + % add it to the other artifacts in the boolean vector + artval(onset(i):offset(i)) = true; + end + + case 'down' + onset = find(diff([0 any(dat<=artfctdef.onset,1)])>0); % find the rising flank + offset = nan(size(onset)); + for i=1:numel(onset) + rem = dat(:,onset(i):end); % this is the remaining data following the artifact onset + offset(i) = find(diff([any(rem>=artfctdef.offset,1) 0])>0, 1, 'first'); % find the falling flank + offset(i) = offset(i) + onset(i); + % add it to the other artifacts in the boolean vector + artval(onset(i):offset(i)) = true; + end + + case 'none' + % nothing to do + end - % test the min, max and range against the specified thresholds - if ~isempty(artfctdef.min) && minvalartfctdef.max - ft_info('threshold artifact scanning: trial %d from %d exceeds max-threshold\n', trlop, numtrl); - artifact(end+1,1:2) = cfg.trl(trlop,1:2); - elseif ~isempty(artfctdef.range) && worstChanRange>artfctdef.range - ft_info('threshold artifact scanning: trial %d from %d exceeds range-threshold; max-range channel = %s\n', trlop, numtrl, hdr.label{channelindx(worstChanInd)}); - artifact(end+1,1:2) = cfg.trl(trlop,1:2); - else - ft_info('threshold artifact scanning: trial %d from %d is ok\n', trlop, numtrl); + if any(artval) + begsample = find(diff([false artval])>0) + cfg.trl(trlop,1) - 1; + endsample = find(diff([artval false])<0) + cfg.trl(trlop,1) - 1; + offset = nan(size(begsample)); + + if size(dat,1)==1 + % determine the offset of the peak value, this only works in case of a single channel + for i=1:numel(begsample) + seg = dat(begsample(i):endsample(i)); % get the segment of data + if all(seg>=artfctdef.max) || strcmp(direction, 'up') + [dum, indx] = max(seg); + offset(i) = 1 - indx; % relative to the start of the segment, 0 is the first sample, -1 is the 2nd, etc. + elseif all(seg<=artfctdef.min) || strcmp(direction, 'down') + [dum, indx] = min(seg); + offset(i) = 1 - indx; % relative to the start of the segment, 0 is the first sample, -1 is the 2nd, etc. + end % if up or down + end % for each artifact in this trial + end % if single channel + + artifact = cat(1, artifact, [begsample(:) endsample(:) offset(:)]); end + +end % for trllop + +if any(isnan(artifact(:,3))) + % don't keep the offset if it cannot be determined consistently + artifact = artifact(:,[1 2]); end ft_info('detected %d artifacts\n', size(artifact,1)); diff --git a/external/fieldtrip/ft_artifact_tms.m b/external/fieldtrip/ft_artifact_tms.m index 85632abc..892818a2 100644 --- a/external/fieldtrip/ft_artifact_tms.m +++ b/external/fieldtrip/ft_artifact_tms.m @@ -1,7 +1,7 @@ function [cfg, artifact] = ft_artifact_tms(cfg, data) -% FT_ARTIFACT_TMS reads the data segments of interest from file and -% identifies tms artifacts. +% FT_ARTIFACT_TMS reads the data segments of interest from file and identifies artefacts in +% EEG recordings that were done during TMS stimulation. % % Use as % [cfg, artifact] = ft_artifact_tms(cfg) @@ -10,17 +10,19 @@ % or % cfg.headerfile = string with the filename % cfg.datafile = string with the filename +% and optionally +% cfg.headerformat +% cfg.dataformat % % Alternatively you can use it as % [cfg, artifact] = ft_artifact_tms(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % In both cases the configuration should also contain % cfg.trl = structure that defines the data segments of interest. See FT_DEFINETRIAL % cfg.continuous = 'yes' or 'no' whether the file contains continuous data (default = 'yes') -% cfg.method = 'detect', TMS-artifacts are detected by preprocessing -% the data to be sensitive to transient high gradients, typical for -% TMS-pulses. -% 'marker', TMS-artifact onset and offsets are based on +% and +% cfg.method = 'detect' or 'marker', see below. % markers written in the EEG. % cfg.prestim = scalar, time in seconds prior to onset of detected % event to mark as artifactual (default = 0.005 seconds) @@ -29,38 +31,34 @@ % % METHOD SPECIFIC OPTIONS AND DESCRIPTIONS % -% DETECT -% The data is preprocessed (again) with the following configuration parameters, -% which are optimal for identifying tms artifacts. This acts as a wrapper -% around ft_artifact_zvalue +% With cfg.method='detect', TMS-artifacts are detected by preprocessing the data to be +% sensitive to transient high gradients, typical for TMS-pulses. The data is preprocessed +% (again) with the following configuration parameters, which are optimal for identifying tms +% artifacts. This acts as a wrapper around ft_artifact_zvalue % cfg.artfctdef.tms.derivative = 'yes' -% % Artifacts are identified by means of thresholding the z-transformed value % of the preprocessed data. % cfg.artfctdef.tms.channel = Nx1 cell-array with selection of channels, see FT_CHANNELSELECTION for details % cfg.artfctdef.tms.cutoff = z-value at which to threshold (default = 4) % cfg.artfctdef.tms.trlpadding = 0.1 % cfg.artfctdef.tms.fltpadding = 0.1 -% cfg.artfctdef.tms.artpadding = 0.01 (Be aware that if one artifact -% falls within this specified range of another artifact, both artifact -% will be counted as one. Depending on cfg.prestim and cfg.poststim you -% may not mark enough data as artifactual.) -% -% MARKER -% This method acts as a wrapper around FT_DEFINETRIAL to determine on- and -% offsets of TMS pulses by reading markers in the EEG. +% cfg.artfctdef.tms.artpadding = 0.01 +% Be aware that if one artifact falls within this specified range of another artifact, both +% artifact will be counted as one. Depending on cfg.prestim and cfg.poststim you may not mark +% enough data as artifactual.) +% +% With cfg.method='marker', TMS-artifact onset and offsets are based on markers/triggers that +% are written into the EEG dataset. This method acts as a wrapper around FT_DEFINETRIAL to +% determine on- and offsets of TMS pulses by reading markers in the EEG. % cfg.trialfun = function name, see below (default = 'ft_trialfun_general') % cfg.trialdef.eventtype = 'string' % cfg.trialdef.eventvalue = number, string or list with numbers or strings -% -% The cfg.trialfun option is a string containing the name of a function -% that you wrote yourself and that FT_ARTIFACT_TMS will call. The -% function should take the cfg-structure as input and should give a -% NxM matrix with M equal to or larger than 3) in the same format as -% "trl" as the output. You can add extra custom fields to the -% configuration structure to pass as arguments to your own trialfun. -% Furthermore, inside the trialfun you can use the FT_READ_EVENT -% function to get the event information from your data file. +% The cfg.trialfun option is a string containing the name of a function that you wrote +% yourself and that FT_ARTIFACT_TMS will call. The function should take the cfg-structure as +% input and should give a NxM matrix with M equal to or larger than 3) in the same format as +% "trl" as the output. You can add extra custom fields to the configuration structure to +% pass as arguments to your own trialfun. Furthermore, inside the trialfun you can use the +% FT_READ_EVENT function to get the event information from your data file. % % The output argument "artifact" is a Nx2 matrix comparable to the % "trl" matrix of FT_DEFINETRIAL. The first column of which specifying the diff --git a/external/fieldtrip/ft_artifact_zvalue.m b/external/fieldtrip/ft_artifact_zvalue.m index dea82acd..bddf797b 100644 --- a/external/fieldtrip/ft_artifact_zvalue.m +++ b/external/fieldtrip/ft_artifact_zvalue.m @@ -1,24 +1,13 @@ function [cfg, artifact] = ft_artifact_zvalue(cfg, data) -% FT_ARTIFACT_ZVALUE reads the interesting segments of data from file and -% identifies artifacts by means of thresholding the z-transformed value -% of the preprocessed raw data. Depending on the preprocessing options, -% this method will be sensitive to EOG, muscle or jump artifacts. -% This procedure only works on continuously recorded data. +% FT_ARTIFACT_ZVALUE reads the interesting segments of data from file and identifies +% artifacts by means of thresholding the z-transformed value of the preprocessed raw data. +% Depending on the preprocessing options, this method will be sensitive to EOG, muscle or +% jump artifacts. This procedure only works on continuously recorded data. % % Use as % [cfg, artifact] = ft_artifact_zvalue(cfg) -% or -% [cfg, artifact] = ft_artifact_zvalue(cfg, data) -% -% The output argument "artifact" is a Nx2 matrix comparable to the -% "trl" matrix of FT_DEFINETRIAL. The first column of which specifying the -% beginsamples of an artifact period, the second column contains the -% endsamples of the artifactperiods. -% -% If you are calling FT_ARTIFACT_ZVALUE with only the configuration as first -% input argument and the data still has to be read from file, you should -% specify +% with the configuration options % cfg.dataset = string with the filename % or % cfg.headerfile = string with the filename @@ -27,22 +16,23 @@ % cfg.headerformat % cfg.dataformat % -% If you are calling FT_ARTIFACT_ZVALUE with also the second input argument -% "data", then that should contain data that was already read from file -% a call to FT_PREPROCESSING. -% -% If you encounter difficulties with memory usage, you can use -% cfg.memory = 'low' or 'high', whether to be memory or computationally efficient, respectively (default = 'high') +% Alternatively you can use it as +% [cfg, artifact] = ft_artifact_zvalue(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. % % The required configuration settings are: -% cfg.trl -% cfg.continuous +% cfg.trl = structure that defines the data segments of interest. See FT_DEFINETRIAL +% cfg.continuous = 'yes' or 'no' whether the file contains continuous data (default = 'yes') +% and % cfg.artfctdef.zvalue.channel % cfg.artfctdef.zvalue.cutoff % cfg.artfctdef.zvalue.trlpadding % cfg.artfctdef.zvalue.fltpadding % cfg.artfctdef.zvalue.artpadding % +% If you encounter difficulties with memory usage, you can use +% cfg.memory = 'low' or 'high', whether to be memory or computationally efficient, respectively (default = 'high') +% % The optional configuration settings (see below) are: % cfg.artfctdef.zvalue.artfctpeak = 'yes' or 'no' % cfg.artfctdef.zvalue.interactive = 'yes' or 'no' @@ -107,6 +97,11 @@ % cfg.artfctdef.zvalue.hilbert = 'no' or 'yes' % cfg.artfctdef.zvalue.rectify = 'no' or 'yes' % +% The output argument "artifact" is a Nx2 matrix comparable to the +% "trl" matrix of FT_DEFINETRIAL. The first column of which specifying the +% beginsamples of an artifact period, the second column contains the +% endsamples of the artifactperiods. +% % See also FT_REJECTARTIFACT, FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG, FT_ARTIFACT_EOG, % FT_ARTIFACT_JUMP, FT_ARTIFACT_MUSCLE, FT_ARTIFACT_THRESHOLD, FT_ARTIFACT_ZVALUE @@ -287,8 +282,8 @@ if ~pertrial % accumulate the sum and the sum-of-squares - sumval = sumval + sum(dat,2); - sumsqr = sumsqr + sum(dat.^2,2); + sumval = sumval + nansum(dat,2); + sumsqr = sumsqr + nansum(dat.^2,2); numsmp = numsmp + size(dat,2); else % store per trial the sum and the sum-of-squares @@ -318,8 +313,8 @@ if ~pertrial % accumulate the sum and the sum-of-squares - sumval = sumval + sum(dat{trlop},2); - sumsqr = sumsqr + sum(dat{trlop}.^2,2); + sumval = sumval + nansum(dat{trlop},2); + sumsqr = sumsqr + nansum(dat{trlop}.^2,2); numsmp = numsmp + size(dat{trlop},2); else % store per trial the sum and the sum-of-squares @@ -722,7 +717,7 @@ function keyboard_cb(h, eventdata) curKey = eventdata.Key; else curKey = [sprintf('%s+', eventdata.Modifier{:}) eventdata.Key]; - end + end elseif isfield(eventdata, 'Key') % only when key was pressed curKey = eventdata.Key; elseif isempty(eventdata) % matlab2012b returns an empty double upon a mouse click @@ -734,7 +729,6 @@ function keyboard_cb(h, eventdata) h = getparent(h); % otherwise h is empty if isa [...].ActionData opt = getappdata(h, 'opt'); -disp(strcat('Key = ', curKey)) switch strtrim(curKey) case 'leftarrow' % change trials opt.trlop = max(opt.trlop - 1, 1); % should not be smaller than 1 @@ -786,38 +780,38 @@ function keyboard_cb(h, eventdata) end setappdata(h, 'opt', opt); redraw_cb(h, eventdata); -% case 'control+uparrow' % change channel -% if strcmp(opt.channel, 'artifact') -% [dum, indx] = max(opt.zval); -% sgnind = opt.zindx(indx); -% else -% if ~isempty(opt.data) -% sgnind = match_str(opt.channel, opt.data.label); -% selchan = match_str(opt.artcfg.channel, opt.channel); -% else -% sgnind = match_str(opt.channel, opt.hdr.label); -% selchan = match_str(opt.artcfg.channel, opt.channel); -% end -% end -% numchan = numel(opt.artcfg.channel); -% chansel = min(selchan+1, numchan); -% % convert numeric array into cell-array with channel labels -% opt.channel = tmpchan(chansel); -% setappdata(h, 'opt', opt); -% redraw_cb(h, eventdata); -% case 'c' % select channel -% select = match_str([opt.artcfg.channel;{'artifact'}], opt.channel); -% opt.channel = select_channel_list([opt.artcfg.channel;{'artifact'}], select); -% setappdata(h, 'opt', opt); -% redraw_cb(h, eventdata); -% case 'control+downarrow' -% tmpchan = [opt.artcfg.channel;{'artifact'}]; % append the 'artifact' channel -% chansel = match_str(tmpchan, opt.channel); -% chansel = max(chansel-1, 1); -% % convert numeric array into cell-array with channel labels -% opt.channel = tmpchan(chansel); -% setappdata(h, 'opt', opt); -% redraw_cb(h, eventdata); + % case 'control+uparrow' % change channel + % if strcmp(opt.channel, 'artifact') + % [dum, indx] = max(opt.zval); + % sgnind = opt.zindx(indx); + % else + % if ~isempty(opt.data) + % sgnind = match_str(opt.channel, opt.data.label); + % selchan = match_str(opt.artcfg.channel, opt.channel); + % else + % sgnind = match_str(opt.channel, opt.hdr.label); + % selchan = match_str(opt.artcfg.channel, opt.channel); + % end + % end + % numchan = numel(opt.artcfg.channel); + % chansel = min(selchan+1, numchan); + % % convert numeric array into cell-array with channel labels + % opt.channel = tmpchan(chansel); + % setappdata(h, 'opt', opt); + % redraw_cb(h, eventdata); + % case 'c' % select channel + % select = match_str([opt.artcfg.channel;{'artifact'}], opt.channel); + % opt.channel = select_channel_list([opt.artcfg.channel;{'artifact'}], select); + % setappdata(h, 'opt', opt); + % redraw_cb(h, eventdata); + % case 'control+downarrow' + % tmpchan = [opt.artcfg.channel;{'artifact'}]; % append the 'artifact' channel + % chansel = match_str(tmpchan, opt.channel); + % chansel = max(chansel-1, 1); + % % convert numeric array into cell-array with channel labels + % opt.channel = tmpchan(chansel); + % setappdata(h, 'opt', opt); + % redraw_cb(h, eventdata); case 'a' % select the artifact to display response = inputdlg(sprintf('artifact trial to display'), 'specify', 1, {num2str(opt.trlop)}); @@ -944,15 +938,18 @@ function redraw_cb(h, eventdata) if ~isempty(opt.data) data = ft_fetch_data(opt.data, 'header', hdr, 'begsample', trl(trlop,1), 'endsample', trl(trlop,2), 'chanindx', sgnind, 'checkboundary', strcmp(cfg.continuous, 'no')); else - data = ft_read_data(cfg.datafile, 'header', hdr, 'begsample', trl(trlop,1), 'endsample', trl(trlop,2), 'chanindx', sgnind, 'checkboundary', strcmp(cfg.continuous, 'no')); + data = ft_read_data(cfg.datafile, 'header', hdr, 'begsample', trl(trlop,1), 'endsample', trl(trlop,2), 'chanindx', sgnind, 'checkboundary', strcmp(cfg.continuous, 'no')); end -%data = preproc(data, '', hdr.Fs, artcfg, [], artcfg.fltpadding, artcfg.fltpadding); -str = sprintf('trial %3d, channel %s', opt.trlop, hdr.label{sgnind}); + +% data = preproc(data, '', hdr.Fs, artcfg, [], artcfg.fltpadding, artcfg.fltpadding); + +% the string us used as title and printed in the command window +str = sprintf('trial %3d of %d, channel %s', trlop, size(trl,1), hdr.label{sgnind}); fprintf('showing %s\n', str); %----------------------------- % plot summary in left subplot -subplot(opt.h1);hold on; +subplot(opt.h1); hold on; % plot as a blue line only once if isempty(get(opt.h1, 'children')) @@ -1041,10 +1038,10 @@ function redraw_cb(h, eventdata) subplot(opt.h2); hold on if isempty(get(opt.h2, 'children')) % do the plotting - plot(xval(selpad), data(selpad), 'color', [0.5 0.5 1], 'displayname', 'line1'); - plot(xval(sel), data(sel), 'color', [0 0 1], 'displayname', 'line2'); - vline(xval( 1)+(trlpadsmp-1/opt.hdr.Fs), 'color', [0 0 0], 'displayname', 'vline1'); - vline(xval(end)-(trlpadsmp/opt.hdr.Fs), 'color', [0 0 0], 'displayname', 'vline2'); + plot(xval(selpad), data(selpad), 'color', [0.5 0.5 1], 'displayname', 'line1'); + plot(xval(sel), data(sel), 'color', [0 0 1], 'displayname', 'line2'); + vline(xval( 1)+(trlpadsmp-1/opt.hdr.Fs), 'color', [0 0 0], 'displayname', 'vline1'); + vline(xval(end)-(trlpadsmp/opt.hdr.Fs), 'color', [0 0 0], 'displayname', 'vline2'); data(~artval) = nan; plot(xval, data, 'r-', 'displayname', 'line3'); xlabel('time(s)'); @@ -1099,8 +1096,8 @@ function redraw_cb(h, eventdata) set(findall(h3children, 'displayname', 'line2b'), 'XData', xval(sel)); set(findall(h3children, 'displayname', 'line2b'), 'YData', zval(sel)); zval(~artval) = nan; - set(findall(h3children, 'displayname', 'line3b'), 'XData', xval); - set(findall(h3children, 'displayname', 'line3b'), 'YData', zval); + set(findall(h3children, 'displayname', 'line3b'), 'XData', xval); + set(findall(h3children, 'displayname', 'line3b'), 'YData', zval); set(findall(h3children, 'displayname', 'threshline'), 'YData', [1 1].*opt.threshold); set(findall(h3children, 'displayname', 'threshline'), 'XData', xval([1 end])); abc = axis(opt.h3); @@ -1135,30 +1132,3 @@ function cleanup_cb(h, eventdata) p = get(h, 'parent'); end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; - -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character; - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end diff --git a/external/fieldtrip/ft_channelnormalise.m b/external/fieldtrip/ft_channelnormalise.m index cb78b3c4..644c78b6 100644 --- a/external/fieldtrip/ft_channelnormalise.m +++ b/external/fieldtrip/ft_channelnormalise.m @@ -8,12 +8,13 @@ % [dataout] = ft_channelnormalise(cfg, data) % % The configuration can contain -% cfg.channel = 'all', or a selection of channels -% cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') -% cfg.demean = 'yes' or 'no' (or boolean value) (default = 'yes') -% cfg.method = 'perchannel', or 'acrosschannel', computes the +% cfg.channel = 'all', or a selection of channels +% cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') +% cfg.demean = 'yes' or 'no' (or boolean value) (default = 'yes') +% cfg.scale = scalar value used for scaling (default = 1) +% cfg.method = 'perchannel', or 'acrosschannel', computes the % standard deviation per channel, or across all channels. -% The latter method leads to the same scaling across +% The latter method leads to the same scaling across % channels and preserves topographical distributions % % To facilitate data-handling and distributed computing you can use @@ -67,12 +68,14 @@ cfg = ft_checkconfig(cfg, 'allowedval', {'method', 'perchannel', 'acrosschannel'}); % set the defaults -cfg.channel = ft_getopt(cfg, 'channel', 'all'); -cfg.trials = ft_getopt(cfg, 'trials', 'all', 1); -cfg.demean = ft_getopt(cfg, 'demean', 'yes'); -cfg.method = ft_getopt(cfg, 'method', 'perchannel'); % or acrosschannel -dodemean = istrue(cfg.demean); -doperchannel = strcmp(cfg.method, 'perchannel'); +cfg.channel = ft_getopt(cfg, 'channel', 'all'); +cfg.trials = ft_getopt(cfg, 'trials', 'all', 1); +cfg.scale = ft_getopt(cfg, 'scale', 1); +cfg.demean = ft_getopt(cfg, 'demean', 'yes'); +cfg.method = ft_getopt(cfg, 'method', 'perchannel'); % or acrosschannel + +dodemean = istrue(cfg.demean); +doperchannel = strcmp(cfg.method, 'perchannel'); % select channels and trials of interest, by default this will select all channels and trials tmpcfg = keepfields(cfg, {'trials', 'channel', 'showcallinfo'}); @@ -116,9 +119,9 @@ % compute the mean and std n = zeros(numel(data.label), numel(data.trial)); for k = 1:ntrl - n(:,k) = sum(~isnan(data.trial{k}),2); - datsum = datsum + nansum(data.trial{k},2); - datssq = datssq + nansum(data.trial{k}.^2,2); + n(:,k) = sum(~isnan(data.trial{k}),2); + datsum = datsum + nansum(data.trial{k},2); + datssq = datssq + nansum(data.trial{k}.^2,2); end datmean = datsum./nansum(n, 2); % apply the mean per channel always if ~doperchannel @@ -127,7 +130,7 @@ datssq(:) = nansum(datssq); n = repmat(nansum(n, 1), size(n, 1), 1); end -datstd = sqrt( (datssq - (datsum.^2)./nansum(n, 2))./nansum(n, 2)); %quick way to compute std from sum and sum-of-squared values +datstd = sqrt( (datssq - (datsum.^2)./nansum(n, 2))./nansum(n, 2)); %quick way to compute std from sum and sum-of-squared values % keep mean and std in output cfg if dodemean @@ -139,11 +142,11 @@ % demean and normalise for k = 1:ntrl - onesvec = ones(1,size(data.trial{k},2)); - if dodemean - dataout.trial{k} = (data.trial{k}-datmean(:,onesvec))./datstd(:,onesvec); + onesvec = ones(1,size(data.trial{k},2)); + if dodemean + dataout.trial{k} = cfg.scale * (data.trial{k}-datmean(:,onesvec))./datstd(:,onesvec); else - dataout.trial{k} = data.trial{k}./datstd(:,onesvec); + dataout.trial{k} = cfg.scale * data.trial{k}./datstd(:,onesvec); end end diff --git a/external/fieldtrip/ft_clusterplot.m b/external/fieldtrip/ft_clusterplot.m index 340ec72f..1f2a0207 100644 --- a/external/fieldtrip/ft_clusterplot.m +++ b/external/fieldtrip/ft_clusterplot.m @@ -397,7 +397,7 @@ % make plots for iPl = 1:Nfig - f = figure('visible', cfg.visible); + figure('visible', cfg.visible); if is2D if iPl < Nfig for iT = 1:numSubplots diff --git a/external/fieldtrip/ft_combineplanar.m b/external/fieldtrip/ft_combineplanar.m index 66e18be5..0a6fa96b 100644 --- a/external/fieldtrip/ft_combineplanar.m +++ b/external/fieldtrip/ft_combineplanar.m @@ -180,7 +180,7 @@ dum = permute(dum, [2 3 1]); dum = reshape(dum, [2 Ntim*Nrpt]); timbin = ~isnan(dum(1,:)); - [loading, ~, ori, sin_val] = svdfft(dum(:,timbin),2,data.cumtapcnt); + [loading, dum, ori, sin_val] = svdfft(dum(:,timbin),2,data.cumtapcnt); dum2 = loading(1,:); dum(1,timbin) = dum2; dum = reshape(dum(1,:),[Ntim Nrpt]); @@ -246,7 +246,7 @@ tmpdat(:, (Csmp(m)+1):Csmp(m+1)) = data.trial{m}([sel_dH(k) sel_dV(k)],:); end if strcmp(cfg.method, 'abssvd')||strcmp(cfg.method, 'svd') - [loading, ~, ori, sin_val] = svdfft(tmpdat,2); + [loading, dum, ori, sin_val] = svdfft(tmpdat,2); data.ori{k} = ori; % to change into a cell data.eta{k} = sin_val(1)/sum(sin_val(2:end)); % to change into a cell if strcmp(cfg.method, 'abssvd') diff --git a/external/fieldtrip/ft_componentanalysis.m b/external/fieldtrip/ft_componentanalysis.m index 605ef278..43c9c196 100644 --- a/external/fieldtrip/ft_componentanalysis.m +++ b/external/fieldtrip/ft_componentanalysis.m @@ -4,7 +4,7 @@ % spatio-temporal decompositions of EEG or MEG data. This function computes % the topography and timecourses of the components. The output of this % function can be further analyzed with FT_TIMELOCKANALYSIS or -% FT_FREQNANALYSIS. +% FT_FREQANALYSIS. % % Use as % [comp] = ft_componentanalysis(cfg, data) @@ -258,6 +258,9 @@ cfg.dss.denf = ft_getopt(cfg.dss, 'denf', []); cfg.dss.denf.function = ft_getopt(cfg.dss.denf, 'function', 'denoise_fica_tanh'); cfg.dss.denf.params = ft_getopt(cfg.dss.denf, 'params', []); + cfg.dss.preprocf = ft_getopt(cfg.dss, 'preprocf', []); + cfg.dss.preprocf.function = ft_getopt(cfg.dss.preprocf, 'function', 'pre_sphere'); + cfg.dss.preprocf.params = ft_getopt(cfg.dss.preprocf, 'params', []); case 'csp' % additional options, see CSP for details cfg.csp = ft_getopt(cfg, 'csp', []); @@ -276,10 +279,8 @@ end % select trials of interest -tmpcfg = []; -tmpcfg.trials = cfg.trials; -tmpcfg.channel = cfg.channel; -data = ft_selectdata(tmpcfg, data); +tmpcfg = keepfields(cfg, {'trials', 'channel', 'showcallinfo'}); +data = ft_selectdata(tmpcfg, data); % restore the provenance information [cfg, data] = rollback_provenance(cfg, data); @@ -305,7 +306,7 @@ if strcmp(cfg.demean, 'yes') % optionally perform baseline correction on each trial - fprintf('baseline correcting data \n'); + ft_info('baseline correcting data \n'); for trial=1:Ntrials data.trial{trial} = ft_preproc_baselinecorrect(data.trial{trial}); end @@ -319,31 +320,31 @@ scale = norm((tmp*tmp')./size(tmp,2)); clear tmp; scale = sqrt(scale); if scale ~= 0 - fprintf('scaling data with 1 over %f\n', scale); + ft_info('scaling data with 1 over %f\n', scale); for trial=1:Ntrials data.trial{trial} = data.trial{trial} ./ scale; end else - fprintf('no scaling applied, since factor is 0\n'); + ft_info('no scaling applied, since factor is 0\n'); end else - fprintf('no scaling applied to the data\n'); + ft_info('no scaling applied to the data\n'); end if strcmp(cfg.method, 'sobi') % concatenate all the data into a 3D matrix respectively 2D (sobi) - fprintf('concatenating data'); + ft_info('concatenating data'); Nsamples = Nsamples(1); dat = zeros(Ntrials, Nchans, Nsamples); % all trials should have an equal number of samples % and it is assumed that the time axes of all trials are aligned for trial=1:Ntrials - fprintf('.'); + ft_info('.'); dat(trial,:,:) = data.trial{trial}; end - fprintf('\n'); - fprintf('concatenated data matrix size %dx%dx%d\n', size(dat,1), size(dat,2), size(dat,3)); + ft_info('\n'); + ft_info('concatenated data matrix size %dx%dx%d\n', size(dat,1), size(dat,2), size(dat,3)); if Ntrials == 1 dummy = 0; [dat, dummy] = shiftdim(dat); @@ -364,39 +365,39 @@ end dat1 = cat(2, data.trial{sel1}); dat2 = cat(2, data.trial{sel2}); - fprintf('concatenated data matrix size for class 1 is %dx%d\n', size(dat1,1), size(dat1,2)); - fprintf('concatenated data matrix size for class 2 is %dx%d\n', size(dat2,1), size(dat2,2)); + ft_info('concatenated data matrix size for class 1 is %dx%d\n', size(dat1,1), size(dat1,2)); + ft_info('concatenated data matrix size for class 2 is %dx%d\n', size(dat2,1), size(dat2,2)); elseif ~strcmp(cfg.method, 'predetermined unmixing matrix') && strcmp(cfg.cellmode, 'no') % concatenate all the data into a 2D matrix unless we already have an % unmixing matrix or unless the user request it otherwise - fprintf('concatenating data'); + ft_info('concatenating data'); dat = zeros(Nchans, sum(Nsamples)); for trial=1:Ntrials - fprintf('.'); + ft_info('.'); begsample = sum(Nsamples(1:(trial-1))) + 1; endsample = sum(Nsamples(1:trial)); dat(:,begsample:endsample) = data.trial{trial}; end - fprintf('\n'); - fprintf('concatenated data matrix size %dx%d\n', size(dat,1), size(dat,2)); + ft_info('\n'); + ft_info('concatenated data matrix size %dx%d\n', size(dat,1), size(dat,2)); hasdatanans = any(~isfinite(dat(:))); if hasdatanans - fprintf('data contains nans, only using the non-nan samples\n'); + ft_info('data contains nans, only using the non-nan samples\n'); finitevals = sum(~isfinite(dat))==0; dat = dat(:,finitevals); end else - fprintf('not concatenating data\n'); + ft_info('not concatenating data\n'); dat = data.trial; % FIXME cellmode processing is not nan-transparent yet end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % perform the component analysis -fprintf('starting decomposition using %s\n', cfg.method); +ft_info('starting decomposition using %s\n', cfg.method); switch cfg.method case 'icasso' @@ -437,7 +438,7 @@ % do the rest of the icasso related processing sR = icassoCluster(sR, 'strategy', 'AL', 'simfcn', 'abscorr', 's2d', 'sim2dis', 'L',cfg.numcomponent); sR = icassoProjection(sR, 'cca', 's2d', 'sqrtsim2dis', 'epochs', 75); - [Iq, mixing, unmixing, ~, index2centrotypes] = icassoResult(sR,cfg.numcomponent); + [Iq, mixing, unmixing, dum, index2centrotypes] = icassoResult(sR,cfg.numcomponent); % this step is done, because in icassoResult mixing is determined to be % pinv(unmixing), which yields strange results. Better take it from the @@ -482,7 +483,7 @@ % the "catch me" syntax is broken on MATLAB74, this fixes it me = lasterror; % give a hopefully instructive error message - fprintf(['If you get an out-of-memory in fastica here, and you use fastica 2.5, change fastica.m, line 482: \n' ... + ft_info(['If you get an out-of-memory in fastica here, and you use fastica 2.5, change fastica.m, line 482: \n' ... 'from\n' ... ' if ~isempty(W) %% ORIGINAL VERSION\n' ... 'to\n' ... @@ -645,6 +646,7 @@ params = struct(cfg.dss); params.denf.h = str2func(cfg.dss.denf.function); + params.preprocf.h = str2func(cfg.dss.preprocf.function); if ~ischar(cfg.numcomponent) params.sdim = cfg.numcomponent; end @@ -669,12 +671,10 @@ % start the decomposition % state = dss(state); % this is for the DSS toolbox version 0.6 beta state = denss(state); % this is for the DSS toolbox version 1.0 - % weights = state.W; - % sphere = state.V; mixing = state.A; unmixing = state.B; - + % remember the updated configuration details cfg.dss.denf = state.denf; cfg.dss.orthof = state.orthof; @@ -683,7 +683,8 @@ cfg.dss.W = state.W; cfg.dss.V = state.V; cfg.dss.dV = state.dV; - cfg.numcomponent = state.sdim; + if isfield(state, 'D'), cfg.dss.D = state.D(1:min([state.sdim size(state.dV)])); end + cfg.numcomponent = min([state.sdim size(state.dV)]); case 'sobi' % check whether the required low-level toolboxes are installed @@ -857,7 +858,7 @@ % apply the linear projection also to the sensor description if ~isempty(sensfield) if strcmp(cfg.updatesens, 'yes') - fprintf('also applying the unmixing matrix to the %s structure\n', sensfield); + ft_info('also applying the unmixing matrix to the %s structure\n', sensfield); % construct a montage and apply it to the sensor description montage = []; montage.labelold = data.label; @@ -872,7 +873,7 @@ comp.(sensfield) = rmfield(comp.(sensfield), 'type'); end else - fprintf('not applying the unmixing matrix to the %s structure\n', sensfield); + ft_info('not applying the unmixing matrix to the %s structure\n', sensfield); % simply copy it over comp.(sensfield) = data.(sensfield); end diff --git a/external/fieldtrip/ft_connectivityanalysis.m b/external/fieldtrip/ft_connectivityanalysis.m index a6b49add..510f9f82 100644 --- a/external/fieldtrip/ft_connectivityanalysis.m +++ b/external/fieldtrip/ft_connectivityanalysis.m @@ -529,7 +529,7 @@ clear sumdat; end hasjack = 1; -elseif hasrpt && ~(exist('debiaswpli', 'var') || exist('weightppc', 'var') || strcmp(cfg.method, 'powcorr_ortho'))% || needrpt) +elseif hasrpt && ~(exist('debiaswpli', 'var') || exist('weightppc', 'var') || any(strcmp({'powcorr_ortho';'mi'},cfg.method)))% || needrpt) % create dof variable if isfield(data, 'dof') dof = data.dof; @@ -711,12 +711,10 @@ tok = tokenize(data.labelcmb{m}, '['); tmp2{m} = tok{1}; end - label = unique(tmp2); + label = cat(1,data.block.label);%unique(tmp2); - [cmbindx, n] = blockindx2cmbindx(data.labelcmb, {label data.blockindx}, tmp); - powindx.cmbindx = cmbindx; - powindx.n = n; - data.labelcmb = newlabelcmb; + [powindx.cmbindx, powindx.n] = blockindx2cmbindx(data.labelcmb, {label data.blockindx}, tmp); + data.labelcmb = newlabelcmb; if isfield(data, 'label') % this field should be removed diff --git a/external/fieldtrip/ft_connectivitysimulation.m b/external/fieldtrip/ft_connectivitysimulation.m index 7dfedfd5..3e261a3c 100644 --- a/external/fieldtrip/ft_connectivitysimulation.m +++ b/external/fieldtrip/ft_connectivitysimulation.m @@ -620,11 +620,11 @@ switch numel(Fbp) case 1 - [~, B, ~] = ft_preproc_lowpassfilter(randn(1,N), Fs, Fbp, [], 'firws', 'onepass-minphase'); + [dum, B] = ft_preproc_lowpassfilter(randn(1,N), Fs, Fbp, [], 'firws', 'onepass-minphase'); z = fft(B, N); case 2 - [~, B, ~] = ft_preproc_bandpassfilter(randn(1,N), Fs, Fbp, [], 'firws', 'onepass-minphase'); + [dum, B] = ft_preproc_bandpassfilter(randn(1,N), Fs, Fbp, [], 'firws', 'onepass-minphase'); z = fft(B, N); end diff --git a/external/fieldtrip/ft_crossfrequencyanalysis.m b/external/fieldtrip/ft_crossfrequencyanalysis.m index fe2285cf..74c58b49 100644 --- a/external/fieldtrip/ft_crossfrequencyanalysis.m +++ b/external/fieldtrip/ft_crossfrequencyanalysis.m @@ -86,16 +86,6 @@ % but nevertheless did not support between-channel CFC computations cfg = ft_checkconfig(cfg, 'forbidden', {'chanlow', 'chanhigh'}); -% this function only support CFC computations within channels, not between channels -if isfield(cfg, 'chanlow') && isfield(cfg, 'chanhigh') - if isequal(cfg.chanlow, cfg.chanhigh) - cfg.channel = cfg.chanlow; - cfg = removefields(cfg, 'chanlow', 'chanhigh'); - else - ft_error('cross-channel CFC not supported, the channel selection should be the same for low and high frequencies') - end -end - cfg.channel = ft_getopt(cfg, 'channel', 'all'); cfg.freqlow = ft_getopt(cfg, 'freqlow', 'all'); cfg.freqhigh = ft_getopt(cfg, 'freqhigh', 'all'); @@ -110,7 +100,7 @@ tmpcfg.frequency = cfg.freqlow; freqlow = ft_selectdata(tmpcfg, freqlow); [tmpcfg, freqlow] = rollback_provenance(cfg, freqlow); -try, cfg.chanlow = tmpcfg.channel; end +try, cfg.channel = tmpcfg.channel; end try, cfg.freqlow = tmpcfg.frequency; end % make selection of frequencies and channels @@ -119,7 +109,7 @@ tmpcfg.frequency = cfg.freqhigh; freqhigh = ft_selectdata(tmpcfg, freqhigh); [tmpcfg, freqhigh] = rollback_provenance(cfg, freqhigh); -try, cfg.chanhigh = tmpcfg.channel; end +try, cfg.channel = tmpcfg.channel; end try, cfg.freqhigh = tmpcfg.frequency; end LF = freqlow.freq; @@ -235,7 +225,7 @@ for j=1:nhf P = squeeze(pac(i,j,:))/ nansum(pac(i,j,:)); % normalized distribution % KL distance - mi(i,j) = nansum(P.* (log(P)-log2(Q)))/log(nbin); + mi(i,j) = nansum(P.* log2(P./Q))./log2(nbin); end end crsspctrm(k,n,:,:) = mi; @@ -257,7 +247,7 @@ for j=1:nhf P = squeeze(pac(i,j,:))/ nansum(pac(i,j,:)); % normalized distribution % KL distance - mi(i,j) = nansum(P.* (log(P)-log2(Q)))/log(nbin); + mi(i,j) = nansum(P.* log2(P./Q))./log2(nbin); end end crsspctrm(k,:,:) = mi; @@ -267,7 +257,7 @@ end % switch method for actual computation -crossfreq.label = cfg.chanlow; +crossfreq.label = cfg.channel; crossfreq.crsspctrm = crsspctrm; crossfreq.dimord = dimord; crossfreq.freqlow = LF; @@ -362,7 +352,7 @@ Ang = angle(LFsigtemp); Amp = abs(HFsigtemp); -[~,bin] = histc(Ang, linspace(-pi,pi,nbin)); % binned low frequency phase +[dum,bin] = histc(Ang, linspace(-pi,pi,nbin)); % binned low frequency phase binamp = zeros (size(HFsigtemp,1),nbin); % binned amplitude for i = 1:size(Ang,1) diff --git a/external/fieldtrip/ft_databrowser.m b/external/fieldtrip/ft_databrowser.m index 5a9f535d..8e68bb95 100644 --- a/external/fieldtrip/ft_databrowser.m +++ b/external/fieldtrip/ft_databrowser.m @@ -26,33 +26,25 @@ % cfg.zlim = color scaling to apply to component topographies, 'minmax', 'maxabs' (default = 'maxmin') % cfg.blocksize = duration in seconds for cutting the data up % cfg.trl = structure that defines the data segments of interest, only applicable for trial-based data -% cfg.continuous = 'yes' or 'no' whether the data should be interpreted as continuous or trial-based +% cfg.continuous = 'yes' or 'no', whether the data should be interpreted as continuous or trial-based +% cfg.allowoverlap = 'yes' or 'no', whether data that is overlapping in multiple trials is allowed (default = 'no') % cfg.channel = cell-array with channel labels, see FT_CHANNELSELECTION -% cfg.channelclamped = cell-array with channel labels, that (when using the 'vertical' viewmode) will always be -% shown at the bottom. This is useful for showing ECG/EOG channels along with the other channels -% cfg.plotlabels = 'yes' (default), 'no', 'some'; whether to plot channel labels in vertical -% viewmode ('some' plots one in every ten labels; useful when plotting a -% large number of channels at a time) +% cfg.channelclamped = cell-array with channel labels, that (when using the 'vertical' viewmode) will always be shown at the bottom. This is useful for showing ECG/EOG channels along with the other channels +% cfg.plotlabels = 'yes', 'no' or 'some', whether to plot channel labels in vertical viewmode. The option 'some' plots one label for every ten channels, which is useful if there are many channels. (default = 'yes') % cfg.ploteventlabels = 'type=value', 'colorvalue' (default = 'type=value'); % cfg.plotevents = 'no' or 'yes', whether to plot event markers. (default is 'yes') % cfg.viewmode = string, 'butterfly', 'vertical', 'component' for visualizing ICA/PCA components (default is 'butterfly') % cfg.artfctdef.xxx.artifact = Nx2 matrix with artifact segments see FT_ARTIFACT_xxx functions % cfg.selectfeature = string, name of feature to be selected/added (default = 'visual') % cfg.selectmode = 'markartifact', 'markpeakevent', 'marktroughevent' (default = 'markartifact') -% cfg.colorgroups = 'sequential' 'allblack' 'labelcharx' (x = xth character in label), 'chantype' or -% vector with length(data/hdr.label) defining groups (default = 'sequential') +% cfg.colorgroups = 'sequential' 'allblack' 'labelcharx' (x = xth character in label), 'chantype' or vector with length(data/hdr.label) defining groups (default = 'sequential') % cfg.channelcolormap = COLORMAP (default = customized lines map with 15 colors) -% cfg.verticalpadding = number or 'auto', padding to be added to top and bottom of plot to avoid channels largely -% dissappearing when viewmode = 'vertical'/'component' (default = 'auto'). The padding is -% expressed as a proportion of the total height added to the top and bottom. The setting 'auto' -% determines the padding depending on the number of channels that are being plotted. -% cfg.selfun = string, name of function that is evaluated using the right-click context menu. The selected -% data and cfg.selcfg are passed on to this function. +% cfg.verticalpadding = number or 'auto', padding to be added to top and bottom of plot to avoid channels largely dissappearing when viewmode = 'vertical'/'component' (default = 'auto'). The padding is expressed as a proportion of the total height added to the top and bottom. The setting 'auto' determines the padding depending on the number of channels that are being plotted. +% cfg.selfun = string, name of function that is evaluated using the right-click context menu. The selected data and cfg.selcfg are passed on to this function. % cfg.selcfg = configuration options for function in cfg.selfun -% cfg.seldat = 'selected' or 'all', specifies whether only the currently selected or all channels will -% be passed to the selfun (default = 'selected') -% cfg.renderer = string, 'opengl', 'zbuffer', 'painters', see MATLAB Figure Properties. If the databrowser -% crashes, you should try 'painters'. +% cfg.seldat = 'selected' or 'all', specifies whether only the currently selected or all channels will be passed to the selfun (default = 'selected') +% cfg.renderer = string, 'opengl', 'zbuffer', 'painters', see MATLAB Figure Properties. If this function crashes, you should try 'painters'. +% cfg.position = location and size of the figure, specified as a vector of the form [left bottom width height]. % % The following options for the scaling of the EEG, EOG, ECG, EMG and MEG channels is % optional and can be used to bring the absolute numbers of the different channel @@ -68,8 +60,7 @@ % cfg.mychanscale = number, scaling to apply to the channels specified in cfg.mychan % cfg.mychan = Nx1 cell-array with selection of channels % cfg.chanscale = Nx1 vector with scaling factors, one per channel specified in cfg.channel -% cfg.compscale = string, 'global' or 'local', defines whether the colormap for the topographic scaling is -% applied per topography or on all visualized components (default 'global') +% cfg.compscale = string, 'global' or 'local', defines whether the colormap for the topographic scaling is applied per topography or on all visualized components (default 'global') % % You can specify preprocessing options that are to be applied to the data prior to % display. Most options from FT_PREPROCESSING are supported. They should be specified @@ -108,10 +99,11 @@ % return the channel with the amplitude closest to the point you have clicked at the % specific time point. This might be counterintuitive at first. % -% The "cfg.artifact" field in the output cfg is a Nx2 matrix comparable to the -% "cfg.trl" matrix of FT_DEFINETRIAL. The first column of which specifying the -% beginsamples of an artifact period, the second column contains the endsamples of -% the artifactperiods. +% The "cfg.artfctdef" structure in the output cfg is comparable to the configuration +% used by the artifact detection functions like FT_ARTIFACT_ZVALUE and in +% FT_REJECTARTIFACT. It contains for each artifact type an Nx2 matrix in which the +% first column corresponds to the begin samples of an artifact period, the second +% column contains the end samples of the artifact periods. % % Note for debugging: in case the databrowser crashes, use delete(gcf) to kill the % figure. @@ -187,7 +179,7 @@ cfg.selcfg = ft_getopt(cfg, 'selcfg'); % defaulting done below, requires layouts/etc to be processed cfg.seldat = ft_getopt(cfg, 'seldat', 'current'); cfg.colorgroups = ft_getopt(cfg, 'colorgroups', 'sequential'); -cfg.channelcolormap = ft_getopt(cfg, 'channelcolormap', [0.75 0 0;0 0 1;0 1 0;0.44 0.19 0.63;0 0.13 0.38;0.5 0.5 0.5;1 0.75 0;1 0 0;0.89 0.42 0.04;0.85 0.59 0.58;0.57 0.82 0.31;0 0.69 0.94;1 0 0.4;0 0.69 0.31;0 0.44 0.75]); +cfg.channelcolormap = ft_getopt(cfg, 'channelcolormap', [0.75 0 0; 0 0 1; 0 1 0; 0.44 0.19 0.63; 0 0.13 0.38;0.5 0.5 0.5;1 0.75 0; 1 0 0; 0.89 0.42 0.04; 0.85 0.59 0.58; 0.57 0.82 0.31; 0 0.69 0.94; 1 0 0.4; 0 0.69 0.31; 0 0.44 0.75]); cfg.eegscale = ft_getopt(cfg, 'eegscale'); cfg.eogscale = ft_getopt(cfg, 'eogscale'); cfg.ecgscale = ft_getopt(cfg, 'ecgscale'); @@ -197,6 +189,7 @@ cfg.gradscale = ft_getopt(cfg, 'gradscale'); cfg.chanscale = ft_getopt(cfg, 'chanscale'); cfg.mychanscale = ft_getopt(cfg, 'mychanscale'); +cfg.mychan = ft_getopt(cfg, 'mychan'); cfg.layout = ft_getopt(cfg, 'layout'); cfg.plotlabels = ft_getopt(cfg, 'plotlabels', 'some'); cfg.event = ft_getopt(cfg, 'event'); % this only exists for backward compatibility and should not be documented @@ -208,13 +201,15 @@ cfg.compscale = ft_getopt(cfg, 'compscale', 'global'); cfg.renderer = ft_getopt(cfg, 'renderer'); cfg.fontsize = ft_getopt(cfg, 'fontsize', 12); -cfg.fontunits = ft_getopt(cfg, 'fontunits', 'points'); % inches, centimeters, normalized, points, pixels +cfg.fontunits = ft_getopt(cfg, 'fontunits', 'points'); % inches, centimeters, normalized, points, pixels cfg.editfontsize = ft_getopt(cfg, 'editfontsize', 12); cfg.editfontunits = ft_getopt(cfg, 'editfontunits', 'points'); % inches, centimeters, normalized, points, pixels cfg.axisfontsize = ft_getopt(cfg, 'axisfontsize', 10); cfg.axisfontunits = ft_getopt(cfg, 'axisfontunits', 'points'); % inches, centimeters, normalized, points, pixels cfg.linewidth = ft_getopt(cfg, 'linewidth', 0.5); cfg.verticalpadding = ft_getopt(cfg, 'verticalpadding', 'auto'); +cfg.artifactalpha = ft_getopt(cfg, 'artifactalpha', 0.2); % for the opacity of marked artifacts +cfg.allowoverlap = ft_getopt(cfg, 'allowoverlap', 'no'); % for ft_fetch_data if ~isfield(cfg, 'viewmode') % butterfly, vertical, component @@ -256,21 +251,15 @@ end end - if strcmp(cfg.viewmode, 'component') % read or create the layout that will be used for the topoplots if ~isempty(cfg.layout) - tmpcfg = []; - tmpcfg.layout = cfg.layout; + tmpcfg = keepfields(cfg, {'layout', 'showcallinfo'}); cfg.layout = ft_prepare_layout(tmpcfg); else ft_warning('No layout specified - will try to construct one using sensor positions'); - tmpcfg = []; - try, tmpcfg.elec = cfg.elec; end - try, tmpcfg.grad = cfg.grad; end - try, tmpcfg.elecfile = cfg.elecfile; end - try, tmpcfg.gradfile = cfg.gradfile; end + tmpcfg = keepfields(cfg, {'elec','grad','elecfile','gradfile','showcallinfo'}); if hasdata cfg.layout = ft_prepare_layout(tmpcfg, data); else @@ -382,12 +371,17 @@ end end end % if hasdata + if strcmp(cfg.continuous, 'no') && isempty(cfg.blocksize) cfg.blocksize = (trlorg(1,2) - trlorg(1,1)+1) ./ hdr.Fs; elseif strcmp(cfg.continuous, 'yes') && isempty(cfg.blocksize) cfg.blocksize = 1; end +if cfg.blocksize', 'userdata', ['control+' num2str(iArt)], 'position', [0.96, 0.855 - ((iArt-1)*0.09), 0.03, 0.04], 'backgroundcolor', opt.artcolors(iArt,:)) + uicontrol('tag', 'artifactui', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', artlabel{iArt}, 'userdata', num2str(iArt), 'position', [0.91, 0.9 - ((iArt-1)*0.09), 0.08, 0.04], 'backgroundcolor', opt.artifactcolors(iArt,:)) + uicontrol('tag', 'artifactui', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', ['shift+' num2str(iArt)], 'position', [0.91, 0.855 - ((iArt-1)*0.09), 0.03, 0.04], 'backgroundcolor', opt.artifactcolors(iArt,:)) + uicontrol('tag', 'artifactui', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', ['control+' num2str(iArt)], 'position', [0.96, 0.855 - ((iArt-1)*0.09), 0.03, 0.04], 'backgroundcolor', opt.artifactcolors(iArt,:)) end if length(artlabel)>1 % highlight the first one as active arth = findobj(h, 'tag', 'artifactui'); @@ -830,14 +828,19 @@ % add the update event to the output cfg cfg.event = opt.event; - % do the general cleanup and bookkeeping at the end of the function - ft_postamble debug - ft_postamble trackconfig - ft_postamble previous data - ft_postamble provenance - end % if nargout +% do the general cleanup and bookkeeping at the end of the function +ft_postamble debug +ft_postamble trackconfig +ft_postamble previous data +ft_postamble provenance +ft_postamble hastoolbox + +if ~nargout + clear cfg +end + end % main function @@ -1062,14 +1065,14 @@ function select_range_cb(h, range, cmenulab) %range 1X4 in sec relative to curre fprintf('there is no overlap with any event, adding an event to the peak/trough value\n'); % check if only 1 chan, other wise not clear max in which channel. % % ingnie: would be cool to add the option to select the channel when multiple channels - if size(opt.curdata.trial{1},1) > 1 + if size(dat,1) > 1 ft_error('cfg.selectmode = ''markpeakevent'' and ''marktroughevent'' only supported with 1 channel in the data') end if strcmp(cfg.selectmode, 'markpeakevent') - [dum ind_minmax] = max(opt.curdata.trial{1}(begsel-begsample+1:endsel-begsample+1)); + [dum ind_minmax] = max(dat(begsel-begsample+1:endsel-begsample+1)); val = 'peak'; elseif strcmp(cfg.selectmode, 'marktroughevent') - [dum ind_minmax] = min(opt.curdata.trial{1}(begsel-begsample+1:endsel-begsample+1)); + [dum ind_minmax] = min(dat(begsel-begsample+1:endsel-begsample+1)); val = 'trough'; end samp_minmax = begsel + ind_minmax - 1; @@ -1123,7 +1126,7 @@ function select_range_cb(h, range, cmenulab) %range 1X4 in sec relative to curre end seldata.time{1} = offset2time(offset+begsel-begsample, opt.fsample, endsel-begsel+1); seldata.fsample = opt.fsample; - seldata.sampleinfo = [begsel endsel offset]; + seldata.sampleinfo = [begsel endsel]; % prepare input funhandle = ft_getuserfun(cmenulab, 'browse'); @@ -1252,8 +1255,9 @@ function keyboard_cb(h, eventdata) key = get(h, 'userdata'); else % determine the key that was pressed on the keyboard - key = parseKeyboardEvent(eventdata); + key = parsekeyboardevent(eventdata); end + % get focus back to figure if ~strcmp(get(h, 'type'), 'figure') set(h, 'enable', 'off'); @@ -1469,12 +1473,12 @@ function keyboard_cb(h, eventdata) response = inputdlg('vertical scale, [ymin ymax], ''maxabs'' or ''maxmin''', 'specify', 1, {['[ ' num2str(cfg.ylim) ' ]']}); if ~isempty(response) if strcmp(response, 'maxmin') - minval = min(opt.curdata.trial{1}(:)); - maxval = max(opt.curdata.trial{1}(:)); + minval = min(dat(:)); + maxval = max(dat(:)); cfg.ylim = [minval maxval]; elseif strcmp(response, 'maxabs') - minval = min(opt.curdata.trial{1}(:)); - maxval = max(opt.curdata.trial{1}(:)); + minval = min(dat(:)); + maxval = max(dat(:)); cfg.ylim = [-max(abs([minval maxval])) max(abs([minval maxval]))]; else % convert to string and add brackets, just to ensure that str2num will work @@ -1562,7 +1566,7 @@ function keyboard_cb(h, eventdata) elseif curstate == 3 cfg.selectmode = 'markartifact'; end - fprintf('switching to selectmode = %s\n',cfg.selectmode); + fprintf('switching to selectmode = %s\n', cfg.selectmode); setappdata(h, 'opt', opt); setappdata(h, 'cfg', cfg); redraw_cb(h, eventdata); @@ -1648,7 +1652,6 @@ function redraw_cb(h, eventdata) opt.changedchanflg = false; end - if ~isempty(opt.event) && isstruct(opt.event) % select only the events in the current time window event = opt.event; @@ -1659,9 +1662,9 @@ function redraw_cb(h, eventdata) end if isempty(opt.orgdata) - dat = ft_read_data(cfg.datafile, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'checkboundary', strcmp(cfg.continuous, 'no'), 'dataformat', cfg.dataformat, 'headerformat', cfg.headerformat); + dat = ft_read_data(cfg.datafile, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'checkboundary', ~istrue(cfg.continuous), 'dataformat', cfg.dataformat, 'headerformat', cfg.headerformat); else - dat = ft_fetch_data(opt.orgdata, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'allowoverlap', true); % ALLOWING OVERLAPPING TRIALS + dat = ft_fetch_data(opt.orgdata, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'allowoverlap', istrue(cfg.allowoverlap)); end art = ft_fetch_data(opt.artdata, 'begsample', begsample, 'endsample', endsample); @@ -1680,57 +1683,32 @@ function redraw_cb(h, eventdata) tim = [tim linspace(tim(end),tim(end)+nsamplepad*mean(diff(tim)),nsamplepad)]; % possible machine precision error here end -% apply scaling to selected channels -% using wildcard to support subselection of channels -if ~isempty(cfg.eegscale) - chansel = match_str(lab, ft_channelselection('EEG', lab)); - dat(chansel,:) = dat(chansel,:) .* cfg.eegscale; -end -if ~isempty(cfg.eogscale) - chansel = match_str(lab, ft_channelselection('EOG', lab)); - dat(chansel,:) = dat(chansel,:) .* cfg.eogscale; -end -if ~isempty(cfg.ecgscale) - chansel = match_str(lab, ft_channelselection('ECG', lab)); - dat(chansel,:) = dat(chansel,:) .* cfg.ecgscale; -end -if ~isempty(cfg.emgscale) - chansel = match_str(lab, ft_channelselection('EMG', lab)); - dat(chansel,:) = dat(chansel,:) .* cfg.emgscale; -end -if ~isempty(cfg.megscale) - type = opt.hdr.grad.type; - chansel = match_str(lab, ft_channelselection('MEG', lab, type)); - dat(chansel,:) = dat(chansel,:) .* cfg.megscale; -end -if ~isempty(cfg.magscale) - chansel = match_str(lab, ft_channelselection('MEGMAG', lab)); - dat(chansel,:) = dat(chansel,:) .* cfg.magscale; -end -if ~isempty(cfg.gradscale) - chansel = match_str(lab, ft_channelselection('MEGGRAD', lab)); - dat(chansel,:) = dat(chansel,:) .* cfg.gradscale; -end -if ~isempty(cfg.chanscale) - chansel = match_str(lab, ft_channelselection(cfg.channel, lab)); - dat(chansel,:) = dat(chansel,:) .* repmat(cfg.chanscale,1,size(dat,2)); -end -if ~isempty(cfg.mychanscale) - chansel = match_str(lab, ft_channelselection(cfg.mychan, lab)); - dat(chansel,:) = dat(chansel,:) .* cfg.mychanscale; -end - +% make a single-trial data structure for the current data opt.curdata.label = lab; opt.curdata.time{1} = tim; opt.curdata.trial{1} = dat; opt.curdata.hdr = opt.hdr; opt.curdata.fsample = opt.fsample; -opt.curdata.sampleinfo = [begsample endsample offset]; +opt.curdata.sampleinfo = [begsample endsample]; +% remove the local copy of the data fields +clear lab tim dat + +fn = fieldnames(cfg); +tmpcfg = keepfields(cfg, fn(contains(fn, 'scale') | contains(fn, 'mychan'))); +tmpcfg.parameter = 'trial'; +opt.curdata = chanscale_common(tmpcfg, opt.curdata); + +% make a local copy (again) of the data fields +lab = opt.curdata.label; +tim = opt.curdata.time{1}; +dat = opt.curdata.trial{1}; % to assure current feature is plotted on top ordervec = 1:length(opt.artdata.label); -ordervec(opt.ftsel) = []; -ordervec(end+1) = opt.ftsel; +if numel(opt.ftsel)==1 + ordervec(opt.ftsel) = []; + ordervec(end+1) = opt.ftsel; +end % FIXME speedup ft_prepare_layout if strcmp(cfg.viewmode, 'butterfly') @@ -1813,7 +1791,7 @@ function redraw_cb(h, eventdata) for k=1:numel(artbeg) xpos = [tim(artbeg(k)) tim(artend(k))] + ([-.5 +.5]./opt.fsample); - h_artifact = ft_plot_box([xpos -1 1], 'facecolor', opt.artcolors(j,:), 'facealpha', .2, 'edgecolor', 'none', 'tag', 'artifact', 'hpos', opt.hpos, 'vpos', opt.vpos, 'width', opt.width, 'height', opt.height, 'hlim', opt.hlim, 'vlim', [-1 1]); + ft_plot_box([xpos -1 1], 'facecolor', opt.artifactcolors(j,:), 'facealpha', cfg.artifactalpha, 'edgecolor', 'none', 'tag', 'artifact', 'hpos', opt.hpos, 'vpos', opt.vpos, 'width', opt.width, 'height', opt.height, 'hlim', opt.hlim, 'vlim', [-1 1]); end end % for each of the artifact channels @@ -2169,36 +2147,6 @@ function redraw_cb(h, eventdata) setappdata(h, 'cfg', cfg); end % function redraw_cb -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; - -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character; - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end - -end % function parseKeyboardEvent - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/ft_defacemesh.m b/external/fieldtrip/ft_defacemesh.m index 47444519..6dec960e 100644 --- a/external/fieldtrip/ft_defacemesh.m +++ b/external/fieldtrip/ft_defacemesh.m @@ -56,13 +56,13 @@ end % the actual work is done by FT_DEFACEVOLUME -previous = cfg.showcallinfo; tmpcfg = cfg; tmpcfg.showcallinfo = 'no'; mesh = ft_defacevolume(tmpcfg, mesh); -% restore provenance information +% restore provenance information and put back cfg.callinfo +tmpcallinfo = cfg.showcallinfo; [cfg, mesh] = rollback_provenance(cfg, mesh); -cfg.showcallinfo = previous; +cfg.showcallinfo = tmpcallinfo; % do the general cleanup and bookkeeping at the end of the function ft_postamble debug diff --git a/external/fieldtrip/ft_defacevolume.m b/external/fieldtrip/ft_defacevolume.m index c4dcc06d..9e150f7b 100644 --- a/external/fieldtrip/ft_defacevolume.m +++ b/external/fieldtrip/ft_defacevolume.m @@ -11,6 +11,9 @@ % mri = ft_defacevolume(cfg, mri) % % The configuration can contain the following options +% cfg.method = 'box', 'spm' (default = 'box') +% +% If you specify the box method, the following options apply % cfg.translate = initial position of the center of the box (default = [0 0 0]) % cfg.scale = initial size of the box along each dimension (default is automatic) % cfg.translate = initial rotation of the box (default = [0 0 0]) @@ -23,9 +26,12 @@ % specify a certain amount of smoothing (in voxels FWHM), the selected area will % be replaced by a smoothed version of the data. % +% The spm method does not have any options, it uses SPM_DEFACE from the +% SPM12 toolbox. +% % See also FT_ANONIMIZEDATA, FT_DEFACEMESH, FT_ANALYSISPIPELINE, FT_SOURCEPLOT -% Copyright (C) 2015-2016, Robert Oostenveld +% Copyright (C) 2015-2018, Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -64,6 +70,7 @@ end % set the defaults +cfg.method = ft_getopt(cfg, 'method', 'box'); cfg.rotate = ft_getopt(cfg, 'rotate', [0 0 0]); cfg.scale = ft_getopt(cfg, 'scale'); % the automatic default is determined further down cfg.translate = ft_getopt(cfg, 'translate', [0 0 0]); @@ -80,216 +87,255 @@ mri = ft_checkdata(mri, 'datatype', 'volume', 'feedback', 'yes'); end -% determine the size of the "unit" sphere in the origin and the length of the axes -switch mri.unit - case 'mm' - axmax = 150; - rbol = 5; - case 'cm' - axmax = 15; - rbol = 0.5; - case 'm' - axmax = 0.15; - rbol = 0.005; - otherwise - ft_error('unknown units (%s)', unit); -end - -figHandle = figure; -set(figHandle, 'CloseRequestFcn', @cb_close); - -% clear persistent variables to ensure fresh figure -clear ft_plot_slice - -if ismri - % the volumetric data needs to be interpolated onto three orthogonal planes - % determine a resolution that is close to, or identical to the original resolution - [corner_vox, corner_head] = cornerpoints(mri.dim, mri.transform); - diagonal_head = norm(range(corner_head)); - diagonal_vox = norm(range(corner_vox)); - resolution = diagonal_head/diagonal_vox; % this is in units of "mri.unit" - - % create a contrast enhanced version of the anatomy - mri.anatomy = double(mri.anatomy); - dum = unique(mri.anatomy(:)); - clim(1) = dum(round(0.05*numel(dum))); - clim(2) = dum(round(0.95*numel(dum))); - anatomy = (mri.anatomy-clim(1))/(clim(2)-clim(1)); - - ft_plot_ortho(anatomy, 'transform', mri.transform, 'unit', mri.unit, 'resolution', resolution, 'style', 'intersect'); -elseif ismesh - if isfield(mri, 'hex') - ft_plot_mesh(mri, 'surfaceonly', 'yes'); - else - ft_plot_mesh(mri); - end -end - -axis vis3d -view([110 36]); - -% shift the axes to the left -ax = get(gca, 'position'); -ax(1) = 0; -set(gca, 'position', ax); - -% get the xyz-axes -xdat = [-axmax 0 0; axmax 0 0]; -ydat = [0 -axmax 0; 0 axmax 0]; -zdat = [0 0 -axmax; 0 0 axmax]; - -% get the xyz-axes dotted -xdatdot = (-axmax:(axmax/15):axmax); -xdatdot = xdatdot(1:floor(numel(xdatdot)/2)*2); -xdatdot = reshape(xdatdot, [2 numel(xdatdot)/2]); -n = size(xdatdot,2); -ydatdot = [zeros(2,n) xdatdot zeros(2,n)]; -zdatdot = [zeros(2,2*n) xdatdot]; -xdatdot = [xdatdot zeros(2,2*n)]; - -% plot axes -hl = line(xdat, ydat, zdat); -set(hl(1), 'linewidth', 1, 'color', 'r'); -set(hl(2), 'linewidth', 1, 'color', 'g'); -set(hl(3), 'linewidth', 1, 'color', 'b'); -hld = line(xdatdot, ydatdot, zdatdot); -for k = 1:n - set(hld(k ), 'linewidth', 3, 'color', 'r'); - set(hld(k+n*1), 'linewidth', 3, 'color', 'g'); - set(hld(k+n*2), 'linewidth', 3, 'color', 'b'); -end - -if isempty(cfg.scale) - cfg.scale = [axmax axmax axmax]/2; -end - -guidata(figHandle, cfg); - -% add the GUI elements -cb_creategui(gca); -cb_redraw(gca); - -uiwait(figHandle); -cfg = guidata(figHandle); -delete(figHandle); -drawnow -fprintf('keeping all voxels from MRI that are %s the box\n', cfg.selection) - -% the order of application is scale, rotate, translate -S = cfg.S; -R = cfg.R; -T = cfg.T; - -if ismri - % it is possible to convert the box to headcoordinates, but it is more efficient the other way around - [X, Y, Z] = ndgrid(1:mri.dim(1), 1:mri.dim(2), 1:mri.dim(3)); - voxpos = ft_warp_apply(mri.transform, [X(:) Y(:) Z(:)]); % voxel positions in head coordinates - voxpos = ft_warp_apply(inv(T*R*S), voxpos); % voxel positions in box coordinates - - remove = ... - voxpos(:,1) > -0.5 & ... - voxpos(:,1) < +0.5 & ... - voxpos(:,2) > -0.5 & ... - voxpos(:,2) < +0.5 & ... - voxpos(:,3) > -0.5 & ... - voxpos(:,3) < +0.5; - -elseif ismesh || issource - meshpos = ft_warp_apply(inv(T*R*S), mri.pos); % mesh vertex positions in box coordinates - - remove = ... - meshpos(:,1) > -0.5 & ... - meshpos(:,1) < +0.5 & ... - meshpos(:,2) > -0.5 & ... - meshpos(:,2) < +0.5 & ... - meshpos(:,3) > -0.5 & ... - meshpos(:,3) < +0.5; -end - -if strcmp(cfg.selection, 'inside') - % invert the selection, i.e. keep the voxels inside the box - remove = ~remove; -end - -if ismri - if istrue(cfg.keepbrain) - tmpcfg = []; - tmpcfg.output = {'brain'}; - seg = ft_volumesegment(tmpcfg, mri); - fprintf('keeping voxels in brain segmentation\n'); - % keep the tissue of the brain - remove(seg.brain) = 0; - clear seg - end - - if istrue(cfg.feedback) - tmpmri = keepfields(mri, {'anatomy', 'transform', 'coordsys', 'units', 'dim'}); - tmpmri.remove = remove; - tmpcfg = []; - tmpcfg.funparameter = 'remove'; - ft_sourceplot(tmpcfg, tmpmri); - end - - if isequal(cfg.smooth, 'no') - fprintf('zero-filling %.0f%% of the volume\n', 100*mean(remove)); - mri.anatomy(remove) = 0; - else - tmp = mri.anatomy; - tmp = (1 + 0.5.*randn(size(tmp))).*tmp; % add 50% noise to each voxel - tmp = volumesmooth(tmp, cfg.smooth, 'anatomy'); - fprintf('smoothing %.0f%% of the volume\n', 100*mean(remove)); - mri.anatomy(remove) = tmp(remove); - end - -elseif ismesh - % determine all fields that might need to be defaced - fn = setdiff(fieldnames(mri), ignorefields('deface')); - dimord = cell(size(fn)); - for i=1:numel(fn) - dimord{i} = getdimord(mri, fn{i}); - end - % this applies to headshapes and meshes in general - fprintf('keeping %d and removing %d vertices in the mesh\n', sum(remove==0), sum(remove==1)); - if isfield(mri, 'tri') - [mri.pos, mri.tri] = remove_vertices(mri.pos, mri.tri, remove); - elseif isfield(mri, 'tet') - [mri.pos, mri.tet] = remove_vertices(mri.pos, mri.tet, remove); - elseif isfield(mri, 'hex') - [mri.pos, mri.hex] = remove_vertices(mri.pos, mri.hex, remove); - else - mri.pos = mri.pos(~remove,1:3); - end - for i=1:numel(fn) - dimtok = tokenize(dimord{i}, '_'); - % do some sanity checks - if any(strcmp(dimtok, '{pos}')) - ft_error('not supported'); +switch cfg.method + case 'spm' + % this requires SPM12 on the path + ft_hastoolbox('spm12', 1); + + % defacing relies on coregistration, which relies on the MRI being reasonably aligned for SPM + mri = ft_checkdata(mri, 'hascoordsys', 'yes'); + + % remember the original transformation matrix and coordinate system + original = []; + original.transform = mri.transform; + original.coordsys = mri.coordsys; + mri = ft_convert_coordsys(mri, 'acpc'); + + filename1 = {[tempname '.nii']}; + ft_write_mri(filename1{1}, mri, 'dataformat', 'nifti'); + + % % apply a least squares pre-alignment step in order to make spm_deface more robust + % % this could be done conditional on the modality/contrast, which is part of the BIDS filename + % template = spm_vol(fullfile(spm('Dir'),'canonical','avg152PD.nii')); + % template = spm_vol(fullfile(spm('Dir'),'canonical','avg152T1.nii')); + % template = spm_vol(fullfile(spm('Dir'),'canonical','avg152T2.nii')); + % filevol = spm_vol(filename1{1}); + % M = spm_affreg(template, filevol); + % spm_get_space(filename1{1}, M * filevol.mat); + + filename2 = spm_deface(filename1); + mri = ft_read_mri(filename2{1}); + + % put the original transformation matrix and coordinate system back + mri.transform = original.transform; + mri.coordsys = original.coordsys; + + % clean up the temporary files + delete(filename1{1}); + delete(filename2{1}); + + case 'box' + % determine the size of the "unit" sphere in the origin and the length of the axes + switch mri.unit + case 'mm' + axmax = 150; + rbol = 5; + case 'cm' + axmax = 15; + rbol = 0.5; + case 'm' + axmax = 0.15; + rbol = 0.005; + otherwise + ft_error('unknown units (%s)', unit); end - if numel(dimtok)>5 - ft_error('too many dimensions'); + + figHandle = figure; + set(figHandle, 'CloseRequestFcn', @cb_close); + + % clear persistent variables to ensure fresh figure + clear ft_plot_slice + + if ismri + % the volumetric data needs to be interpolated onto three orthogonal planes + % determine a resolution that is close to, or identical to the original resolution + [corner_vox, corner_head] = cornerpoints(mri.dim, mri.transform); + diagonal_head = norm(range(corner_head)); + diagonal_vox = norm(range(corner_vox)); + resolution = diagonal_head/diagonal_vox; % this is in units of "mri.unit" + + % create a contrast enhanced version of the anatomy + mri.anatomy = double(mri.anatomy); + dum = unique(mri.anatomy(:)); + clim(1) = dum(round(0.05*numel(dum))); + clim(2) = dum(round(0.95*numel(dum))); + anatomy = (mri.anatomy-clim(1))/(clim(2)-clim(1)); + + ft_plot_ortho(anatomy, 'transform', mri.transform, 'unit', mri.unit, 'resolution', resolution, 'style', 'intersect'); + elseif ismesh + if isfield(mri, 'hex') + ft_plot_mesh(mri, 'surfaceonly', 'yes'); + else + ft_plot_mesh(mri); + end end - % remove the same positions from each matching dimension - if numel(dimtok)>0 && strcmp(dimtok{1}, 'pos') - mri.(fn{i}) = mri.(fn{i})(~remove,:,:,:,:); + + axis vis3d + view([110 36]); + + % shift the axes to the left + ax = get(gca, 'position'); + ax(1) = 0; + set(gca, 'position', ax); + + % get the xyz-axes + xdat = [-axmax 0 0; axmax 0 0]; + ydat = [0 -axmax 0; 0 axmax 0]; + zdat = [0 0 -axmax; 0 0 axmax]; + + % get the xyz-axes dotted + xdatdot = (-axmax:(axmax/15):axmax); + xdatdot = xdatdot(1:floor(numel(xdatdot)/2)*2); + xdatdot = reshape(xdatdot, [2 numel(xdatdot)/2]); + n = size(xdatdot,2); + ydatdot = [zeros(2,n) xdatdot zeros(2,n)]; + zdatdot = [zeros(2,2*n) xdatdot]; + xdatdot = [xdatdot zeros(2,2*n)]; + + % plot axes + hl = line(xdat, ydat, zdat); + set(hl(1), 'linewidth', 1, 'color', 'r'); + set(hl(2), 'linewidth', 1, 'color', 'g'); + set(hl(3), 'linewidth', 1, 'color', 'b'); + hld = line(xdatdot, ydatdot, zdatdot); + for k = 1:n + set(hld(k ), 'linewidth', 3, 'color', 'r'); + set(hld(k+n*1), 'linewidth', 3, 'color', 'g'); + set(hld(k+n*2), 'linewidth', 3, 'color', 'b'); end - if numel(dimtok)>1 && strcmp(dimtok{2}, 'pos') - mri.(fn{i}) = mri.(fn{i})(:,~remove,:,:,:); + + if isempty(cfg.scale) + cfg.scale = [axmax axmax axmax]/2; end - if numel(dimtok)>2 && strcmp(dimtok{3}, 'pos') - mri.(fn{i}) = mri.(fn{i})(:,:,~remove,:,:); + + guidata(figHandle, cfg); + + % add the GUI elements + cb_creategui(gca); + cb_redraw(gca); + + uiwait(figHandle); + cfg = guidata(figHandle); + delete(figHandle); + drawnow + fprintf('keeping all voxels from MRI that are %s the box\n', cfg.selection) + + % the order of application is scale, rotate, translate + S = cfg.S; + R = cfg.R; + T = cfg.T; + + if ismri + % it is possible to convert the box to headcoordinates, but it is more efficient the other way around + [X, Y, Z] = ndgrid(1:mri.dim(1), 1:mri.dim(2), 1:mri.dim(3)); + voxpos = ft_warp_apply(mri.transform, [X(:) Y(:) Z(:)]); % voxel positions in head coordinates + voxpos = ft_warp_apply(inv(T*R*S), voxpos); % voxel positions in box coordinates + + remove = ... + voxpos(:,1) > -0.5 & ... + voxpos(:,1) < +0.5 & ... + voxpos(:,2) > -0.5 & ... + voxpos(:,2) < +0.5 & ... + voxpos(:,3) > -0.5 & ... + voxpos(:,3) < +0.5; + + elseif ismesh || issource + meshpos = ft_warp_apply(inv(T*R*S), mri.pos); % mesh vertex positions in box coordinates + + remove = ... + meshpos(:,1) > -0.5 & ... + meshpos(:,1) < +0.5 & ... + meshpos(:,2) > -0.5 & ... + meshpos(:,2) < +0.5 & ... + meshpos(:,3) > -0.5 & ... + meshpos(:,3) < +0.5; end - if numel(dimtok)>3 && strcmp(dimtok{4}, 'pos') - mri.(fn{i}) = mri.(fn{i})(:,:,:,~remove,:); + + if strcmp(cfg.selection, 'inside') + % invert the selection, i.e. keep the voxels inside the box + remove = ~remove; end - if numel(dimtok)>4 && strcmp(dimtok{5}, 'pos') - mri.(fn{i}) = mri.(fn{i})(:,:,:,:,~remove); - end - end % for fn - mri = removefields(mri, {'dim', 'transform'}); % these fields don't apply any more -end % ismesh - -% remove the temporary fields from the configuration, keep the rest for provenance -cfg = removefields(cfg, {'R', 'S', 'T'}); + + if ismri + if istrue(cfg.keepbrain) + tmpcfg = []; + tmpcfg.output = {'brain'}; + seg = ft_volumesegment(tmpcfg, mri); + fprintf('keeping voxels in brain segmentation\n'); + % keep the tissue of the brain + remove(seg.brain) = 0; + clear seg + end + + if istrue(cfg.feedback) + tmpmri = keepfields(mri, {'anatomy', 'transform', 'coordsys', 'units', 'dim'}); + tmpmri.remove = remove; + tmpcfg = []; + tmpcfg.funparameter = 'remove'; + ft_sourceplot(tmpcfg, tmpmri); + end + + if isequal(cfg.smooth, 'no') + fprintf('zero-filling %.0f%% of the volume\n', 100*mean(remove)); + mri.anatomy(remove) = 0; + else + tmp = mri.anatomy; + tmp = (1 + 0.5.*randn(size(tmp))).*tmp; % add 50% noise to each voxel + tmp = volumesmooth(tmp, cfg.smooth, 'anatomy'); + fprintf('smoothing %.0f%% of the volume\n', 100*mean(remove)); + mri.anatomy(remove) = tmp(remove); + end + + elseif ismesh + % determine all fields that might need to be defaced + fn = setdiff(fieldnames(mri), ignorefields('deface')); + dimord = cell(size(fn)); + for i=1:numel(fn) + dimord{i} = getdimord(mri, fn{i}); + end + % this applies to headshapes and meshes in general + fprintf('keeping %d and removing %d vertices in the mesh\n', sum(remove==0), sum(remove==1)); + if isfield(mri, 'tri') + [mri.pos, mri.tri] = remove_vertices(mri.pos, mri.tri, remove); + elseif isfield(mri, 'tet') + [mri.pos, mri.tet] = remove_vertices(mri.pos, mri.tet, remove); + elseif isfield(mri, 'hex') + [mri.pos, mri.hex] = remove_vertices(mri.pos, mri.hex, remove); + else + mri.pos = mri.pos(~remove,1:3); + end + for i=1:numel(fn) + dimtok = tokenize(dimord{i}, '_'); + % do some sanity checks + if any(strcmp(dimtok, '{pos}')) + ft_error('not supported'); + end + if numel(dimtok)>5 + ft_error('too many dimensions'); + end + % remove the same positions from each matching dimension + if numel(dimtok)>0 && strcmp(dimtok{1}, 'pos') + mri.(fn{i}) = mri.(fn{i})(~remove,:,:,:,:); + end + if numel(dimtok)>1 && strcmp(dimtok{2}, 'pos') + mri.(fn{i}) = mri.(fn{i})(:,~remove,:,:,:); + end + if numel(dimtok)>2 && strcmp(dimtok{3}, 'pos') + mri.(fn{i}) = mri.(fn{i})(:,:,~remove,:,:); + end + if numel(dimtok)>3 && strcmp(dimtok{4}, 'pos') + mri.(fn{i}) = mri.(fn{i})(:,:,:,~remove,:); + end + if numel(dimtok)>4 && strcmp(dimtok{5}, 'pos') + mri.(fn{i}) = mri.(fn{i})(:,:,:,:,~remove); + end + end % for fn + mri = removefields(mri, {'dim', 'transform'}); % these fields don't apply any more + end % ismesh + + % remove the temporary fields from the configuration, keep the rest for provenance + cfg = removefields(cfg, {'R', 'S', 'T'}); +end % do the general cleanup and bookkeeping at the end of the function ft_postamble debug diff --git a/external/fieldtrip/ft_defaults.m b/external/fieldtrip/ft_defaults.m index c3c1af36..e7516aa5 100644 --- a/external/fieldtrip/ft_defaults.m +++ b/external/fieldtrip/ft_defaults.m @@ -5,15 +5,16 @@ % call this function in your startup.m script. This function is also called at the % begin of all FieldTrip functions. % -% The configuration defaults are stored in the global "ft_default" structure. -% The ft_checkconfig function that is called by many FieldTrip functions will -% merge this global ft_default structure with the cfg ctructure that you pass to +% The global configuration defaults are stored in the global "ft_default" structure. +% The ft_checkconfig function that is called by many FieldTrip functions will merge +% these global configuration defaults with the cfg ctructure that you pass to % the FieldTrip function that you are calling. % % The global options and their default values are -% ft_default.showcallinfo = string, can be 'yes' or 'no' (default = 'yes') % ft_default.checkconfig = string, can be 'pedantic', 'loose', 'silent' (default = 'loose') +% ft_default.checkpath = string, can be 'pedantic', 'once', 'no' (default = 'pedantic') % ft_default.checksize = number in bytes, can be inf (default = 1e5) +% ft_default.showcallinfo = string, can be 'yes' or 'no' (default = 'yes') % ft_default.trackconfig = string, can be 'cleanup', 'report', 'off' (default = 'off') % ft_default.trackusage = false, or string with salt for one-way encryption of identifying information (by default this is enabled and an automatic salt is created) % ft_default.trackdatainfo = string, can be 'yes' or 'no' (default = 'no') @@ -24,6 +25,12 @@ % ft_default.toolbox.stats = string, can be 'compat' or 'matlab' (default = 'compat') % ft_default.toolbox.images = string, can be 'compat' or 'matlab' (default = 'compat') % +% If you want to overrule these default settings, you can add something like this in your startup.m script +% ft_defaults +% global ft_default +% ft_default.option1 = value1 +% ft_default.option2 = value2 +% % The toolbox option for signal, stats and images allows you to specify whether you % want to use a compatible drop-in to be used for these MathWorks toolboxes, or the % original version from MathWorks. The default is 'compat', which has the advantage @@ -34,7 +41,7 @@ % undocumented options % ft_default.siunits = 'yes' or 'no' -% Copyright (C) 2009-2016, Robert Oostenveld +% Copyright (C) 2009-2018, Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -55,18 +62,24 @@ % $Id$ global ft_default + persistent initialized +persistent checkpath if isempty(initialized) initialized = false; end +if isempty(checkpath) + checkpath = false; +end + % ft_warning is located in fieldtrip/utilities, which may not be on the path yet if ~exist('ft_warning', 'file') ft_warning = @warning; end -% locate the file that contains the persistent FieldTrip preferences +% locate the file with the persistent FieldTrip preferences fieldtripprefs = fullfile(prefdir, 'fieldtripprefs.mat'); if exist(fieldtripprefs, 'file') prefs = load(fieldtripprefs); % the file contains multiple fields @@ -77,11 +90,12 @@ % NOTE ft_getopt might not be available on the path at this moment and can therefore not yet be used. % NOTE all options here should be explicitly listed as allowed in ft_checkconfig -if ~isfield(ft_default, 'trackconfig'), ft_default.trackconfig = 'off'; end % cleanup, report, off -if ~isfield(ft_default, 'checkconfig'), ft_default.checkconfig = 'loose'; end % pedantic, loose, silent -if ~isfield(ft_default, 'checksize'), ft_default.checksize = 1e5; end % number in bytes, can be inf -if ~isfield(ft_default, 'showcallinfo'), ft_default.showcallinfo = 'yes'; end % yes or no, this is used in ft_pre/postamble_provenance -if ~isfield(ft_default, 'debug'), ft_default.debug = 'no'; end % no, save, saveonerror, display, displayonerror, this is used in ft_pre/postamble_debug +if ~isfield(ft_default, 'trackconfig'), ft_default.trackconfig = 'off'; end % cleanup, report, off +if ~isfield(ft_default, 'checkconfig'), ft_default.checkconfig = 'loose'; end % pedantic, loose, silent +if ~isfield(ft_default, 'checkpath'), ft_default.checkpath = 'pedantic'; end % pedantic, once, no +if ~isfield(ft_default, 'checksize'), ft_default.checksize = 1e5; end % number in bytes, can be inf +if ~isfield(ft_default, 'showcallinfo'), ft_default.showcallinfo = 'yes'; end % yes or no, this is used in ft_pre/postamble_provenance +if ~isfield(ft_default, 'debug'), ft_default.debug = 'no'; end % no, save, saveonerror, display, displayonerror, this is used in ft_pre/postamble_debug if ~isfield(ft_default, 'outputfilepresent'), ft_default.outputfilepresent = 'overwrite'; end % can be keep, overwrite, error % These options allow to disable parts of the provenance @@ -94,6 +108,22 @@ if ~isfield(ft_default.toolbox, 'stats') , ft_default.toolbox.stats = 'compat'; end % matlab or compat if ~isfield(ft_default.toolbox, 'signal'), ft_default.toolbox.signal = 'compat'; end % matlab or compat +% Some people mess up their path settings and then have stuff on the path that should not be there. +% The following will issue a warning +switch ft_default.checkpath + case 'pedantic' + % check every time + checkIncorrectPath(); + case 'once' + % check only once + if ~checkpath + checkIncorrectPath(); + checkpath = true; + end + case 'no' + % do not check +end % case + % Check whether this ft_defaults function has already been executed. Note that we % should not use ft_default itself directly, because the user might have set stuff % in that struct already prior to ft_defaults being called for the first time. @@ -101,6 +131,12 @@ return; end +if isfield(ft_default, 'toolbox') && isfield(ft_default.toolbox, 'cleanup') + prevcleanup = ft_default.toolbox.cleanup; +else + prevcleanup = {}; +end + % Ensure that the path containing ft_defaults is on the path. % This allows people to do "cd path_to_fieldtrip; ft_defaults" ftPath = fileparts(mfilename('fullpath')); % get the full path to this function, strip away 'ft_defaults' @@ -112,14 +148,13 @@ if ~isdeployed - if isempty(which('ft_hastoolbox')) + if isempty(which('ft_hastoolbox')) || isempty(which('ft_platform_supports')) % the fieldtrip/utilities directory contains the ft_hastoolbox and ft_warning % functions, which are required for the remainder of this script addpath(fullfile(fileparts(which('ft_defaults')), 'utilities')); end - % Some people mess up their path settings and then have - % different versions of certain toolboxes on the path. + % Some people mess up their path settings and then have different versions of certain toolboxes on the path. % The following will issue a warning checkMultipleToolbox('FieldTrip', 'ft_defaults.m'); checkMultipleToolbox('spm', 'spm.m'); @@ -152,21 +187,21 @@ try % external/signal contains alternative implementations of some signal processing functions - if ~ft_platform_supports('signal') || ~ft_hastoolbox('signal') || ~strcmp(ft_default.toolbox.signal, 'matlab') + if ~ft_platform_supports('signal') || ~strcmp(ft_default.toolbox.signal, 'matlab') || ~ft_hastoolbox('signal') addpath(fullfile(fileparts(which('ft_defaults')), 'external', 'signal')); end end try % external/stats contains alternative implementations of some statistics functions - if ~ft_platform_supports('stats') || ~ft_hastoolbox('stats') || ~strcmp(ft_default.toolbox.stats, 'matlab') + if ~ft_platform_supports('stats') || ~strcmp(ft_default.toolbox.stats, 'matlab') || ~ft_hastoolbox('stats') addpath(fullfile(fileparts(which('ft_defaults')), 'external', 'stats')); end end try % external/images contains alternative implementations of some image processing functions - if ~ft_platform_supports('images') || ~ft_hastoolbox('images') || ~strcmp(ft_default.toolbox.images, 'matlab') + if ~ft_platform_supports('images') || ~strcmp(ft_default.toolbox.images, 'matlab') || ~ft_hastoolbox('images') addpath(fullfile(fileparts(which('ft_defaults')), 'external', 'images')); end end @@ -198,6 +233,14 @@ if ft_platform_supports('matlabversion', -inf, '2016b'), ft_hastoolbox('compat/matlablt2017a', 3, 1); end if ft_platform_supports('matlabversion', -inf, '2017a'), ft_hastoolbox('compat/matlablt2017b', 3, 1); end if ft_platform_supports('matlabversion', -inf, '2017b'), ft_hastoolbox('compat/matlablt2018a', 3, 1); end + if ft_platform_supports('matlabversion', -inf, '2018a'), ft_hastoolbox('compat/matlablt2018b', 3, 1); end + if ft_platform_supports('matlabversion', -inf, '2018b'), ft_hastoolbox('compat/matlablt2019a', 3, 1); end + if ft_platform_supports('matlabversion', -inf, '2019a'), ft_hastoolbox('compat/matlablt2019b', 3, 1); end + if ft_platform_supports('matlabversion', -inf, '2019b'), ft_hastoolbox('compat/matlablt2020a', 3, 1); end + if ft_platform_supports('matlabversion', -inf, '2020a'), ft_hastoolbox('compat/matlablt2020b', 3, 1); end + if ft_platform_supports('matlabversion', -inf, '2020b'), ft_hastoolbox('compat/matlablt2021a', 3, 1); end + % this deals with compatibility with all OCTAVE versions + if ft_platform_supports('octaveversion', -inf, +inf), ft_hastoolbox('compat/octave', 3, 1); end end try @@ -275,6 +318,9 @@ end +% the toolboxes added by this function should not be removed by FT_POSTAMBLE_HASTOOLBOX +ft_default.toolbox.cleanup = prevcleanup; + % track the usage of this function, this only happens once at startup ft_trackusage('startup'); @@ -283,6 +329,7 @@ end % function ft_default + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -310,3 +357,13 @@ function checkMultipleToolbox(toolbox, keyfile) end end % function checkMultipleToolbox +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function checkIncorrectPath +p = fileparts(mfilename('fullpath')); +incorrect = fullfile(p, 'compat', 'incorrect'); +if ~isdeployed && ~isempty(strfind(path, incorrect)) + ft_warning('Your path is set up incorrectly. You probably used addpath(genpath(''path_to_fieldtrip'')), this can lead to unexpected behaviour. See http://www.fieldtriptoolbox.org/faq/should_i_add_fieldtrip_with_all_subdirectories_to_my_matlab_path'); +end +end % function checkIncorrectPath diff --git a/external/fieldtrip/ft_definetrial.m b/external/fieldtrip/ft_definetrial.m index a970423c..899c2fbc 100644 --- a/external/fieldtrip/ft_definetrial.m +++ b/external/fieldtrip/ft_definetrial.m @@ -34,7 +34,7 @@ % a negative offset indicates that the trial begins before the trigger. % % The trial definition "trl" can contain additional columns besides the -% required three that represend begin, end and offset. These additional +% required three that represent begin, end and offset. These additional % columns can be used by a custom trialfun to provide numeric information % about each trial such as trigger codes, response latencies, trial % type and response correctness. The additional columns of the "trl" diff --git a/external/fieldtrip/ft_denoise_dssp.m b/external/fieldtrip/ft_denoise_dssp.m new file mode 100644 index 00000000..91268609 --- /dev/null +++ b/external/fieldtrip/ft_denoise_dssp.m @@ -0,0 +1,276 @@ +function [dataout] = ft_denoise_dssp(cfg, datain) + +% FT_DENOISE_DSSP implements a dual signal subspace projection algorithm +% to suppress interference outside a predefined source region of +% interest. It is based on: Sekihara et al. J. Neural Eng. 2016 13(3), and +% Sekihara et al. J. Neural Eng. 2018 15(3). +% +% Use as +% dataout = ft_denoise_dssp(cfg, datain) +% where cfg is a configuration structure that contains +% cfg.grid = structure, source model with precomputed leadfields (see +% FT_PREPARE_LEADFIELD) +% cfg.dssp = structure, containing the parameters that determine the +% behavior of the algorithm. +% cfg.dssp.n_space = 'all', or scalar. Number of +% dimensions for the initial spatial +% projection. +% cfg.dssp.n_in = 'all', or scalar. Number of +% dimensions of the subspace describing +% the field inside the ROI +% cfg.dssp.n_out = 'all', or scalar. Number of +% dimensions of the subspace describing +% the field outside the ROI +% cfg.dssp.n_intersect = scalar (default = 0.9). Number +% of dimensions (if value is an +% integer>=1), or threshold for the +% included eigenvalues (if value<1), +% determining the dimensionality of +% the intersection. +% +% cfg.channel = Nx1 cell-array with selection of channels (default = 'all'), +% see FT_CHANNELSELECTION for details +% cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') + +% +% See also FT_DENOISE_PCA, FT_DENOISE_SYNTHETIC, FT_DENOISE_TSR + +% these are used by the ft_preamble/ft_postamble function and scripts +ft_revision = '$Id$'; +ft_nargin = nargin; +ft_nargout = nargout; + +% do the general setup of the function +ft_defaults +ft_preamble init +ft_preamble debug +ft_preamble loadvar datain +ft_preamble provenance datain +ft_preamble trackconfig + +% the ft_abort variable is set to true or false in ft_preamble_init +if ft_abort + % do not continue function execution in case the outputfile is present and the user indicated to keep it + return +end + +% get the options +cfg.trials = ft_getopt(cfg, 'trials', 'all', 1); +cfg.channel = ft_getopt(cfg, 'channel', 'all'); +cfg.grid = ft_getopt(cfg, 'grid'); +cfg.dssp = ft_getopt(cfg, 'dssp'); % sub-structure to hold the parameters +cfg.dssp.n_space = ft_getopt(cfg.dssp, 'n_space', 'all'); % number of spatial components to retain from the Gram matrix +cfg.dssp.n_in = ft_getopt(cfg.dssp, 'n_in', 'all'); % dimensionality of the Bin subspace to be used for the computation of the intersection +cfg.dssp.n_out = ft_getopt(cfg.dssp, 'n_out', 'all'); % dimensionality of the Bout subspace to be used for the computation of the intersection +cfg.dssp.n_intersect = ft_getopt(cfg.dssp, 'n_intersect', 0.9); % dimensionality of the intersection +cfg.output = ft_getopt(cfg, 'output', 'original'); + +% check the input data +datain = ft_checkdata(datain, 'datatype', {'raw' 'timelock'}); % freq? +% FIXME tlck not yet supported + +% select channels and trials of interest, by default this will select all channels and trials +tmpcfg = keepfields(cfg, {'trials', 'channel', 'showcallinfo'}); +datain = ft_selectdata(tmpcfg, datain); +% restore the provenance information +[cfg, datain] = rollback_provenance(cfg, datain); + +% match the input data's channels with the labels in the leadfield +grid = cfg.grid; +if ~isfield(grid, 'leadfield') + ft_error('cfg.grid needs to contain leadfields'); +end +[indx1, indx2] = match_str(datain.label, grid.label); +if ~isequal(indx1(:),(1:numel(datain.label))') + ft_error('unsupported mismatch between data channels and leadfields'); +end +if islogical(grid.inside) + inside = find(grid.inside); +else + inside = grid.inside; +end +for k = inside(:)' + grid.leadfield{k} = grid.leadfield{k}(indx2,:); +end + +% compute the Gram-matrix of the supplied forward model +lf = cat(2, grid.leadfield{:}); +G = lf*lf'; + +dat = cat(2,datain.trial{:}); +[dum, Ae, N, Nspace, Sout, Sin, Sspace, S] = dssp(dat, G, cfg.dssp.n_in, cfg.dssp.n_out, cfg.dssp.n_space, cfg.dssp.n_intersect); +datAe = dat*Ae; % the projection is a right multiplication +% with a matrix (eye(size(Ae,1))-Ae*Ae'), since Ae*Ae' can become quite +% sizeable, it's computed slightly differently here. + +% put some diagnostic information in the output cfg. +cfg.dssp.S_space = Sspace; +cfg.dssp.n_space = Nspace; +cfg.dssp.S_out = Sout; +cfg.dssp.S_in = Sin; +cfg.dssp.S_intersect = S; +cfg.dssp.n_intersect = N; + +% compute the cleaned data and put in a cell-array +nsmp = cellfun(@numel, datain.time); +csmp = cumsum([0 nsmp]); +trial = cell(size(datain.trial)); +switch cfg.output + case 'original' + for k = 1:numel(datain.trial) + trial{k} = datain.trial{k} - datAe*Ae((csmp(k)+1):csmp(k+1),:)'; + end + case 'complement' + for k = 1:numel(datain.trial) + trial{k} = datAe*Ae((csmp(k)+1):csmp(k+1),:)'; + end + otherwise + ft_error(sprintf('cfg.output = ''%s'' is not implemented',cfg.output)); +end + +% create the output argument +dataout = keepfields(datain, {'label','time','fsample','trialinfo','sampleinfo','grad', 'elec', 'opto'}); % grad can be kept and does not need to be balanced, since the cleaned data is a mixture over time, not space. +dataout.trial = trial; + +ft_postamble debug +ft_postamble trackconfig +ft_postamble previous datain +ft_postamble provenance dataout +ft_postamble history dataout +ft_postamble savevar dataout + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% subfunctions for the computation of the projection matrix +% kindly provided by Kensuke, and adjusted a bit by Jan-Mathijs +function [Bclean, Ae, Nee, Nspace, Sout, Sin, Sspace, S] = dssp(B, G, Nin, Nout, Nspace, Nee) + +% Nc: number of sensors +% Nt: number of time points +% inputs +% B(Nc,Nt): interference overlapped sensor data +% G(Nc,Nc): Gram matrix of voxel lead field +% Nout and Nin: dimensions of the two row spaces +% recom_Nspace: recommended value for the dimension of the pseudo-signal subspace +% outputs +% Bclean(Nc,Nt): cleaned sensor data +% Nee: dimension of the intersection +% Nspace: dimension of the pseudo-signal subspace +% ------------------------------------------------------------ +% programmed by K. Sekihara, Signal Analysis Inc. +% All right reserved by Signal Analysis Inc. +% ------------------------------------------------------------- +% +% The code below is modified by Jan-Mathijs, no functional changes +% merely cosmetics + +% eigen decomposition of the Gram matrix, matrix describing the spatial +% components +[U,S] = eig(G); +Sspace = abs(diag(S)); + +[Sspace, iorder] = sort(-Sspace); +Sspace = -Sspace; +U(:,:) = U(:,iorder); + +if isempty(Nspace) + ttext = 'enter the spatial dimension: '; + Nspace = input(ttext); +elseif ischar(Nspace) && isequal(Nspace, 'interactive') + figure, plot(log10(Sspace),'-o'); + Nspace = input('enter spatial dimension of the ROI subspace: '); +elseif ischar(Nspace) && isequal(Nspace, 'all') + Nspace = find(Sspace./Sspace(1)>1e5*eps, 1, 'last'); +end +fprintf('Using %d spatial dimensions\n', Nspace); + +% spatial subspace projector +Us = U(:,1:Nspace); +USUS = Us*Us'; + +% Bin and Bout creations +Bin = USUS * B; +Bout = (eye(size(USUS))-USUS) * B; + +% create the temporal subspace projector and apply it to the data +%[AeAe, Nee] = CSP01(Bin, Bout, Nout, Nin, Nee); +%Bclean = B*(eye(size(AeAe))-AeAe); + +[Ae, Nee, Sout, Sin, S] = CSP01(Bin, Bout, Nin, Nout, Nee); +Bclean = B - (B*Ae)*Ae'; % avoid computation of Ae*Ae' + + +function [Ae, Nee, Sout, Sin, S] = CSP01(Bin, Bout, Nin, Nout, Nee) +% +% interference rejection by removing the common temporal subspace of the two subspaces +% K. Sekihara, March 28, 2012 +% Golub and Van Loan, Matrix computations, The Johns Hopkins University Press, 1996 +% +% Nc: number of channels +% Nt: number of time points +% inputs +% Bout(1:Nc,1:Nt): interference data +% Bin(1:Nc,1:Nt): signal plus interference data +% ypost(1:Nc,1:Nt): denoised data +% Nout: dimension of the interference subspace +% Nin: dimension of the signal plus interference subspace +% Nee: dimension of the intersection of the two subspaces +% outputs +% Ae = matrix from which the projector onto the intersection can +% be obtained: +% AeAe: projector onto the intersection, which is equal to the +% interference subspace. +% Nee: dimension of the intersection +% ------------------------------------------------------------ +% programmed by K. Sekihara, Signal Analysis Inc. +% All right reserved by Signal Analysis Inc. +% ------------------------------------------------------------- +% + +[dum,Sout,Vout] = svd(Bout,'econ'); +[dum,Sin, Vin] = svd(Bin, 'econ'); +Sout = diag(Sout); +Sin = diag(Sin); + +if isempty(Nout) + ttext = 'enter the spatial dimension for the outside field: '; + Nout = input(ttext); +elseif ischar(Nout) && isequal(Nout, 'interactive') + figure, plot(Sout,'-o'); + Nout = input('enter dimension of the outside field: '); +elseif ischar(Nout) && isequal(Nout, 'all') + Nout = find(Sout./Sout(1)>1e5*eps, 1, 'last'); +end +fprintf('Using %d spatial dimensions for the outside field\n', Nout); + +if isempty(Nin) + ttext = 'enter the spatial dimension for the inside field: '; + Nin = input(ttext); +elseif ischar(Nin) && isequal(Nin, 'interactive') + figure, plot(log10(Sin),'-o'); + Nin = input('enter dimension of the inside field: '); +elseif ischar(Nin) && isequal(Nin, 'all') + Nin = find(Sin./Sin(1)>1e5*eps, 1, 'last'); +end +fprintf('Using %d spatial dimensions for the inside field\n', Nin); + +Qout = Vout(:,1:Nout); +Qin = Vin(:, 1:Nin); + +C = Qin'*Qout; +[U,S] = svd(C); +S = diag(S); +if (ischar(Nee) && strcmp(Nee, 'auto')) + ft_error('automatic determination of intersection dimension is not supported'); +elseif ischar(Nee) && strcmp(Nee, 'interactive') + figure, plot(S,'-o'); + Nee = input('enter dimension of the intersection: '); +elseif Nee<1 + % treat a numeric value < 1 as a threshold + Nee = find(S>Nee,1,'last'); + if isempty(Nee), Nee = 1; end +end +fprintf('Using %d dimensions for the interaction\n', Nee); + +Ae = Qin*U; +Ae = Ae(:,1:Nee); +%AeAe = Ae*Ae'; diff --git a/external/fieldtrip/ft_denoise_tsr.m b/external/fieldtrip/ft_denoise_tsr.m new file mode 100644 index 00000000..7416985e --- /dev/null +++ b/external/fieldtrip/ft_denoise_tsr.m @@ -0,0 +1,466 @@ +function dataout = ft_denoise_tsr(cfg, varargin) + +% FT_DENOISE_TSR performs a regression analysis, using a (time-shifted set +% of) reference signal(s) as independent variable. It is a generic +% implementation of the method described by De Cheveigne +% (https://doi.org/10.1016/j.jneumeth.2007.06.003), or can be +% used to compute temporal-response-functions (see e.g. Crosse +% (https://doi.org/10.3389/fnhum.2016.00604)), or +% spatial filters based on canonical correlation (see Thielen +% (https://doi.org/10.1371/journal.pone.0133797)) +% +% Use as +% [dataout] = ft_denoise_tsr(cfg, data) +% +% or as +% [dataout] = ft_denoise_tsr(cfg, data, refdata) +% +% where "data" is a raw data structure that was obtained with FT_PREPROCESSING. If +% you specify the additional input "refdata", the specified reference channels for +% the regression will be taken from this second data structure. This can be useful +% when reference-channel specific preprocessing needs to be done (e.g. low-pass +% filtering). +% +% The output structure dataout contains the denoised data in a format that is +% consistent with the output of FT_PREPROCESSING. +% +% The configuration options are: +% +% cfg.refchannel = the channels used as reference signal (default = 'MEGREF'), see FT_SELECTDATA +% cfg.channel = the channels to be denoised (default = 'all'), see FT_SELECTDATA +% cfg.method = string, 'mlr', 'cca', 'pls', 'svd', option specifying the criterion for the regression +% (default = 'mlr') +% cfg.reflags = integer array, specifying temporal lags (in msec) by which to shift refchannel +% with respect to data channels +% cfg.trials = integer array, trials to be used in regression, see FT_SELECTDATA +% cfg.testtrials = cell array or string, trial indices to be used as test folds in a cross-validation scheme +% (numel(cfg.testrials == number of folds)) +% cfg.nfold = scalar, indicating the number of test folds to +% use in a cross-validation scheme +% cfg.standardiserefdata = string, 'yes' or 'no', whether or not to standardise reference data +% prior to the regression (default = 'no') +% cfg.standardisedata = string, 'yes' or 'no', whether or not to standardise dependent variable +% prior to the regression (default = 'no') +% cfg.demeanrefdata = string, 'yes' or 'no', whether or not to make +% reference data zero mean prior to the regression (default = 'no') +% cfg.demeandata = string, 'yes' or 'no', whether or not to make +% dependent variable zero mean prior to the regression (default = 'no') +% cfg.threshold = integer array, ([1 by 2] or [1 by numel(cfg.channel) + numel(cfg.reflags)]), +% regularization or shrinkage ('lambda') parameter to be loaded on the diagonal of the +% penalty term (if cfg.method == 'mlrridge' or 'mlrqridge') +% cfg.updatesens = string, 'yes' or 'no' (default = 'yes') +% cfg.perchannel = string, 'yes' or 'no', or logical, whether or not to perform estimation of beta weights +% separately per channel +% cfg.output = string, 'model' or 'residual' (defaul = 'model'), +% specifies what is outputed in .trial field in +% cfg.performance = string, 'Pearson' or 'r-squared' (default = +% 'Pearson'), indicating what performance metric is outputed in .weights(k).performance +% field of for the k-th fold +% +% === cfg.threshold +% if cfg.threshold is 1 x 2 integer array, cfg.threshold(1) parameter scales uniformly +% in the dimension of predictor variable and cfg.threshold(2) in the space of +% response variable +% +% + +% See also FT_PREPROCESSING, FT_DENOISE_SYNTHETIC, FT_DENOISE_PCA + +% Copyright (c) 2008-2009, Jan-Mathijs Schoffelen, CCNi Glasgow +% Copyright (c) 2010-2011, Jan-Mathijs Schoffelen, DCCN Nijmegen +% Copyright (c) 2018, Jan-Mathijs Schoffelen +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +% UNDOCUMENTED OPTIONS (or possibly unused) +% cfg.testsamples +% cfg.truncate +% cfg.trials +% +% === cfg.truncate +% if cfg.truncate is integer n > 1, n will be the number of singular values kept. +% if 0 < cfg.truncate < 1, the singular value spectrum will be thresholded at the +% fraction cfg.truncate of the explained variance. + +% these are used by the ft_preamble/ft_postamble function and scripts +ft_revision = '$Id$'; +ft_nargin = nargin; +ft_nargout = nargout; + +% do the general setup of the function +ft_defaults +ft_preamble init +ft_preamble debug +ft_preamble provenance varargin +ft_preamble trackconfig + +% the ft_abort variable is set to true or false in ft_preamble_init +if ft_abort + return +end + +% check if the input data is valid for this function +for i=1:length(varargin) + varargin{i} = ft_checkdata(varargin{i}, 'datatype', 'raw'); +end + +cfg.nfold = ft_getopt(cfg, 'nfold', 1); +cfg.blocklength = ft_getopt(cfg, 'blocklength', 'trial'); +cfg.testtrials = ft_getopt(cfg, 'testtrials', 'all'); +cfg.testsamples = ft_getopt(cfg, 'testsamples', 'all'); +cfg.refchannel = ft_getopt(cfg, 'refchannel', ''); +cfg.reflags = ft_getopt(cfg, 'reflags', 0); %this needs to be known for the folding + +% set the rest of the defaults +cfg.channel = ft_getopt(cfg, 'channel', 'all'); +cfg.truncate = ft_getopt(cfg, 'truncate', 'no'); +cfg.standardiserefdata = ft_getopt(cfg, 'standardiserefdata', 'no'); +cfg.standardisedata = ft_getopt(cfg, 'standardisedata', 'no'); +cfg.demeanrefdata = ft_getopt(cfg, 'demeanrefdata', 'no'); +cfg.demeandata = ft_getopt(cfg, 'demeandata', 'no'); +cfg.trials = ft_getopt(cfg, 'trials', 'all', 1); +cfg.feedback = ft_getopt(cfg, 'feedback', 'none'); +cfg.updatesens = ft_getopt(cfg, 'updatesens', 'yes'); +cfg.perchannel = ft_getopt(cfg, 'perchannel', 'yes'); +cfg.method = ft_getopt(cfg, 'method', 'mlr'); +cfg.threshold = ft_getopt(cfg, 'threshold', 0); +cfg.output = ft_getopt(cfg, 'output', 'model'); +cfg.performance = ft_getopt(cfg, 'performance', 'Pearson'); + +if ~iscell(cfg.refchannel) + cfg.refchannel = {cfg.refchannel}; +end + +if iscell(cfg.testtrials) + % this has precedence above nfold + cfg.nfold = numel(cfg.testtrials); +end + +if cfg.nfold<=1 + dataout = ft_denoise_tsr_core(cfg, varargin{:}); +else + % do a cross validation + if numel(varargin{1}.trial)>1 && ischar(cfg.blocklength) && isequal(cfg.blocklength, 'trial') + if ~iscell(cfg.testtrials) + % create sets of trial indices for the test data + ntrl = numel(varargin{1}.trial); + edges = round(linspace(0,ntrl,cfg.nfold+1)); + indx = randperm(ntrl); + cfg.testtrials = cell(1,cfg.nfold); + for k = 1:cfg.nfold + cfg.testtrials{k} = indx((edges(k)+1):edges(k+1)); + end + end + + testtrials = cfg.testtrials; + tmp = cell(1,numel(testtrials)); + for k = 1:numel(testtrials) + fprintf('estimating model for fold %d/%d\n', k, numel(testtrials)); + cfg.testtrials = testtrials{k}; + tmp{k} = ft_denoise_tsr_core(cfg, varargin{:}); + end + + % create output data structure + dataout = keepfields(tmp{1}, {'fsample' 'label'}); + for k = 1:numel(testtrials) + tmp{k}.weights.trials = testtrials{k}; + + dataout.trial(testtrials{k}) = tmp{k}.trial; + dataout.time(testtrials{k}) = tmp{k}.time; + dataout.weights(k) = tmp{k}.weights; + dataout.cfg.previous{k} = tmp{k}.cfg; + if isfield(tmp{k}, 'trialinfo') + dataout.trialinfo(testtrials{k},:) = tmp{k}.trialinfo; + end + end + + elseif numel(varargin{1}.trial==1) ||(numel(varargin{1}.trial)>1 && ~ischar(cfg.blocklength)) + % concatenate into a single trial, with sufficient nan-spacing to + % accommodate the shifting, and do a chunk-based folding + error('not yet implemented'); + else + error('incorrect specification of data and cfg.blocklength'); + end + +end + +% do the general cleanup and bookkeeping at the end of the function +ft_postamble debug +ft_postamble trackconfig +ft_postamble previous varargin + +ft_postamble provenance dataout +ft_postamble history dataout +ft_postamble savevar dataout + +%------------------------------------------------- +function dataout = ft_denoise_tsr_core(cfg, varargin) + +% create a separate structure for the reference data +tmpcfg = keepfields(cfg, {'trials', 'showcallinfo'}); +tmpcfg.channel = cfg.refchannel; +if numel(varargin)>1 + fprintf('selecting reference channel data from the second data input argument\n'); + refdata = ft_selectdata(tmpcfg, varargin{2}); +else + fprintf('selecting reference channel data from the first data input argument\n'); + refdata = ft_selectdata(tmpcfg, varargin{1}); +end +[dum, refdata] = rollback_provenance(cfg, refdata); + +% keep the requested channels from the data +tmpcfg = keepfields(cfg, {'trials', 'showcallinfo' 'channel'}); +data = ft_selectdata(tmpcfg, varargin{1}); +[cfg, data] = rollback_provenance(cfg, data); + +% deal with the specification of testtrials/testsamples, as per the +% instruction by the caller function, for cross-validation purposes +if ~ischar(cfg.testtrials) && ischar(cfg.testsamples) && isequal(cfg.testsamples, 'all') + % subselect trials for testing + usetestdata = true; + + tmpcfg = []; + tmpcfg.trials = cfg.testtrials; + testdata = ft_selectdata(tmpcfg, data); + testrefdata = ft_selectdata(tmpcfg, refdata); + tmpcfg.trials = setdiff(1:numel(data.trial), cfg.testtrials); + data = ft_selectdata(tmpcfg, data); + refdata = ft_selectdata(tmpcfg, refdata); + +elseif ~ischar(cfg.testsamples) && ischar(cfg.testtrials) && isequal(cfg.testtrials, 'all') + % subselect samples from a single trial for testing + usetestdata = true; +elseif ischar(cfg.testtrials) && ischar(cfg.testsamples) + % just a single fold, use all data for training and testing + usetestdata = false; +else + error('something wrong here'); +end + +% demean +if istrue(cfg.demeanrefdata) + fprintf('demeaning the reference channels\n'); + mu_refdata = cellmean(refdata.trial, 2); + refdata.trial = cellvecadd(refdata.trial, -mu_refdata); + if usetestdata + mu_testrefdata = cellmean(testrefdata.trial, 2); + testrefdata.trial = cellvecadd(testrefdata.trial, -mu_testrefdata); + end +end +if istrue(cfg.demeandata) + fprintf('demeaning the data channels\n'); + mu_data = cellmean(data.trial, 2); + data.trial = cellvecadd(data.trial, -mu_data); + if usetestdata + mu_testdata = cellmean(testdata.trial, 2); + testdata.trial = cellvecadd(testdata.trial, -mu_testdata); + end +end + +% standardise the data +if istrue(cfg.standardiserefdata) + fprintf('standardising the reference channels \n'); + [refdata.trial, std_refdata] = cellzscore(refdata.trial, 2, 0); +end +if istrue(cfg.standardisedata) + fprintf('standardising the data channels \n'); + [data.trial, std_data] = cellzscore(data.trial, 2, 0); +end + +% do the time shifting for the reference channel data +ft_hastoolbox('cellfunction', 1); + +timestep = mean(diff(data.time{1})); +reflags = -round(cfg.reflags./timestep); +reflabel = refdata.label; % to be used later +% the convention is to have a positive cfg.reflags defined as a delay of the ref w.r.t. the chan +% cellshift has an opposite convention with respect to the sign of the +% delay, hence the minus +if ~any(reflags==0) + ft_error('the time lags for the reference data should at least include the sample 0'); +end +fprintf('shifting the reference data\n'); +refdata.trial = cellshift(refdata.trial, reflags, 2, [], 'overlap'); +refdata.time = cellshift(data.time, 0, 2, [abs(min(reflags)) abs(max(reflags))], 'overlap'); +refdata.label = repmat(refdata.label,numel(reflags),1); +for k = 1:numel(refdata.label) + refdata.label{k} = sprintf('%s_shift%03d',refdata.label{k}, k); +end + +% center the data on lag 0 +data.trial = cellshift(data.trial, 0, 2, [abs(min(reflags)) abs(max(reflags))], 'overlap'); +data.time = cellshift(data.time, 0, 2, [abs(min(reflags)) abs(max(reflags))], 'overlap'); + +% only keep the trials that have > 0 samples +tmpcfg = []; +tmpcfg.trials = find(cellfun('size',data.trial,2)>0); +data = ft_selectdata(tmpcfg, data); +[cfg, data] = rollback_provenance(cfg, data); +refdata = ft_selectdata(tmpcfg, refdata); +[dum,refdata] = rollback_provenance(cfg, refdata); + +% demean again, just to be sure +if istrue(cfg.demeanrefdata) + fprintf('demeaning the reference channels\n'); + mu_refdata = cellmean(refdata.trial, 2); + refdata.trial = cellvecadd(refdata.trial, -mu_refdata); +end +if istrue(cfg.demeandata) + fprintf('demeaning the data channels\n'); % the edges have been chopped off + mu_data = cellmean(data.trial, 2); + data.trial = cellvecadd(data.trial, -mu_data); +end + +% compute the covariance +fprintf('computing the covariance\n'); +nref = size(refdata.trial{1},1); +nchan = numel(data.label); + +C = nan(nchan,nchan); +C(1:nchan,1:nchan) = nancov(data.trial, data.trial, 1, 2, 1); +C(1:nchan,nchan+(1:nref)) = nancov(data.trial, refdata.trial, 1, 2, 1); +C(nchan+(1:nref),1:nchan) = C(1:nchan,nchan+(1:nref)).'; +C(nchan+(1:nref),nchan+(1:nref)) = nancov(refdata.trial, refdata.trial, 1, 2, 1); + +% compute the regression +if istrue(cfg.perchannel) + beta_ref = zeros(nchan, nref); + rho = zeros(nchan,1); + for k = 1:nchan + indx = [k nchan+(1:nref)]; + [E, rho(k)] = multivariate_decomp(C(indx,indx), 1+(1:nref), 1, cfg.method, 1, cfg.threshold); + %beta_ref(k,:) = E(2:end)./E(1); + beta_ref(k,:) = E(2:end);%./E(1); + end + %beta_ref = (diag(rho))*beta_ref; % scale with sqrt(rho), to get the proper scaling + +else + [E, rho] = multivariate_decomp(C, 1:nchan, nchan+(1:nref), cfg.method, 1, cfg.threshold); + %beta_ref = normc(E(nchan+(1:nref),:))'; + %beta_data = normc(E(1:nchan,:))'; + beta_ref = E(nchan+(1:nref),:); + beta_data = E(1:nchan,:); +end + +% Unstandardise the data/refchannels and test data/refchannels +if istrue(cfg.standardiserefdata) + std_refdata = repmat(std_refdata, numel(cfg.reflags), 1); + refdata.trial = cellvecmult(refdata.trial, std_refdata); + if exist('beta_data', 'var') + beta_ref = beta_ref'*diag(std_refdata); + else + beta_ref = diag(std_data)*beta_ref*diag(1./std_refdata); + end +end + +if istrue(cfg.standardisedata) + data.trial = cellvecmult(data.trial, std_data); + if exist('beta_data', 'var') + beta_data = diag(std_data)*beta_data; + end +end + +if usetestdata + fprintf('shifting the reference data for the test data\n'); + testrefdata.trial = cellshift(testrefdata.trial, reflags, 2, [], 'overlap'); + testrefdata.time = cellshift(testdata.time, 0, 2, [abs(min(reflags)) abs(max(reflags))], 'overlap'); + testrefdata.label = repmat(testrefdata.label,numel(reflags),1); + for k = 1:numel(testrefdata.label) + testrefdata.label{k} = sprintf('%s_shift%03d',testrefdata.label{k}, k); + end + + % center the data on lag 0 + testdata.trial = cellshift(testdata.trial, 0, 2, [abs(min(reflags)) abs(max(reflags))], 'overlap'); + testdata.time = cellshift(testdata.time, 0, 2, [abs(min(reflags)) abs(max(reflags))], 'overlap'); + + % demean again, just to be sure + if istrue(cfg.demeanrefdata) + fprintf('demeaning the reference channels\n'); + mu_testrefdata = cellmean(testrefdata.trial, 2); + testrefdata.trial = cellvecadd(testrefdata.trial, -mu_testrefdata); + end + if istrue(cfg.demeandata) + fprintf('demeaning the data channels\n'); % the edges have been chopped off + mu_testdata = cellmean(testdata.trial, 2); + testdata.trial = cellvecadd(testdata.trial, -mu_testdata); + end + + % only keep the trials that have > 0 samples + tmpcfg = []; + tmpcfg.trials = find(cellfun('size',testdata.trial,2)>0); + testdata = ft_selectdata(tmpcfg, testdata); + [dum,testdata] = rollback_provenance(cfg, testdata); + testrefdata = ft_selectdata(tmpcfg, testrefdata); + [dum,testrefdata] = rollback_provenance(cfg, testrefdata); + + predicted = beta_ref*testrefdata.trial; + observed = testdata.trial; + time = testdata.time; +else + predicted = beta_ref*refdata.trial; + observed = data.trial; + time = data.time; +end + +% create output data structure +dataout = keepfields(data, {'cfg' 'label' 'grad' 'elec' 'opto' 'trialinfo' 'fsample'}); +dataout.time = time; +switch cfg.output + case 'model' + dataout.trial = predicted; + case 'residual' + dataout.trial = observed - predicted; +end + +% update the weights-structure +weights.time = cfg.reflags; +weights.rho = rho; +if exist('beta_data', 'var') + weights.unmixing = beta_data; + weights.beta = beta_ref; +else + % a per channel approach has been done, the beta weights reflect + % (channelxtime-lag) -> reshape + nref = numel(cfg.refchannel); + newbeta = zeros(size(beta_ref,1),size(beta_ref,2)./nref,nref); + for k = 1:size(newbeta,3) + newbeta(:,:,k) = beta_ref(:,k:nref:end); + end + weights.beta = newbeta; + weights.reflabel = reflabel; + weights.dimord = 'chan_lag_refchan'; +end + +% Compute performance statistics +fprintf('Computing performance metric\n'); +switch cfg.performance + case 'Pearson' + for k = 1:size(observed{1}, 1) + tmp = nancov(cellcat(1, cellrowselect(observed,k), cellrowselect(predicted,k)), 1, 2, 1); + weights.performance(k,1) = tmp(1,2)./sqrt(tmp(1,1).*tmp(2,2)); + end + case 'r-squared' + tss = nansum(observed.^2, 2); % total sum of squares, testdata are already mean subtracted in l. 330 + rss = nansum((observed - predicted).^2, 2); % sum of squared residual error + % R-squared + weights.performance = (tss-rss)./tss; +end + +dataout.weights = weights; + diff --git a/external/fieldtrip/ft_electrodeplacement.m b/external/fieldtrip/ft_electrodeplacement.m index dd81586a..e53d1061 100644 --- a/external/fieldtrip/ft_electrodeplacement.m +++ b/external/fieldtrip/ft_electrodeplacement.m @@ -30,16 +30,16 @@ % % Use as % [elec] = ft_electrodeplacement(cfg, ct) -% [elec] = ft_electrodeplacement(cfg, ct, mri) +% [elec] = ft_electrodeplacement(cfg, ct, mri, ..) % where the input mri should be an anatomical CT or MRI volume, or % [elec] = ft_electrodeplacement(cfg, headshape) % where the input headshape should be a surface triangulation. % % The configuration can contain the following options % cfg.method = string representing the method for placing the electrodes -% 'mri' interactively locate electrodes in a MRI or CT scan +% 'volume' interactively locate electrodes on three orthogonal slices of a volumetric MRI or CT scan % 'headshape' interactively locate electrodes on a head surface -% '1020' automatically place electrodes on a head surface +% '1020' automatically locate electrodes on a head surface according to the 10-20 system % % The following options apply to the mri method % cfg.parameter = string, field in data (default = 'anatomy' if present in data) @@ -63,7 +63,7 @@ % % See also FT_ELECTRODEREALIGN, FT_VOLUMEREALIGN, FT_VOLUMESEGMENT, FT_PREPARE_MESH -% Copyright (C) 2015-2017, Arjen Stolk, Sandon Griffin & Robert Oostenveld +% Copyright (C) 2015-2018, Arjen Stolk, Sandon Griffin & Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -129,6 +129,8 @@ cfg.method = 'volume'; case 'mesh' cfg.method = 'headshape'; + case 'source+mesh' + cfg.method = 'headshape'; end end @@ -143,45 +145,131 @@ headshape = ft_checkdata(headshape, 'hascoordsys', 'yes'); end +% set-up channels labels if possible +chanlabel = {}; chanstring = {}; +markerlab = {}; markerpos = {}; +if ~isempty(cfg.elec) % re-use previously placed (cfg.elec) electrodes + for e = 1:numel(cfg.elec.label) + chanlabel{end+1,1} = cfg.elec.label{e}; + chanstring{end+1} = ['' cfg.elec.label{e} '']; % hmtl'ize + + markerlab{end+1,1} = cfg.elec.label{e}; + markerpos{end+1,1} = cfg.elec.elecpos(e,:); + end +end +if ~isempty(cfg.channel) % use prespecified (cfg.channel) electrode labels + for c = 1:numel(cfg.channel) + if ~ismember(cfg.channel{c}, chanlabel) % avoid overlap between cfg.channel and elec.label + chanlabel{end+1,1} = cfg.channel{c}; + chanstring{end+1} = ['' cfg.channel{c} '']; % hmtl'ize + + markerlab{end+1,1} = {}; + markerpos{end+1,1} = zeros(0,3); + end + end +end +if isempty(cfg.elec) && isempty(cfg.channel) % create electrode labels on-the-fly + for c = 1:300 + chanlabel{end+1,1} = sprintf('%d', c); + chanstring{end+1} = ['' sprintf('%d', c) '']; % hmtl'ize + + markerlab{end+1,1} = {}; + markerpos{end+1,1} = zeros(0,3); + end +end + +% draw the user-interfaces switch cfg.method case 'headshape' + % start building the figure + h = figure(... + 'Name', mfilename,... + 'Units', 'normalized', ... + 'Color', [1 1 1], ... + 'Visible', 'on'); + set(h, 'windowbuttondownfcn', @cb_buttonpress); + set(h, 'windowbuttonupfcn', @cb_buttonrelease); + set(h, 'windowkeypressfcn', @cb_keyboard); + set(h, 'CloseRequestFcn', @cb_quit); + set(h, 'renderer', cfg.renderer); + + % electrode listbox + h1 = uicontrol('Style', 'listbox', ... + 'Parent', h, ... + 'Value', [], 'Min', 0, 'Max', numel(chanstring), ... + 'Units', 'normalized', ... + 'FontSize', 12, ... + 'Position', [.8 0.001 .2 .5], ... + 'Callback', @cb_eleclistbox, ... + 'String', chanstring); + + h8 = uicontrol('Style', 'checkbox',... + 'Parent', h, ... + 'Value', 0, ... + 'String', 'Labels',... + 'Units', 'normalized', ... + 'Position', [.8 0.6 .2 .05],... + 'BackgroundColor', [1 1 1], ... + 'HandleVisibility', 'on', ... + 'Callback', @cb_labelsbutton); + % give the user instructions - disp('Use the mouse to click on the desired electrode positions'); - disp('Afterwards you may have to update the electrode labels'); - disp('Press "r" to delete the last point add'); - disp('Press "+/-" to zoom in/out'); - disp('Press "w/a/s/d" to rotate'); + disp('Use the mouse to click on the desired position for the electrode'); + disp('Subsequently use the mouse to click on the corresponding electrode label'); + disp('Press "v" to update the light position'); disp('Press "q" when you are done'); - % open a figure - figure; - % plot the faces of the 2D or 3D triangulation - if isfield(headshape, 'color'); + % plot the faces of the 2D or 3D triangulation + if isfield(headshape, 'color') skin = 'none'; ft_plot_mesh(headshape); + view([90, 0]); else - skin = [255 213 119]/255; - ft_plot_mesh(headshape, 'facecolor', skin, 'EdgeColor', 'none', 'facealpha',1); + ft_plot_mesh(headshape, 'facecolor', 'skin', 'EdgeColor', 'none', 'facealpha',1); lighting gouraud - material shiny - camlight + material dull + lightangle(0, 90); + alpha 0.9 end - % rotate3d on - xyz = ft_select_point3d(headshape, 'nearest', false, 'multiple', true, 'marker', '*'); - numelec = size(xyz, 1); + % create structure to be passed to gui + opt = []; + opt.method = 'headshape'; % this is to use the same functionalities across volume and headshape + opt.headshape = headshape; + opt.label = chanlabel; + opt.axes = [h1 h8]; + opt.mainfig = h; + opt.quit = false; + opt.init = true; + opt.pos = [0 0 0]; % middle of the scan, head coordinates (FIXME: this might mess up vertex finding, being an anchor) + opt.showcrosshair = true; + opt.showlabels = false; + opt.showmarkers = true; + opt.markerlab = markerlab; + opt.markerpos = markerpos; + opt.markerdist = cfg.markerdist; % hidden option + + setappdata(h, 'opt', opt); - % construct the output electrode structure + while(opt.quit==0) + uiwait(h); + opt = getappdata(h, 'opt'); + end + delete(h); + + % collect the results elec = keepfields(headshape, {'unit', 'coordsys'}); - elec.elecpos = xyz; - for i=1:numelec - try - elec.label{i} = cfg.channel{i,1}; - catch - elec.label{i} = sprintf('%d', i); + elec.label = {}; + elec.elecpos = []; + for i=1:length(opt.markerlab) + if ~isempty(opt.markerlab{i,1}) + elec.label = [elec.label; opt.markerlab{i,1}]; + elec.elecpos = [elec.elecpos; opt.markerpos{i,1}]; end end + elec.chanpos = elec.elecpos; + elec.tra = eye(size(elec.elecpos,1)); case 'volume' @@ -195,7 +283,7 @@ set(h, 'windowbuttondownfcn', @cb_buttonpress); set(h, 'windowbuttonupfcn', @cb_buttonrelease); set(h, 'windowkeypressfcn', @cb_keyboard); - set(h, 'CloseRequestFcn', @cb_cleanup); + set(h, 'CloseRequestFcn', @cb_quit); set(h, 'renderer', cfg.renderer); % volume-dependent axis settings @@ -298,38 +386,6 @@ % 'Background', java.awt.Color.white, 'StateChangedCallback', @cb_intensityslider); % electrode listbox - chanlabel = {}; chanstring = {}; - markerlab = {}; markerpos = {}; - if ~isempty(cfg.elec) % re-use previously placed (cfg.elec) electrodes - for e = 1:numel(cfg.elec.label) - chanlabel{end+1,1} = cfg.elec.label{e}; - chanstring{end+1} = ['' cfg.elec.label{e} '']; % hmtl'ize - - markerlab{end+1,1} = cfg.elec.label{e}; - markerpos{end+1,1} = cfg.elec.elecpos(e,:); - end - end - if ~isempty(cfg.channel) % use prespecified (cfg.channel) electrode labels - for c = 1:numel(cfg.channel) - if ~ismember(cfg.channel{c}, chanlabel) % avoid overlap between cfg.channel and elec.label - chanlabel{end+1,1} = cfg.channel{c}; - chanstring{end+1} = ['' cfg.channel{c} '']; % hmtl'ize - - markerlab{end+1,1} = {}; - markerpos{end+1,1} = zeros(0,3); - end - end - end - if isempty(cfg.elec) && isempty(cfg.channel) % create electrode labels on-the-fly - for c = 1:150 - chanlabel{end+1,1} = sprintf('%d', c); - chanstring{end+1} = ['' sprintf('%d', c) '']; % hmtl'ize - - markerlab{end+1,1} = {}; - markerpos{end+1,1} = zeros(0,3); - end - end - h6 = uicontrol('Style', 'listbox', ... 'Parent', h, ... 'Value', [], 'Min', 0, 'Max', numel(chanstring), ... @@ -434,6 +490,7 @@ % create structure to be passed to gui opt = []; + opt.method = 'volume'; % this is to use the same functionalities across volume and headshape opt.label = chanlabel; opt.axes = [mri{1}.axes(1) mri{1}.axes(2) mri{1}.axes(3) h4 h5 h6 h7 h8 h9 h10 hscatter hscan]; opt.mainfig = h; @@ -443,6 +500,7 @@ opt.tag = 'ik'; opt.ana = mri{1}.dat; % the plotted anatomy opt.mri = mri; + opt.currmri = 1; opt.showcrosshair = true; opt.pos = [0 0 0]; % middle of the scan, head coordinates opt.showlabels = false; @@ -546,7 +604,7 @@ function cb_redraw(h, eventdata) if opt.init delete(findobj(opt.mainfig, 'Type', 'Surface')); % get rid of old orthos (to facilitate switching scans) - ft_plot_ortho(opt.ana, 'transform', opt.mri{1}.transform, 'location', opt.pos, 'style', 'subplot', 'parents', [h1 h2 h3], 'update', opt.update, 'doscale', false, 'clim', opt.clim, 'unit', opt.mri{1}.unit); + ft_plot_ortho(opt.ana, 'transform', opt.mri{opt.currmri}.transform, 'location', opt.pos, 'style', 'subplot', 'parents', [h1 h2 h3], 'update', opt.update, 'doscale', false, 'clim', opt.clim, 'unit', opt.mri{opt.currmri}.unit); opt.anahandles = findobj(opt.mainfig, 'Type', 'Surface')'; parenttag = get(opt.anahandles, 'parent'); @@ -562,19 +620,19 @@ function cb_redraw(h, eventdata) opt.axis = zeros(1,6); opt.axis = [opt.axes(1).XLim opt.axes(1).YLim opt.axes(1).ZLim]; else - ft_plot_ortho(opt.ana, 'transform', opt.mri{1}.transform, 'location', opt.pos, 'style', 'subplot', 'surfhandle', opt.anahandles, 'update', opt.update, 'doscale', false, 'clim', opt.clim, 'unit', opt.mri{1}.unit); + ft_plot_ortho(opt.ana, 'transform', opt.mri{opt.currmri}.transform, 'location', opt.pos, 'style', 'subplot', 'surfhandle', opt.anahandles, 'update', opt.update, 'doscale', false, 'clim', opt.clim, 'unit', opt.mri{opt.currmri}.unit); fprintf('==================================================================================\n'); lab = 'crosshair'; - switch opt.mri{1}.unit + switch opt.mri{opt.currmri}.unit case 'mm' - fprintf('%10s at [%.1f %.1f %.1f] %s\n', lab, opt.pos, opt.mri{1}.unit); + fprintf('%10s at [%.1f %.1f %.1f] %s\n', lab, opt.pos, opt.mri{opt.currmri}.unit); case 'cm' - fprintf('%10s at [%.2f %.2f %.2f] %s\n', lab, opt.pos, opt.mri{1}.unit); + fprintf('%10s at [%.2f %.2f %.2f] %s\n', lab, opt.pos, opt.mri{opt.currmri}.unit); case 'm' - fprintf('%10s at [%.4f %.4f %.4f] %s\n', lab, opt.pos, opt.mri{1}.unit); + fprintf('%10s at [%.4f %.4f %.4f] %s\n', lab, opt.pos, opt.mri{opt.currmri}.unit); otherwise - fprintf('%10s at [%f %f %f] %s\n', lab, opt.pos, opt.mri{1}.unit); + fprintf('%10s at [%f %f %f] %s\n', lab, opt.pos, opt.mri{opt.currmri}.unit); end end @@ -734,7 +792,7 @@ function cb_scatterredraw(h, eventdata) set(opt.scatterfig, 'CloseRequestFcn', @cb_scattercleanup); opt.scatterfig_h1 = axes('position', [0.02 0.02 0.96 0.96]); set(opt.scatterfig_h1, 'DataAspectRatio', get(opt.axes(1), 'DataAspectRatio')); - axis square; axis tight; axis off; view([0 0]); + axis square; axis tight; axis off; view([90 0]); xlabel('x'); ylabel('y'); zlabel('z'); % scatter range sliders @@ -775,7 +833,7 @@ function cb_scatterredraw(h, eventdata) set(opt.scatterfig_dcm, ... 'DisplayStyle', 'datatip', ... 'SnapToDataVertex', 'off', ... - 'Enable', 'off', ... + 'Enable', 'on', ... 'UpdateFcn', @cb_scatter_dcm); % draw the crosshair for the first time @@ -794,14 +852,14 @@ function cb_scatterredraw(h, eventdata) if opt.redrawscatter delete(findobj('type', 'scatter')); % remove previous scatters - msize = round(2000/opt.mri{1}.dim(3)); % headsize (20 cm) / z slices + msize = round(2000/opt.mri{opt.currmri}.dim(3)); % headsize (20 cm) / z slices inc = abs(opt.slim(2)-opt.slim(1))/4; % color increments for r = 1:4 % 4 color layers to encode peaks lim1 = opt.slim(1) + r*inc - inc; lim2 = opt.slim(1) + r*inc; voxind = find(opt.ana>lim1 & opt.ana0 % magnetize - opt = magnetize(opt); +if strcmp(opt.method, 'volume') + curr_ax = get(h, 'currentaxes'); + tag = get(curr_ax, 'tag'); + if ~isempty(tag) && ~opt.init + pos = mean(get(curr_ax, 'currentpoint')); + if strcmp(tag, 'ik') + opt.pos([1 3]) = pos([1 3]); + opt.update = [1 1 1]; + elseif strcmp(tag, 'ij') + opt.pos([1 2]) = pos([1 2]); + opt.update = [1 1 1]; + elseif strcmp(tag, 'jk') + opt.pos([2 3]) = pos([2 3]); + opt.update = [1 1 1]; + end + opt.pos = min(opt.pos(:)', opt.axis([2 4 6])); % avoid out-of-bounds + opt.pos = max(opt.pos(:)', opt.axis([1 3 5])); + end + if opt.magradius>0 % magnetize + opt = magnetize(opt); + end +elseif strcmp(opt.method, 'headshape') + h2 = get(gca, 'children'); % get the object handles + iscorrect = false(size(h2)); + for i=1:length(h2) % select the correct objects + try + pos = get(h2(i),'vertices'); + tri = get(h2(i),'faces'); + if ~isempty(opt.headshape) && isequal(opt.headshape.pos, pos) && isequal(opt.headshape.tri, tri) + % it is the same object that the user has plotted before + iscorrect(i) = true; + elseif isempty(opt.headshape) + % assume that it is the same object that the user has plotted before + iscorrect(i) = true; + end + end + end + h2 = h2(iscorrect); + opt.pos = select3d(h2)'; % enforce column direction + if ~isempty(opt.pos) + delete(findobj(h,'Type','Line','Marker','+','Color',[0 0 0])) % remove previous crosshairs + hold on; plot3(opt.pos(:,1),opt.pos(:,2),opt.pos(:,3), 'marker', '+', 'linestyle', 'none', 'color', [0 0 0]); % plot the crosshair + end + %opt.pos = ft_select_point3d(opt.headshape, 'nearest', true, 'multiple', false, 'marker', '+'); % FIXME: this gets stuck in a loop waiting for any abritrary buttonpress end setappdata(h, 'opt', opt); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function cb_cleanup(h, eventdata) +function cb_quit(h, eventdata) opt = getappdata(h, 'opt'); if isfield(opt, 'scatterfig') @@ -1100,33 +1224,6 @@ function cb_cleanup(h, eventdata) p = get(h, 'parent'); end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character;d - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1208,7 +1305,11 @@ function cb_eleclistbox(h6, eventdata) set(h6, 'ListboxTop', listtopidx); % ensure listbox does not move upon label selec opt.redrawmarker = 1; setappdata(h, 'opt', opt); - cb_redraw(h); + if strcmp(opt.method, 'volume') + cb_redraw(h); + elseif strcmp(opt.method, 'headshape') + cb_headshaperedraw(h); + end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1220,7 +1321,7 @@ function cb_magnetbutton(h7, eventdata) opt = getappdata(h, 'opt'); radii = get(h7, 'String'); opt.magradius = str2double(radii{get(h7, 'value')}); -fprintf(' changed magnet radius to %.1f %s\n', opt.magradius, opt.mri{1}.unit); +fprintf(' changed magnet radius to %.1f %s\n', opt.magradius, opt.mri{opt.currmri}.unit); setappdata(h, 'opt', opt); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1230,7 +1331,7 @@ function cb_magnetbutton(h7, eventdata) try pos = opt.pos; - vox = round(ft_warp_apply(inv(opt.mri{1}.transform), pos)); % head to vox coord (for indexing within anatomy) + vox = round(ft_warp_apply(inv(opt.mri{opt.currmri}.transform), pos)); % head to vox coord (for indexing within anatomy) xsel = vox(1)+(-opt.magradius:opt.magradius); ysel = vox(2)+(-opt.magradius:opt.magradius); zsel = vox(3)+(-opt.magradius:opt.magradius); @@ -1251,7 +1352,7 @@ function cb_magnetbutton(h7, eventdata) ix = (X(:)' * cubic(:)); iy = (Y(:)' * cubic(:)); iz = (Z(:)' * cubic(:)); - elseif strcmp(opt.magtype, 'peakweighted') + elseif strcmp(opt.magtype, 'peakweighted') % find the peak intensity voxel and then the center of mass [val, idx] = max(cubic(:)); [ix, iy, iz] = ind2sub(size(cubic), idx); @@ -1262,11 +1363,11 @@ function cb_magnetbutton(h7, eventdata) cubic = opt.ana(xsel, ysel, zsel); dim = size(cubic); [X, Y, Z] = ndgrid(1:dim(1), 1:dim(2), 1:dim(3)); - cubic = cubic./sum(cubic(:)); + cubic = cubic./sum(cubic(:)); ix = (X(:)' * cubic(:)); iy = (Y(:)' * cubic(:)); iz = (Z(:)' * cubic(:)); - elseif strcmp(opt.magtype, 'troughweighted') + elseif strcmp(opt.magtype, 'troughweighted') % find the peak intensity voxel and then the center of mass [val, idx] = min(cubic(:)); [ix, iy, iz] = ind2sub(size(cubic), idx); @@ -1278,16 +1379,16 @@ function cb_magnetbutton(h7, eventdata) dim = size(cubic); [X, Y, Z] = ndgrid(1:dim(1), 1:dim(2), 1:dim(3)); cubic = 1-cubic; - cubic = cubic./(sum(cubic(:))); + cubic = cubic./(sum(cubic(:))); ix = (X(:)' * cubic(:)); iy = (Y(:)' * cubic(:)); iz = (Z(:)' * cubic(:)); end % adjust the indices for the selection - voxadj = [ix, iy, iz] + vox - opt.magradius - 1; - opt.pos = ft_warp_apply(opt.mri{1}.transform, voxadj); + voxadj = [ix, iy, iz] + vox - opt.magradius - 1; + opt.pos = ft_warp_apply(opt.mri{opt.currmri}.transform, voxadj); fprintf('==================================================================================\n'); - fprintf(' clicked at [%.1f %.1f %.1f], %s magnetized adjustment [%.1f %.1f %.1f] %s\n', pos, opt.magtype, opt.pos-pos, opt.mri{1}.unit); + fprintf(' clicked at [%.1f %.1f %.1f], %s magnetized adjustment [%.1f %.1f %.1f] %s\n', pos, opt.magtype, opt.pos-pos, opt.mri{opt.currmri}.unit); catch % this fails if the selection is at the edge of the volume end @@ -1302,7 +1403,11 @@ function cb_labelsbutton(h8, eventdata) opt.showlabels = get(h8, 'value'); opt.redrawmarker = 1; setappdata(h, 'opt', opt); -cb_redraw(h); +if strcmp(opt.method, 'volume') + cb_redraw(h); +elseif strcmp(opt.method, 'headshape') + cb_headshaperedraw(h); +end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION @@ -1386,10 +1491,10 @@ function cb_scattermaxslider(h3, eventdata) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function dcm_txt = cb_scatter_dcm(hObject, eventdata) -h = findobj('type', 'figure', 'name',mfilename); +h = findobj('type', 'figure', 'name', mfilename); opt = getappdata(h, 'opt'); opt.pos = get(eventdata, 'Position'); % current datamarker position -if opt.magradius>0 % magnetize +if strcmp(opt.method, 'volume') && opt.magradius>0 % magnetize opt = magnetize(opt); end dcm_txt = ['']; % ['index = [' num2str(opt.pos) ']']; @@ -1397,7 +1502,11 @@ function cb_scattermaxslider(h3, eventdata) if strcmp(get(opt.scatterfig_dcm, 'Enable'), 'on') % update appl and figures setappdata(h, 'opt', opt); figure(h); - cb_redraw(h); + if strcmp(opt.method, 'volume') + cb_redraw(h); + elseif strcmp(opt.method, 'headshape') + cb_headshaperedraw(h); + end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1407,21 +1516,21 @@ function cb_skullstrip(hObject, eventdata) h = findobj('type', 'figure', 'name',mfilename); opt = getappdata(h, 'opt'); -if get(hObject, 'value') && ~isfield(opt.mri{1}, 'dat_strip') % skullstrip +if get(hObject, 'value') && ~isfield(opt.mri{opt.currmri}, 'dat_strip') % skullstrip fprintf('stripping the skull - this could take a few minutes\n') - tmp = keepfields(opt.mri{1}, {'anatomy', 'dim', 'coordsys', 'unit', 'transform'}); + tmp = keepfields(opt.mri{opt.currmri}, {'anatomy', 'dim', 'coordsys', 'unit', 'transform'}); cfg = []; cfg.output = 'skullstrip'; seg = ft_volumesegment(cfg, tmp); dmin = min(seg.anatomy(:)); dmax = max(seg.anatomy(:)); - opt.mri{1}.dat_strip = (seg.anatomy-dmin)./(dmax-dmin); % range between 0 and 1 - opt.ana = opt.mri{1}.dat_strip; % overwrite with skullstrip + opt.mri{opt.currmri}.dat_strip = (seg.anatomy-dmin)./(dmax-dmin); % range between 0 and 1 + opt.ana = opt.mri{opt.currmri}.dat_strip; % overwrite with skullstrip clear seg tmp dmin dmax -elseif ~get(hObject, 'value') && isfield(opt.mri{1}, 'dat_strip') % use original again - opt.ana = opt.mri{1}.dat; -elseif get(hObject, 'value') && isfield(opt.mri{1}, 'dat_strip') % use skullstrip again - opt.ana = opt.mri{1}.dat_strip; +elseif ~get(hObject, 'value') && isfield(opt.mri{opt.currmri}, 'dat_strip') % use original again + opt.ana = opt.mri{opt.currmri}.dat; +elseif get(hObject, 'value') && isfield(opt.mri{opt.currmri}, 'dat_strip') % use skullstrip again + opt.ana = opt.mri{opt.currmri}.dat_strip; end opt.redrawscatter = 1; setappdata(h, 'opt', opt); @@ -1437,16 +1546,400 @@ function cb_scanbutton(hscan, eventdata) opt = getappdata(h, 'opt'); opt.scan = get(hscan, 'value'); -opt.mri{1}.clim = opt.clim; % store current clim -opt.mri{1}.slim = opt.slim; % store current scatter clim -opt.mri = flip(opt.mri); % switch mri order -opt.ana = opt.mri{1}.dat; -opt.clim = opt.mri{1}.clim; % use other scan's clim +opt.mri{opt.currmri}.clim = opt.clim; % store current scan's clim +opt.mri{opt.currmri}.slim = opt.slim; % store current scan's scatter clim + +opt.currmri = opt.currmri+1; % rotate to next scan +if opt.currmri>numel(opt.mri) + opt.currmri = 1; % restart at 1 +end +opt.ana = opt.mri{opt.currmri}.dat; +opt.clim = opt.mri{opt.currmri}.clim; % use next scan's clim set(opt.axes(4), 'Value', opt.clim(1)); % update minslider set(opt.axes(5), 'Value', opt.clim(2)); % update maxslider -opt.slim = opt.mri{1}.slim; % use other scan's scatter clim -opt.axes(1:3) = opt.mri{1}.axes; +opt.slim = opt.mri{opt.currmri}.slim; % use next scan's scatter clim +opt.axes(1:3) = opt.mri{opt.currmri}.axes; opt.init = true; setappdata(h, 'opt', opt); cb_redraw(h); + + + + +% FIXME: this function is located in plotting/private, hence not accessible +% to ft_electrodeplacement +function [pout, vout, viout, facevout, faceiout] = select3d(obj) +%SELECT3D(H) Determines the selected point in 3-D data space. +% P = SELECT3D determines the point, P, in data space corresponding +% to the current selection position. P is a point on the first +% patch or surface face intersected along the selection ray. If no +% face is encountered along the selection ray, P returns empty. +% +% P = SELECT3D(H) constrains selection to graphics handle H and, +% if applicable, any of its children. H can be a figure, axes, +% patch, or surface object. +% +% [P V] = SELECT3D(...), V is the closest face or line vertex +% selected based on the figure's current object. +% +% [P V VI] = SELECT3D(...), VI is the index into the object's +% x,y,zdata properties corresponding to V, the closest face vertex +% selected. +% +% [P V VI FACEV] = SELECT3D(...), FACE is an array of vertices +% corresponding to the face polygon containing P and V. +% +% [P V VI FACEV FACEI] = SELECT3D(...), FACEI is the row index into +% the object's face array corresponding to FACE. For patch +% objects, the face array can be obtained by doing +% get(mypatch,'faces'). For surface objects, the face array +% can be obtained from the output of SURF2PATCH (see +% SURF2PATCH for more information). +% +% RESTRICTIONS: +% SELECT3D supports surface, patch, or line object primitives. For surface +% and patches, the algorithm assumes non-self-intersecting planar faces. +% For line objects, the algorithm always returns P as empty, and V will +% be the closest vertex relative to the selection point. +% +% Example: +% +% h = surf(peaks); +% zoom(10); +% disp('Click anywhere on the surface, then hit return') +% pause +% [p v vi face facei] = select3d; +% marker1 = line('xdata',p(1),'ydata',p(2),'zdata',p(3),'marker','o',... +% 'erasemode','xor','markerfacecolor','k'); +% marker2 = line('xdata',v(1),'ydata',v(2),'zdata',v(3),'marker','o',... +% 'erasemode','xor','markerfacecolor','k'); +% marker2 = line('erasemode','xor','xdata',face(1,:),'ydata',face(2,:),... +% 'zdata',face(3,:),'linewidth',10); +% disp(sprintf('\nYou clicked at\nX: %.2f\nY: %.2f\nZ: %.2f',p(1),p(2),p(3)')) +% disp(sprintf('\nThe nearest vertex is\nX: %.2f\nY: %.2f\nZ: %.2f',v(1),v(2),v(3)')) +% +% Version 1.2 2-15-02 +% Copyright Joe Conti 2002 +% Send comments to jconti@mathworks.com +% +% See also GINPUT, GCO. + +% Output variables +pout = []; +vout = []; +viout = []; +facevout = []; +faceiout = []; + +% other variables +ERRMSG = 'Input argument must be a valid graphics handle'; +isline = false; +isperspective = false; + +% Parse input arguments +if nargin<1 + obj = gco; +end + +if isempty(obj) || ~ishandle(obj) || length(obj)~=1 + ft_error(ERRMSG); +end + +% if obj is a figure +if strcmp(get(obj,'type'),'figure') + fig = obj; + ax = get(fig,'currentobject'); + currobj = get(fig,'currentobject'); + + % bail out if not a child of the axes + if ~strcmp(get(get(currobj,'parent'),'type'),'axes') + return; + end + + % if obj is an axes +elseif strcmp(get(obj,'type'),'axes') + ax = obj; + fig = get(ax,'parent'); + currobj = get(fig,'currentobject'); + currax = get(currobj,'parent'); + + % Bail out if current object is under an unspecified axes + if ~isequal(ax,currax) + return; + end + + % if obj is child of axes +elseif strcmp(get(get(obj,'parent'),'type'),'axes') + currobj = obj; + ax = get(obj,'parent'); + fig = get(ax,'parent'); + + % Bail out +else + return +end + +axchild = currobj; +obj_type = get(axchild,'type'); +is_perspective = strcmp(get(ax,'projection'),'perspective'); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Get projection transformation %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% syntax not supported in old versions of MATLAB +[a b] = view(ax); +xform = viewmtx(a,b); +if is_perspective + ft_warning('%s does not support perspective axes projection.',mfilename); + d = norm(camtarget(ax)-campos(ax)) + P = [1 0 0 0; + 0 1 0 0; + 0 0 1 0; + 0 0 -1/d 1]; + xform = P*xform; +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Get vertex, face, and current point data %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +cp = get(ax,'currentpoint')'; + +% If surface object +if strcmp(obj_type,'surface') + % Get surface face and vertices + fv = surf2patch(axchild); + vert = fv.vertices; + faces = fv.faces; + + % If patch object +elseif strcmp(obj_type,'patch') + vert = get(axchild,'vertices'); + faces = get(axchild,'faces'); + + % If line object +elseif strcmp(obj_type,'line') + xdata = get(axchild,'xdata'); + ydata = get(axchild,'ydata'); + zdata = get(axchild,'zdata'); + vert = [xdata', ydata',zdata']; + faces = []; + isline = true; + + % Ignore all other objects +else + return; +end + +% Add z if empty +if size(vert,2)==2 + vert(:,3) = zeros(size(vert(:,2))); + if isline + zdata = vert(:,3); + end +end + +% NaN and Inf check +nan_inf_test1 = isnan(faces) | isinf(faces); +nan_inf_test2 = isnan(vert) | isinf(vert); +if any(nan_inf_test1(:)) || any(nan_inf_test2(:)) + ft_warning('%s does not support NaNs or Infs in face/vertex data.',mfilename); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Normalize for data aspect ratio %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +dar = get(ax,'DataAspectRatio'); + +ncp(1,:) = cp(1,:)./dar(1); +ncp(2,:) = cp(2,:)./dar(2); +ncp(3,:) = cp(3,:)./dar(3); +ncp(4,:) = ones(size(ncp(3,:))); + +nvert(:,1) = vert(:,1)./dar(1); +nvert(:,2) = vert(:,2)./dar(2); +nvert(:,3) = vert(:,3)./dar(3); +nvert(:,4) = ones(size(nvert(:,3))); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Transform data to view space %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +xvert = xform*nvert'; +xcp = xform*ncp; + +if is_perspective % normalize 4th dimension + xcp(1,:) = xcp(1,:)./xcp(4,:); + xcp(2,:) = xcp(2,:)./xcp(4,:); + xcp(3,:) = xcp(3,:)./xcp(4,:); + xcp(4,:) = xcp(4,:)./xcp(4,:); + + xvert(1,:) = xvert(1,:)./xvert(4,:); + xvert(2,:) = xvert(2,:)./xvert(4,:); + xvert(3,:) = xvert(3,:)./xvert(4,:); + xvert(4,:) = xvert(4,:)./xvert(4,:); +end + +% Ignore 3rd & 4th dimensions for crossing test +xvert(4,:) = []; +xvert(3,:) = []; +xcp(4,:) = []; +xcp(3,:) = []; + +% For debugging +% if 0 +% ax1 = getappdata(ax,'testselect3d'); +% if isempty(ax1) | ~ishandle(ax1) +% fig = figure; +% ax1 = axes; +% axis(ax1,'equal'); +% setappdata(ax,'testselect3d',ax1); +% end +% cla(ax1); +% patch('parent',ax1,'faces',faces,'vertices',xvert','facecolor','none','edgecolor','k'); +% line('parent',ax1,'xdata',xcp(1,2),'ydata',xcp(2,2),'zdata',0,'marker','o','markerfacecolor','r','erasemode','xor'); +% end + +% Translate vertices so that the selection point is at the origin. +xvert(1,:) = xvert(1,:) - xcp(1,2); +xvert(2,:) = xvert(2,:) - xcp(2,2); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% simple algorithm (almost naive algorithm!) for line objects %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if isline + + % Ignoring line width and marker attributes, find closest + % vertex in 2-D view space. + d = xvert(1,:).*xvert(1,:) + xvert(2,:).*xvert(2,:); + [val i] = min(d); + i = i(1); % enforce only one output + + % Assign output + vout = [ xdata(i) ydata(i) zdata(i)]; + viout = i; + + return % Bail out early +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Perform 2-D crossing test (Jordan Curve Theorem) %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Find all vertices that have y components less than zero +vert_with_negative_y = zeros(size(faces)); +face_y_vert = xvert(2,faces); +ind_vert_with_negative_y = find(face_y_vert<0); +vert_with_negative_y(ind_vert_with_negative_y) = true; + +% Find all the line segments that span the x axis +is_line_segment_spanning_x = abs(diff([vert_with_negative_y, vert_with_negative_y(:,1)],1,2)); + +% Find all the faces that have line segments that span the x axis +ind_is_face_spanning_x = find(any(is_line_segment_spanning_x,2)); + +% Ignore data that doesn't span the x axis +candidate_faces = faces(ind_is_face_spanning_x,:); +vert_with_negative_y = vert_with_negative_y(ind_is_face_spanning_x,:); +is_line_segment_spanning_x = is_line_segment_spanning_x(ind_is_face_spanning_x,:); + +% Create line segment arrays +pt1 = candidate_faces; +pt2 = [candidate_faces(:,2:end), candidate_faces(:,1)]; + +% Point 1 +x1 = reshape(xvert(1,pt1),size(pt1)); +y1 = reshape(xvert(2,pt1),size(pt1)); + +% Point 2 +x2 = reshape(xvert(1,pt2),size(pt2)); +y2 = reshape(xvert(2,pt2),size(pt2)); + +% Cross product of vector to origin with line segment +cross_product_test = -x1.*(y2-y1) > -y1.*(x2-x1); + +% Find all line segments that cross the positive x axis +crossing_test = (cross_product_test==vert_with_negative_y) & is_line_segment_spanning_x; + +% If the number of line segments is odd, then we intersected the polygon +s = sum(crossing_test,2); +s = mod(s,2); +ind_intersection_test = find(s~=0); + +% Bail out early if no faces were hit +if isempty(ind_intersection_test) + return; +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Plane/ray intersection test %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Perform plane/ray intersection with the faces that passed +% the polygon intersection tests. Grab the only the first +% three vertices since that is all we need to define a plane). +% assuming planar polygons. +candidate_faces = candidate_faces(ind_intersection_test,1:3); +candidate_faces = reshape(candidate_faces',1,numel(candidate_faces)); +vert = vert'; +candidate_facev = vert(:,candidate_faces); +candidate_facev = reshape(candidate_facev,3,3,length(ind_intersection_test)); + +% Get three contiguous vertices along polygon +v1 = squeeze(candidate_facev(:,1,:)); +v2 = squeeze(candidate_facev(:,2,:)); +v3 = squeeze(candidate_facev(:,3,:)); + +% Get normal to face plane +vec1 = v2-v1; +vec2 = v3-v2; +crs = cross(vec1,vec2); +mag = sqrt(sum(crs.*crs)); +nplane(1,:) = crs(1,:)./mag; +nplane(2,:) = crs(2,:)./mag; +nplane(3,:) = crs(3,:)./mag; + +% Compute intersection between plane and ray +cp1 = cp(:,1); +cp2 = cp(:,2); +d = cp2-cp1; +dp = dot(-nplane,v1); + +%A = dot(nplane,d); +A(1,:) = nplane(1,:).*d(1); +A(2,:) = nplane(2,:).*d(2); +A(3,:) = nplane(3,:).*d(3); +A = sum(A,1); + +%B = dot(nplane,pt1) +B(1,:) = nplane(1,:).*cp1(1); +B(2,:) = nplane(2,:).*cp1(2); +B(3,:) = nplane(3,:).*cp1(3); +B = sum(B,1); + +% Distance to intersection point +t = (-dp-B)./A; + +% Find "best" distance (smallest) +[tbest, ind_best] = min(t); + +% Determine intersection point +pout = cp1 + tbest .* d; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Assign additional output variables %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if nargout>1 + + % Get face index and vertices + faceiout = ind_is_face_spanning_x(ind_intersection_test(ind_best)); + facevout = vert(:,faces(faceiout,:)); + + % Determine index of closest face vertex intersected + facexv = xvert(:,faces(faceiout,:)); + dist = sqrt(facexv(1,:).*facexv(1,:) + facexv(2,:).*facexv(2,:)); + min_index = (dist==min(dist)); + + % Get closest vertex index and vertex + viout = faces(faceiout,min_index); + vout = vert(:,viout); +end diff --git a/external/fieldtrip/ft_electroderealign.m b/external/fieldtrip/ft_electroderealign.m index 2a3de940..2287a572 100644 --- a/external/fieldtrip/ft_electroderealign.m +++ b/external/fieldtrip/ft_electroderealign.m @@ -55,7 +55,8 @@ % 'nonlin4' apply a 4th order non-linear warp % 'nonlin5' apply a 5th order non-linear warp % 'dykstra2012' non-linear wrap only for headshape method, useful for projecting ECoG onto cortex hull -% 'fsaverage' surface-based realignment with the freesurfer fsaverage brain +% 'fsaverage' surface-based realignment with FreeSurfer fsaverage brain +% 'fsinflated' surface-based realignment with FreeSurfer individual subject inflated brain % cfg.channel = Nx1 cell-array with selection of channels (default = 'all'), % see FT_CHANNELSELECTION for details % cfg.keepchannel = string, 'yes' or 'no' (default = 'no') @@ -90,12 +91,11 @@ % points % % If you want to align ECoG electrodes to the pial surface, you first need to compute -% the cortex hull with FT_PREPARE_MESH. dykstra2012 uses algorithm described in -% Dykstra et al. (2012, Neuroimage) in which electrodes are projected onto pial -% surface while minimizing the displacement of the electrodes from original location -% and maintaining the grid shape. It relies on the optimization toolbox. +% the cortex hull with FT_PREPARE_MESH. Then use either the algorithm described in +% Dykstra et al. (2012, Neuroimage) or in Hermes et al. (2010, J Neurosci methods) to +% snap the electrodes back to the cortical hull, e.g. % cfg.method = 'headshape' -% cfg.warp = 'dykstra2012' +% cfg.warp = 'dykstra2012', or 'hermes2010' % cfg.headshape = a filename containing headshape, a structure containing a % single triangulated boundary, or a Nx3 matrix with surface % points @@ -114,7 +114,7 @@ % rather than surface_pial_left.mat. % cfg.method = 'headshape' % cfg.warp = 'fsaverage' -% cfg.headshape = string, filename containing headshape (e.g. ) +% cfg.headshape = string, filename containing subject headshape (e.g. ) % cfg.fshome = string, path to freesurfer % % See also FT_READ_SENS, FT_VOLUMEREALIGN, FT_INTERACTIVEREALIGN, @@ -404,31 +404,12 @@ norm.label = elec.label; if strcmp(cfg.warp, 'dykstra2012') norm.elecpos = warp_dykstra2012(cfg, elec, headshape); + elseif strcmp(cfg.warp, 'hermes2010') + norm.elecpos = warp_hermes2010(cfg, elec, headshape); elseif strcmp(cfg.warp, 'fsaverage') - subj_pial = ft_read_headshape(cfg.headshape); - [PATHSTR, NAME] = fileparts(cfg.headshape); % lh or rh - subj_reg = ft_read_headshape([PATHSTR filesep NAME '.sphere.reg']); - if ~isdir([cfg.fshome filesep 'subjects' filesep 'fsaverage' filesep 'surf']) - ft_error(['freesurfer dir ' cfg.fshome filesep 'subjects' filesep 'fsaverage' filesep 'surf cannot be found']) - end - fsavg_pial = ft_read_headshape([cfg.fshome filesep 'subjects' filesep 'fsaverage' filesep 'surf' filesep NAME '.pial']); - fsavg_reg = ft_read_headshape([cfg.fshome filesep 'subjects' filesep 'fsaverage' filesep 'surf' filesep NAME '.sphere.reg']); - - for e = 1:numel(elec.label) - % subject space (3D surface): electrode pos -> vertex index - dist = sqrt(sum(((subj_pial.pos - repmat(elec.elecpos(e,:), size(subj_pial.pos,1), 1)).^2),2)); - [~, minidx] = min(dist); - - % intersubject space (2D sphere): vertex index -> vertex pos -> template vertex index - dist2 = sqrt(sum(((fsavg_reg.pos - repmat(subj_reg.pos(minidx,:), size(fsavg_reg.pos,1), 1)).^2),2)); - [~, minidx2] = min(dist2); - clear minidx - - % template space (3D surface): template vertex index -> template electrode pos - norm.elecpos(e,:) = fsavg_pial.pos(minidx2,:); - clear minidx2 - end - clear subj_pial subj_reg fsavg_pial fsavg_reg + norm.elecpos = warp_fsaverage(cfg, elec); + elseif strcmp(cfg.warp, 'fsinflated') + norm.elecpos = warp_fsinflated(cfg, elec); else fprintf('warping electrodes to skin surface... '); % the newline comes later [norm.elecpos, norm.m] = ft_warp_optim(elec.elecpos, headshape, cfg.warp); @@ -638,7 +619,7 @@ % electrode labels by their case-sensitive original values switch cfg.method case {'template', 'headshape'} - if strcmpi(cfg.warp, 'dykstra2012') || strcmpi(cfg.warp, 'fsaverage') + if strcmpi(cfg.warp, 'dykstra2012') || strcmpi(cfg.warp, 'hermes2010') || strcmpi(cfg.warp, 'fsaverage') || strcmpi(cfg.warp, 'fsinflated') elec_realigned = norm; elec_realigned.label = label_original; else @@ -690,7 +671,7 @@ elec_realigned.coordsys = headshape.coordsys; end if isfield(elec_original, 'coordsys') - if strcmp(cfg.warp, 'dykstra2012') % this warp simply moves the electrodes in the same coordinate space + if strcmp(cfg.warp, 'dykstra2012') || strcmp(cfg.warp, 'hermes2010') % this warp simply moves the electrodes in the same coordinate space elec_realigned.coordsys = elec_original.coordsys; elseif strcmp(cfg.warp, 'fsaverage') elec_realigned.coordsys = 'fsaverage'; @@ -713,7 +694,7 @@ if istrue(cfg.keepchannel) % append the channels that are not realigned - [~, idx] = setdiff(elec_original.label, elec_realigned.label); + [dum, idx] = setdiff(elec_original.label, elec_realigned.label); idx = sort(idx); elec_realigned.label = [elec_realigned.label; elec_original.label(idx)]; elec_realigned.elecpos = [elec_realigned.elecpos; elec_original.elecpos(idx,:)]; @@ -721,16 +702,20 @@ % channel positions are identical to the electrode positions (this was checked at the start) elec_realigned.chanpos = elec_realigned.elecpos; +elec_realigned.tra = eye(numel(elec_realigned.label)); -% copy over unit, chantype, and chanunit information in case this was not already done +% copy over unit, chantype, chanunit, and tra information in case this was not already done if ~isfield(elec_realigned, 'unit') && isfield(elec_original, 'unit') elec_realigned.unit = elec_original.unit; end if ~isfield(elec_realigned, 'chantype') && isfield(elec_original, 'chantype') - elec_realigned.chantype = elec_original.chantype; + idx = match_str(elec_original.label, elec_realigned.label); + elec_realigned.chantype = elec_original.chantype(idx); end if ~isfield(elec_realigned, 'chanunit') && isfield(elec_original, 'chanunit') elec_realigned.chanunit = elec_original.chanunit; + idx = match_str(elec_original.label, elec_realigned.label); + elec_realigned.chanunit = elec_original.chanunit(idx); end % update it to the latest version diff --git a/external/fieldtrip/ft_electrodermalactivity.m b/external/fieldtrip/ft_electrodermalactivity.m new file mode 100644 index 00000000..6c2a110d --- /dev/null +++ b/external/fieldtrip/ft_electrodermalactivity.m @@ -0,0 +1,133 @@ +function [dataout] = ft_electrodermalactivity(cfg, datain) + +% FT_ELECTRODERMALACTIVITY estimates the electrodermal activity from a recording of +% the electric resistance of the skin. +% +% Use as +% eda = ft_electrodermalactivity(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. +% +% The configuration structure has the following options +% cfg.channel = selected channel for processing, see FT_CHANNELSELECTION +% cfg.feedback = 'yes' or 'no' +% cfg.medianwindow = scalar, length of window for median filter in seconds (default = 8) +% +% After using this function you can use FT_REDEFINETRIAL and FT_TIMELOCKANLAYSIS to +% investigate electrodermal responses (EDRs) to stimulation. You can use +% FT_ARTIFACT_THRESHOLD to determine the timing and frequency of nonspecific EDRs. +% +% See https://doi.org/10.1111/j.1469-8986.2012.01384.x "Publication recommendations +% for electrodermal measurements" by the SPR for an introduction in electrodermal +% methods and for recommendations. +% +% See also FT_HEARTRATE, FT_HEADMOVEMENT, FT_REGRESSCONFOUND + +% Copyright (C) 2018, Robert Oostenveld, DCCN +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% the initial part deals with parsing the input options and data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% these are used by the ft_preamble/ft_postamble function and scripts +ft_revision = '$Id$'; +ft_nargin = nargin; +ft_nargout = nargout; + +% the ft_preamble function works by calling a number of scripts from +% fieldtrip/utility/private that are able to modify the local workspace + +ft_defaults +ft_preamble init +ft_preamble debug +ft_preamble loadvar datain +ft_preamble provenance datain +ft_preamble trackconfig + +% the ft_abort variable is set to true or false in ft_preamble_init +if ft_abort + % do not continue function execution in case the outputfile is present and the user indicated to keep it + return +end + +% check if the input data is valid for this function, the input data must be raw +datain = ft_checkdata(datain, 'datatype', 'raw', 'feedback', 'yes'); + +% set the default options +cfg.channel = ft_getopt(cfg, 'channel', {}); +cfg.feedback = ft_getopt(cfg, 'feedback', 'yes'); +cfg.medianwindow = ft_getopt(cfg, 'medianwindow', 8); % in seconds +cfg.preproc = ft_getopt(cfg, 'preproc', []); + +% copy some of the fields over to the new data structure +dataout = keepfields(datain, {'time', 'fsample', 'sampleinfo', 'trialinfo'}); +dataout.label = {'tonic', 'phasic'}; +dataout.trial = {}; % this is to be determined in the main code + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% the actual computation is done in the middle part +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +cfg.channel = ft_channelselection(cfg.channel, datain.label); +assert(numel(cfg.channel)==1, 'you should specify exactly one channel'); + +fsample = datain.fsample; +chansel = strcmp(datain.label, cfg.channel{1}); +medianwindow = round(cfg.medianwindow*fsample); % in samples + +for trllop=1:numel(datain.trial) + dat = datain.trial{trllop}(chansel,:); + label = datain.label(chansel); + time = datain.time{trllop}; + + if ~isempty(cfg.preproc) + % apply the preprocessing to the selected channel + [dat, label, time, cfg.preproc] = preproc(dat, label, time, cfg.preproc, 0, 0); + end + + tonic = ft_preproc_medianfilter(dat, medianwindow); + phasic = dat - tonic; + + if istrue(cfg.feedback) + figure + subplot(3,1,1) + plot(time, dat) + title('preprocessed') + subplot(3,1,2) + plot(time, tonic) + title('tonic') + subplot(3,1,3) + plot(time, phasic) + title('phasic') + end + + dataout.trial{trllop} = [tonic; phasic]; +end % for trllop + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% deal with the output +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ft_postamble debug +ft_postamble trackconfig +ft_postamble previous datain +ft_postamble provenance dataout +ft_postamble history dataout +ft_postamble savevar dataout diff --git a/external/fieldtrip/ft_freqstatistics.m b/external/fieldtrip/ft_freqstatistics.m index 41c91445..c5474d97 100644 --- a/external/fieldtrip/ft_freqstatistics.m +++ b/external/fieldtrip/ft_freqstatistics.m @@ -79,6 +79,7 @@ ft_preamble loadvar varargin ft_preamble provenance varargin ft_preamble trackconfig +ft_preamble randomseed % the ft_abort variable is set to true or false in ft_preamble_init if ft_abort @@ -164,13 +165,13 @@ design = cfg.design; -% determine the function handle to the intermediate-level statistics function -if exist(['ft_statistics_' cfg.method], 'file') - statmethod = str2func(['ft_statistics_' cfg.method]); -else +% fetch function handle to the intermediate-level statistics function +statmethod = ft_getuserfun(cfg.method, 'statistics'); +if isempty(statmethod) ft_error('could not find the corresponding function for cfg.method="%s"\n', cfg.method); +else + fprintf('using "%s" for the statistical testing\n', func2str(statmethod)); end -fprintf('using "%s" for the statistical testing\n', func2str(statmethod)); % check that the design completely describes the data if size(dat,2) ~= size(cfg.design,2) @@ -186,22 +187,11 @@ end % perform the statistical test -if strcmp(func2str(statmethod),'ft_statistics_montecarlo') - % because ft_statistics_montecarlo (or to be precise, clusterstat) requires to know whether it is getting source data, - % the following (ugly) work around is necessary - if num>1 - [stat, cfg] = statmethod(cfg, dat, design); - cfg = rollback_provenance(cfg); % ensure that changes to the cfg are passed back to the right level - else - [stat] = statmethod(cfg, dat, design); - end +if num>1 + [stat, cfg] = statmethod(cfg, dat, design); + cfg = rollback_provenance(cfg); % ensure that changes to the cfg are passed back to the right level else - if num>1 - [stat, cfg] = statmethod(cfg, dat, design); - cfg = rollback_provenance(cfg); % ensure that changes to the cfg are passed back to the right level - else - [stat] = statmethod(cfg, dat, design); - end + [stat] = statmethod(cfg, dat, design); end if ~isstruct(stat) @@ -223,13 +213,14 @@ stat.dimord = cfg.dimord; % copy the descripive fields into the output -stat = copyfields(varargin{1}, stat, {'freq', 'time', 'label'}); +stat = copyfields(varargin{1}, stat, {'freq', 'time', 'label', 'elec', 'grad', 'opto'}); % these were only present to inform the low-level functions cfg = removefields(cfg, {'dim', 'dimord'}); % do the general cleanup and bookkeeping at the end of the function ft_postamble debug +ft_postamble randomseed ft_postamble trackconfig ft_postamble previous varargin ft_postamble provenance stat diff --git a/external/fieldtrip/ft_headmovement.m b/external/fieldtrip/ft_headmovement.m index 479dd0e5..34373025 100644 --- a/external/fieldtrip/ft_headmovement.m +++ b/external/fieldtrip/ft_headmovement.m @@ -1,22 +1,59 @@ -function [grad] = ft_headmovement(cfg) +function [varargout] = ft_headmovement(cfg) -% FT_HEADMOVEMENT creates a representation of the CTF gradiometer definition -% in which the headmovement in incorporated. The result can be used -% for source reconstruction. +% FT_HEADMOVEMENT creates a raw data structure, or cell-array of datastructures +% containing the HLC-coil data, which have a grad structure that has the +% head position information incorporated. % % Use as -% grad = ft_headmovement(cfg) +% data = ft_headmovement(cfg) +% % where the configuration should contain % cfg.dataset = string with the filename -% cfg.trl = Nx3 matrix with the trial definition, see FT_DEFINETRIAL -% cfg.numclusters = number of segments with constant headposition in which to split the data (default = 12) +% cfg.method = 'updatesens' (default), 'cluster', 'avgoverrpt', +% 'pertrial_cluster', 'pertrial' +% +% optional arguments are +% cfg.trl = empty (default), or Nx3 matrix with the trial +% definition, can be empty.see FT_DEFINETRIAL. If +% defined empty, the whole recording is used +% cfg.numclusters = number of segments with constant headposition in +% which to split the data (default = 10). This +% argument is used in some of the methods only (see +% below), and is used in a kmeans clustering scheme. +% +% If cfg.method = 'updatesens', the grad in the single output structure has +% a specification of the coils expanded as per the centroids of the position +% clusters. The balancing matrix is s a weighted concatenation of the +% original tra-matrix. This method requires cfg.numclusters to be specified +% +% If cfg.method = 'avgoverrpt', the grad in the single output structure has +% a specification of the coils according to the average head position +% across the specified samples. % -% This method and related methods are described by Stolk et al., Online and +% If cfg.method = 'cluster', the cell-array of output structures represent +% the epochs in which the head was considered to be positioned close to the +% corresponding kmeans-cluster's centroid. The corresponding grad-structure +% is specified according to this cluster's centroid. This method requires +% cfg.numclusters to be specified. +% +% If cfg.method = 'pertrial', the cell-array of output structures contains +% single trials, each trial with a trial-specific grad structure. Note that +% this is extremely memory inefficient with large numbers of trials, and +% probably an overkill. +% +% If cfg.method = 'pertrial_clusters', the cell-array of output structures +% contains sets of trials where the trial-specific head position was +% considered to be positioned close to the corresponding kmeans-cluster's +% centroid. The corresponding grad-structure is specified accordin to the +% cluster's centroid. This method requires cfg.numclusters to be specified. +% +% The updatesens method and related methods are described by Stolk et al., Online and % offline tools for head movement compensation in MEG. NeuroImage, 2012. % % See also FT_REGRESSCONFOUND FT_REALTIME_HEADLOCALIZER % Copyright (C) 2008-2017, Jan-Mathijs Schoffelen, Robert Oostenveld +% Copyright (C) 2018, Jan-Mathijs Schoffelen % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -54,67 +91,141 @@ end % check if the input cfg is valid for this function +% FIXME: consider allowing a data structure as input. cfg = ft_checkconfig(cfg, 'dataset2files', 'yes'); % set the defaults -cfg.numclusters = ft_getopt(cfg, 'numclusters', 12); -cfg.feedback = ft_getopt(cfg, 'feedback', 'yes'); +cfg.method = ft_getopt(cfg, 'method', 'updatesens'); % 'pertrial', 'pertrial_cluster', 'avgoverrpt', 'cluster' +cfg.numclusters = ft_getopt(cfg, 'numclusters', 10); +cfg.feedback = ft_getopt(cfg, 'feedback', 'yes'); + +if isequal(cfg.method,'updatesens') || isequal(cfg.method, 'pertrial_cluster') || isequal(cfg.method, 'cluster') + dokmeans = true; +else + dokmeans = false; +end -% read the header information +% read the header information and check whether it's a CTF dataset with HLC +% information. hdr = ft_read_header(cfg.headerfile); assert(numel(intersect(hdr.label, {'HLC0011' 'HLC0012' 'HLC0013' 'HLC0021' 'HLC0022' 'HLC0023' 'HLC0031' 'HLC0032' 'HLC0033'}))==9, 'the data does not contain the expected head localizer channels'); -% work with gradiometers in dewar coordinates, since HLCs are also -% in dewar coords. at present I did not find the nas, lpa, rpa channels, -% which according to ctf's documentation should contain the positions -% of these channels directly (HDAC channels). FIXME grad_head = ctf2grad(hdr.orig, 0); -grad_dewar = ctf2grad(hdr.orig, 1); grad_head = ft_datatype_sens(grad_head); % ensure up-to-date sensor description (Oct 2011) +grad_dewar = ctf2grad(hdr.orig, 1); grad_dewar = ft_datatype_sens(grad_dewar); % ensure up-to-date sensor description (Oct 2011) -grad = grad_dewar; % we want to work with dewar coordinates, ... -grad.chanpos = grad_head.chanpos; % except the chanpos, which should remain in head coordinates +grad = grad_dewar; % we want to work with dewar coordinates, ... +grad.chanpos = grad_head.chanpos; % read HLC-channels % HLC0011 HLC0012 HLC0013 x, y, z coordinates of nasion-coil in m. % HLC0021 HLC0022 HLC0023 x, y, z coordinates of lpa-coil in m. % HLC0031 HLC0032 HLC0033 x, y, z coordinates of rpa-coil in m. +if ~isfield(cfg, 'trl') + cfg.trl = [1 hdr.nTrials.*hdr.nSamples 0]; +end tmpcfg = []; tmpcfg.dataset = cfg.dataset; tmpcfg.trl = cfg.trl; tmpcfg.channel = {'HLC0011' 'HLC0012' 'HLC0013' 'HLC0021' 'HLC0022' 'HLC0023' 'HLC0031' 'HLC0032' 'HLC0033'}; tmpcfg.continuous = 'yes'; data = ft_preprocessing(tmpcfg); +data = removefields(data, 'elec'); % this slows down a great + % rendering the persistent variable trick useless. + % we don't need the elec anyway -dat = cat(2, data.trial{:}); -dat = dat * ft_scalingfactor('m', grad.unit); % scale in units of the gradiometer definition +wdat = cellfun('size', data.time, 2); % weights for weighted average -[tmpdata, dum, ic] = unique(dat', 'rows'); -dat = tmpdata'; +trial_index = cell(1,numel(data.trial)); +for k = 1:numel(data.trial) + % it sometimes happens that data are numerically 0, which causes problems + % downstream, replace with nans + data.trial{k}(:,sum(data.trial{k}==0)==9) = nan; + + % create a bookkeeping cell-array, indexing the trial-indx + trial_index{k} = k.*ones(1,numel(data.time{k})); +end -% counthow often each position occurs -[wdat, dum] = hist(ic, unique(ic)); +% average across time if needed +if isequal(cfg.method, 'pertrial') || isequal(cfg.method, 'avgoverrpt') || isequal(cfg.method, 'pertrial_cluster') + tmpcfg = []; + tmpcfg.avgovertime = 'yes'; + tmpcfg.nanmean = 'yes'; + data_timeavg = ft_selectdata(tmpcfg, data); + + % concatenate across trials and scale the units + dat = cat(2, data_timeavg.trial{:}); +else + % concatenate across trials and scale the units + dat = cat(2, data.trial{:}); +end +dat = dat * ft_scalingfactor('m', grad.unit); % scale in units of the gradiometer definition, which is probably cm -% compute the cluster means -[bin, cluster] = kmeans(dat', cfg.numclusters); +if isequal(cfg.method, 'pertrial_cluster') + trl_idx = 1:numel(data.trial); +else + trl_idx = cat(2, trial_index{:}); +end + +% average across trials if needed +if isequal(cfg.method, 'avgoverrpt') + dat = sum(dat*diag(wdat), 2)./sum(wdat); +end + +% remove duplicates if clustering is to be performed +if dokmeans && ~isequal(cfg.method, 'pertrial_cluster') + [tmpdata, dum, ic] = unique(dat', 'rows'); + dat = tmpdata'; + % count how often each position occurs + wdat = hist(ic, unique(ic)); +end + +% perform the clustering if needed +if dokmeans + % compute the cluster means + dat_orig = dat'; + [bin, dat] = kmeans(dat', cfg.numclusters, 'EmptyAction', 'drop'); + + % create a cell-array 1xnrpt with time specific indices of cluster id + cluster_id = cell(1,numel(data.trial)); + for k = 1:numel(data.trial) + cluster_id{k} = nan+zeros(1,numel(data.time{k})); + if ~isequal(cfg.method, 'pertrial_cluster') + for m = 1:size(dat,1) + tmpdat = ic(trl_idx==k); + cluster_id{k}(ismember(tmpdat, find(bin==m))) = m; + end + else + cluster_id{k}(:) = bin(k); + end + end + +else + bin = 1:size(dat,2); + dat = dat'; +end + % find the three channels for each fiducial -selnas = strmatch('HLC001', data.label); -sellpa = strmatch('HLC002', data.label); -selrpa = strmatch('HLC003', data.label); +selnas = match_str(data.label,{'HLC0011';'HLC0012';'HLC0013'}); +sellpa = match_str(data.label,{'HLC0021';'HLC0022';'HLC0023'}); +selrpa = match_str(data.label,{'HLC0031';'HLC0032';'HLC0033'}); -ubin = unique(bin); +ubin = unique(bin(isfinite(bin))); +nas = zeros(numel(ubin),3); +lpa = zeros(numel(ubin),3); +rpa = zeros(numel(ubin),3); +numperbin = zeros(numel(ubin),1); for k = 1:length(ubin) - nas(k, :) = cluster(k, selnas); - lpa(k, :) = cluster(k, sellpa); - rpa(k, :) = cluster(k, selrpa); + nas(k, :) = dat(k, selnas); + lpa(k, :) = dat(k, sellpa); + rpa(k, :) = dat(k, selrpa); numperbin(k) = sum(wdat(bin==ubin(k))); end +hc = read_ctf_hc([cfg.datafile(1:end-4),'hc']); if istrue(cfg.feedback) - hc = read_ctf_hc([cfg.datafile(1:end-4),'hc']); - % plot some stuff figure; hold on; title(sprintf('%s coordinates (%s)', grad_dewar.coordsys, grad_dewar.unit)); @@ -127,32 +238,89 @@ plot3(hc.dewar.lpa(1), hc.dewar.lpa(2), hc.dewar.lpa(3), 'ro'); plot3(hc.dewar.rpa(1), hc.dewar.rpa(2), hc.dewar.rpa(3), 'ro'); axis vis3d; axis off - end % compute transformation matrix from dewar to head coordinates -transform = zeros(4, 4, size(nas,1)); -for k = 1:size(transform, 3) - transform(:,:,k) = ft_headcoordinates(nas(k,:), lpa(k,:), rpa(k,:), 'ctf'); +dewar2head = zeros(4, 4, size(nas,1)); +for k = 1:size(dewar2head, 3) + dewar2head(:,:,k) = ft_headcoordinates(nas(k,:), lpa(k,:), rpa(k,:), 'ctf'); end -npos = size(transform, 3); -ncoils = size(grad.coilpos, 1); -gradnew = grad; -gradnew.coilpos = zeros(size(grad.coilpos,1)*npos, size(grad.coilpos,2)); -gradnew.coilori = zeros(size(grad.coilpos,1)*npos, size(grad.coilpos,2)); -gradnew.tra = repmat(grad.tra, [1 npos]); -for m = 1:npos - tmptransform = transform(:,:,m); - gradnew.coilpos((m-1)*ncoils+1:(m*ncoils), :) = ft_warp_apply(tmptransform, grad.coilpos); % back to head coordinates - tmptransform(1:3, 4) = 0; % keep rotation only - gradnew.coilori((m-1)*ncoils+1:(m*ncoils), :) = ft_warp_apply(tmptransform, grad.coilori); - gradnew.tra(:, (m-1)*ncoils+1:(m*ncoils)) = grad.tra.*(numperbin(m)./sum(numperbin)); +if isequal(cfg.method, 'updatesens') + npos = size(dewar2head, 3); + ncoils = size(grad.coilpos, 1); + gradnew = grad; + gradnew.coilpos = zeros(size(grad.coilpos,1)*npos, size(grad.coilpos,2)); + gradnew.coilori = zeros(size(grad.coilpos,1)*npos, size(grad.coilpos,2)); + gradnew.tra = repmat(grad.tra, [1 npos]); + for m = 1:npos + tmptransform = dewar2head(:,:,m); + gradnew.coilpos((m-1)*ncoils+1:(m*ncoils), :) = ft_warp_apply(tmptransform, grad.coilpos); % back to head coordinates + tmptransform(1:3, 4) = 0; % keep rotation only + gradnew.coilori((m-1)*ncoils+1:(m*ncoils), :) = ft_warp_apply(tmptransform, grad.coilori); + gradnew.tra(:, (m-1)*ncoils+1:(m*ncoils)) = grad.tra.*(numperbin(m)./sum(numperbin)); + end + grad = gradnew; +else + npos = size(dewar2head, 3); + for k = 1:npos + grad(k) = ft_transform_geometry(dewar2head(:,:,k), grad_dewar); + end end -grad = gradnew; - +switch cfg.method + case 'cluster' + varargout = cell(1,numel(grad)); + tmpdata = data; + tmpdata.trial = cluster_id; + tmpdata.label = {'cluster_id'}; + data = ft_appenddata([],data,tmpdata); + for k = 1:numel(grad) + + tmpcfg = []; + tmpcfg.artfctdef.bpfilter = 'no'; + tmpcfg.artfctdef.threshold.channel = {'cluster_id'}; + tmpcfg.artfctdef.threshold.min = 0.9+k-1; + tmpcfg.artfctdef.threshold.max = 1.1+k-1; + tmpcfg = ft_artifact_threshold(tmpcfg, tmpdata); + artifacts = tmpcfg.artfctdef.threshold.artifact; + + tmpcfg = []; + tmpcfg.artfctdef.reject = 'partial'; + tmpcfg.artfctdef.threshold.artifact = artifacts; + tmpdata_clus = ft_rejectartifact(tmpcfg, data); + tmpdata_clus.grad = grad(k); + + varargout{k} = tmpdata_clus; + end + case {'avgoverrpt' 'updatesens'} + data.grad = grad; + varargout{1} = data; + case 'pertrial' + ft_error('still to do'); + case 'pertrial_cluster' + varargout = cell(1,numel(grad)); + tmpdata = data; + tmpdata.trial = cluster_id; + tmpdata.label = {'cluster_id'}; + data = ft_appenddata([],data,tmpdata); + for k = 1:numel(grad) + tmpcfg = []; + tmpcfg.trials = find(bin==k); + tmpdata_clus = ft_selectdata(tmpcfg, data); + %tmpcfg.previous = tmpdata_clus.cfg; + + %tmpdata_clus.cfg = tmpcfg; + tmpdata_clus.grad = grad(k); + + varargout{k} = tmpdata_clus; + end +end % do the general cleanup and bookkeeping at the end of the function ft_postamble debug ft_postamble trackconfig ft_postamble provenance +ft_postamble previous varargout +ft_postamble history varargout +ft_postamble savevar varargout + diff --git a/external/fieldtrip/ft_heartrate.m b/external/fieldtrip/ft_heartrate.m new file mode 100644 index 00000000..29a5b147 --- /dev/null +++ b/external/fieldtrip/ft_heartrate.m @@ -0,0 +1,214 @@ +function [dataout] = ft_heartrate(cfg, datain) + +% FT_HEARTRATE estimates the heart rate from a continuous PPG or ECG channel. It +% returns a new data structure with a continuous representation of the heartrate in +% beats per minute. +% +% Use as +% dataout = ft_heartrate(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. +% +% The configuration structure has the following options +% cfg.channel = selected channel for processing, see FT_CHANNELSELECTION +% cfg.envelopewindow = scalar, time in seconds +% cfg.peakseparation = scalar, time in seconds +% cfg.threshold = scalar, between 0 and 1 (default = 0.4) +% cfg.feedback = 'yes' or 'no' +% The input data can be preprocessed on the fly using +% cfg.preproc.bpfilter = 'yes' or 'no' +% cfg.preproc.bpfreq = [low high], filter frequency in Hz +% +% See also FT_ELECTRODERMALACTIVITY, FT_HEADMOVEMENT, FT_REGRESSCONFOUND + +% Copyright (C) 2018, Robert Oostenveld, DCCN +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% the initial part deals with parsing the input options and data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% these are used by the ft_preamble/ft_postamble function and scripts +ft_revision = '$Id$'; +ft_nargin = nargin; +ft_nargout = nargout; + +% the ft_preamble function works by calling a number of scripts from +% fieldtrip/utility/private that are able to modify the local workspace + +ft_defaults +ft_preamble init +ft_preamble debug +ft_preamble loadvar datain +ft_preamble provenance datain +ft_preamble trackconfig + +% the ft_abort variable is set to true or false in ft_preamble_init +if ft_abort + % do not continue function execution in case the outputfile is present and the user indicated to keep it + return +end + +% check if the input data is valid for this function, the input data must be raw +datain = ft_checkdata(datain, 'datatype', 'raw', 'feedback', 'yes'); + +% ensure that users with old scripts are aware of changes +cfg = ft_checkconfig(cfg, 'forbidden', 'medianwindow'); + +% set the default options +cfg.channel = ft_getopt(cfg, 'channel', {}); +cfg.envelopewindow = ft_getopt(cfg, 'envelopewindow', 10); % in seconds +cfg.peakseparation = ft_getopt(cfg, 'peakseparation', []); % in seconds +cfg.threshold = ft_getopt(cfg, 'threshold', 0.4); % between 0 and 1 +cfg.feedback = ft_getopt(cfg, 'feedback', 'yes'); +cfg.preproc = ft_getopt(cfg, 'preproc', []); +% the expected rate is around 80 bpm, which means 80/60=1.33 Hz +cfg.preproc.bpfilter = ft_getopt(cfg.preproc, 'bpfilter', 'yes'); +cfg.preproc.bpfilttype = ft_getopt(cfg.preproc, 'bpfilttype', 'but'); +cfg.preproc.bpfiltdir = ft_getopt(cfg.preproc, 'bpfiltdir', 'twopass'); +cfg.preproc.bpfiltord = ft_getopt(cfg.preproc, 'bpfiltord', 2); +cfg.preproc.bpfreq = ft_getopt(cfg.preproc, 'bpfreq', [1/3 10] * 1.33); % in Hz + +% copy some of the fields over to the new data structure +dataout = keepfields(datain, {'time', 'fsample', 'sampleinfo', 'trialinfo'}); +dataout.label = {'heartrate', 'heartbeatphase', 'heartbeatonset'}; +dataout.trial = {}; % this is to be determined in the main code + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% the actual computation is done in the middle part +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +cfg.channel = ft_channelselection(cfg.channel, datain.label); +assert(numel(cfg.channel)==1, 'you should specify exactly one channel'); + +chansel = strcmp(datain.label, cfg.channel{1}); +fsample = datain.fsample; + +for trllop=1:numel(datain.trial) + dat = datain.trial{trllop}(chansel,:); + label = datain.label(chansel); + time = datain.time{trllop}; + + if skewness(dat)<0 + ft_notice('flipping signal polarity'); + dat = -dat; + end + + if ~isempty(cfg.peakseparation) + [yupper,ylower] = envelope(dat, round(cfg.peakseparation*fsample), 'peaks'); + elseif ~isempty(cfg.envelopewindow) + [yupper,ylower] = envelope(dat, round(cfg.envelopewindow*fsample), 'rms'); + end + + if istrue(cfg.feedback) + figure + subplot(4,1,1) + hold on + plot(time, dat) + plot(time, yupper, 'g'); + plot(time, ylower, 'g'); + xlim([min(time) max(time)]) + xlabel('time (s)'); + title(sprintf('original, trial %d', trllop)) + end + + if ~isempty(cfg.preproc) + % apply the preprocessing to the selected channel + [dat, label, time, cfg.preproc] = preproc(dat, label, time, cfg.preproc, 0, 0); + end + + if ~isempty(cfg.peakseparation) + [yupper,ylower] = envelope(dat, round(cfg.peakseparation*fsample), 'peaks'); + elseif ~isempty(cfg.envelopewindow) + [yupper,ylower] = envelope(dat, round(cfg.envelopewindow*fsample), 'rms'); + end + + if istrue(cfg.feedback) + subplot(4,1,2) + hold on + plot(time, dat) + plot(time, yupper, 'g'); + plot(time, ylower, 'g'); + xlim([min(time) max(time)]) + xlabel('time (s)'); + title('filtered') + end + + dat = (dat - ylower) ./ (yupper - ylower); + + if ~isempty(cfg.peakseparation) + [yupper,ylower] = envelope(dat, round(cfg.peakseparation*fsample), 'peaks'); + elseif ~isempty(cfg.envelopewindow) + [yupper,ylower] = envelope(dat, round(cfg.envelopewindow*fsample), 'rms'); + end + + % find the sample numbers where the filtered value increases above the threshold + [vals, peaks] = findpeaks(dat, 'MinPeakHeight', cfg.threshold); + + if istrue(cfg.feedback) + subplot(4,1,3) + hold on + plot(time, dat) + plot(time, yupper, 'g'); + plot(time, ylower, 'g'); + plot(time(peaks), vals, 'r*'); + xlim([min(time) max(time)]) + xlabel('time (s)'); + title('locally rescaled') + end + + % construct a continuous channel with the rate and the phase + rate = nan(size(dat)); + phase = nan(size(dat)); + for i=1:length(peaks)-1 + begsample = peaks(i); + endsample = peaks(i+1); + rate(begsample:endsample) = 60 * fsample/(endsample-begsample); % in bpm + phase(begsample:endsample) = linspace(-pi, pi, (endsample-begsample+1)); + end + % also construct a boolean channel with a pulse at the beat onset + tmp = zeros(size(dat)); + tmp(peaks) = 1; + + % add the continuous channels to the output structure + dataout.trial{trllop} = [rate; phase; tmp]; + + if istrue(cfg.feedback) + subplot(4,1,4) + plot(time, rate) + ylim([0 160]) + xlim([min(time) max(time)]) + xlabel('time (s)'); + ylabel('rate (bpm)'); + end + + ft_info('heart rate in trial %d: mean=%.1f, min=%.1f, max=%.1f\n', trllop, nanmean(rate), nanmin(rate), nanmax(rate)); + +end % for trllop + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% deal with the output +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ft_postamble debug +ft_postamble trackconfig +ft_postamble previous datain +ft_postamble provenance dataout +ft_postamble history dataout +ft_postamble savevar dataout diff --git a/external/fieldtrip/ft_interactiverealign.m b/external/fieldtrip/ft_interactiverealign.m index 32388337..cb8ab391 100644 --- a/external/fieldtrip/ft_interactiverealign.m +++ b/external/fieldtrip/ft_interactiverealign.m @@ -188,21 +188,22 @@ % open a figure fig = figure; +set(fig, 'CloseRequestFcn', @cb_quit); +set(fig, 'windowkeypressfcn', @cb_keyboard); set(gca, 'position', [0.05 0.15 0.75 0.75]); -axis([-150 150 -150 150 -150 150]); % add the data to the figure -set(fig, 'CloseRequestFcn', @cb_quit); setappdata(fig, 'individual', individual); setappdata(fig, 'template', template); setappdata(fig, 'transform', eye(4)); setappdata(fig, 'cleanup', false); setappdata(fig, 'coordsys', coordsys); % can be unknown - -setappdata(fig, 'toggle_axes', 1); -setappdata(fig, 'toggle_grid', 1); +setappdata(fig, 'toggle_labels', true); +setappdata(fig, 'toggle_axes', true); +setappdata(fig, 'toggle_grid', true); % add the GUI elements +axis([-150 150 -150 150 -150 150]); cb_creategui(gcf); cb_redraw(gcf); rotate3d on @@ -282,9 +283,9 @@ function cb_creategui(h, eventdata, handles) uicontrol('tag', 'viewpointbtn', 'parent', fig, 'units', 'normalized', 'style', 'popup', 'string', 'top|bottom|left|right|front|back', 'value', 1, 'callback', @cb_viewpoint); uicontrol('tag', 'redisplaybtn', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'redisplay', 'value', [], 'callback', @cb_redraw); uicontrol('tag', 'applybtn', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'apply', 'value', [], 'callback', @cb_apply); -uicontrol('tag', 'toggle labels', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'toggle label', 'value', 0, 'callback', @cb_redraw); -uicontrol('tag', 'toggle axes', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'toggle axes', 'value', getappdata(fig, 'toggle_axes'), 'callback', @cb_redraw); -uicontrol('tag', 'toggle grid', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'toggle grid', 'value', getappdata(fig, 'toggle_grid'), 'callback', @cb_redraw); +uicontrol('tag', 'toggle labels', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'toggle label', 'value', getappdata(fig, 'toggle_labels'), 'callback', @cb_redraw); +uicontrol('tag', 'toggle axes', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'toggle axes', 'value', getappdata(fig, 'toggle_axes'), 'callback', @cb_redraw); +uicontrol('tag', 'toggle grid', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'toggle grid', 'value', getappdata(fig, 'toggle_grid'), 'callback', @cb_redraw); uicontrol('tag', 'quitbtn', 'parent', fig, 'units', 'normalized', 'style', 'pushbutton', 'string', 'quit', 'value', 1, 'callback', @cb_quit); ft_uilayout(fig, 'tag', 'viewpointbtn', 'BackgroundColor', [0.8 0.8 0.8], 'width', 6*CONTROL_WIDTH, 'height', CONTROL_HEIGHT, 'vpos', CONTROL_VOFFSET-2*CONTROL_HEIGHT, 'hpos', CONTROL_HOFFSET); ft_uilayout(fig, 'tag', 'redisplaybtn', 'BackgroundColor', [0.8 0.8 0.8], 'width', 6*CONTROL_WIDTH, 'height', CONTROL_HEIGHT, 'vpos', CONTROL_VOFFSET-4*CONTROL_HEIGHT, 'hpos', CONTROL_HOFFSET); @@ -328,9 +329,6 @@ function cb_redraw(h, eventdata, handles) transform = H * transform; axis vis3d; cla -xlabel('x (mm)') -ylabel('y (mm)') -zlabel('z (mm)') hold on @@ -352,11 +350,11 @@ function cb_redraw(h, eventdata, handles) end if ~isempty(template.mri) - ft_plot_ortho(template.mri.anatomy, 'transform', template.mri.transform, 'style', 'intersect', 'intersectmesh', individual.headshape, cfg.individual.mristyle{:}); + ft_plot_ortho(template.mri.anatomy, 'transform', template.mri.transform, 'style', 'intersect', 'intersectmesh', individual.headshape, individual.mristyle{:}); end if ~isempty(individual.mri) - ft_plot_ortho(individual.mri.anatomy, 'transform', individual.mri.transform, 'style', 'intersect', 'intersectmesh', template.headshape, cfg.template.mristyle{:}); + ft_plot_ortho(individual.mri.anatomy, 'transform', individual.mri.transform, 'style', 'intersect', 'intersectmesh', template.headshape, template.mristyle{:}); end if istrue(template.axes) @@ -420,6 +418,20 @@ function cb_redraw(h, eventdata, handles) material shiny camlight +if strcmp(get(h, 'tag'), 'toggle labels') + setappdata(fig, 'toggle_labels', ~getappdata(fig, 'toggle_labels')) +end + +if getappdata(fig, 'toggle_labels') + xlabel('x (mm)') + ylabel('y (mm)') + zlabel('z (mm)') +else + xlabel('') + ylabel('') + zlabel('') +end + if strcmp(get(h, 'tag'), 'toggle axes') setappdata(fig, 'toggle_axes', ~getappdata(fig, 'toggle_axes')) end @@ -480,6 +492,52 @@ function cb_apply(h, eventdata, handles) cb_redraw(h); end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function cb_keyboard(h, eventdata) + +fig = getparent(h); + +if isempty(eventdata) + % determine the key that corresponds to the uicontrol element that was activated + key = get(fig, 'userdata'); +else + % determine the key that was pressed on the keyboard + key = parsekeyboardevent(eventdata); +end + +% get focus back to figure +if ~strcmp(get(h, 'type'), 'figure') + set(h, 'enable', 'off'); + drawnow; + set(h, 'enable', 'on'); +end + +if isempty(key) + % this happens if you press the apple key + key = ''; +end + +% the following code is largely shared by FT_SOURCEPLOT, FT_VOLUMEREALIGN, FT_INTERACTIVEREALIGN, FT_MESHREALIGN, FT_ELECTRODEPLACEMENT +switch key + case {'' 'shift+shift' 'alt-alt' 'control+control' 'command-0'} + % do nothing + + case 'q' + cb_quit(h); + + case 'v' % camlight angle reset + delete(findall(fig,'Type','light')) % shut out the lights + % add a new light from the current camera position + lighting gouraud + material shiny + camlight + + otherwise + % do nothing + +end % switch key + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function cb_viewpoint(h, eventdata) diff --git a/external/fieldtrip/ft_meshrealign.m b/external/fieldtrip/ft_meshrealign.m index 2e7ac6d9..7306cf94 100644 --- a/external/fieldtrip/ft_meshrealign.m +++ b/external/fieldtrip/ft_meshrealign.m @@ -171,7 +171,7 @@ set(h, 'visible', 'on'); % add callbacks set(h, 'windowkeypressfcn', @cb_keyboard_surface); - set(h, 'CloseRequestFcn', @cb_cleanup); + set(h, 'CloseRequestFcn', @cb_quit); % create figure handles h1 = axes; @@ -292,7 +292,7 @@ function cb_keyboard_surface(h, eventdata) key = get(h, 'userdata'); else % determine the key that was pressed on the keyboard - key = parseKeyboardEvent(eventdata); + key = parsekeyboardevent(eventdata); end % get the most recent surface position that was clicked with the mouse @@ -325,7 +325,7 @@ function cb_keyboard_surface(h, eventdata) setappdata(h, 'opt', opt); if isequal(key, 'q') - cb_cleanup(h); + cb_quit(h); else cb_redraw_surface(h); end @@ -624,7 +624,7 @@ function cb_keyboard(h, eventdata) key = get(h, 'userdata'); else % determine the key that was pressed on the keyboard - key = parseKeyboardEvent(eventdata); + key = parsekeyboardevent(eventdata); end % get focus back to figure if ~strcmp(get(h, 'type'), 'figure') @@ -644,7 +644,7 @@ function cb_keyboard(h, eventdata) key = ''; end -% the following code is largely shared with FT_SOURCEPLOT +% the following code is largely shared by FT_SOURCEPLOT, FT_VOLUMEREALIGN, FT_INTERACTIVEREALIGN, FT_MESHREALIGN, FT_ELECTRODEPLACEMENT switch key case {'' 'shift+shift' 'alt-alt' 'control+control' 'command-0'} % do nothing @@ -670,7 +670,7 @@ function cb_keyboard(h, eventdata) case 'q' setappdata(h, 'opt', opt); - cb_cleanup(h); + cb_quit(h); case {'i' 'j' 'k' 'm' 28 29 30 31 'leftarrow' 'rightarrow' 'uparrow' 'downarrow'} % TODO FIXME use leftarrow rightarrow uparrow downarrow % update the view to a new position @@ -693,8 +693,8 @@ function cb_keyboard(h, eventdata) setappdata(h, 'opt', opt); cb_redraw(h); + case {43 'add' 'shift+equal'} % + or numpad + % contrast scaling - case {43 'shift+equal'} % numpad + % disable if viewresult if ~opt.viewresult if isempty(opt.clim) @@ -708,7 +708,8 @@ function cb_keyboard(h, eventdata) cb_redraw(h); end - case {45 'shift+hyphen'} % numpad - + case {45 'subtract' 'hyphen' 'shift+hyphen'} % - or numpad - + % contrast scaling % disable if viewresult if ~opt.viewresult if isempty(opt.clim) @@ -857,7 +858,7 @@ function cb_getposition(h, eventdata) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function cb_cleanup(h, eventdata) +function cb_quit(h, eventdata) opt = getappdata(h, 'opt'); if ~opt.viewresult @@ -879,83 +880,3 @@ function cb_cleanup(h, eventdata) p = get(h, 'parent'); end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; - -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character; - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function cb_minslider(h4, eventdata) - -tag = get(h4, 'tag'); -newlim = get(h4, 'value'); -h = getparent(h4); -opt = getappdata(h, 'opt'); -if isempty(tag) - opt.clim(1) = newlim; -elseif strcmp(tag, 'rel') - opt.realignclim(1) = newlim; -elseif strcmp(tag, 'tar') - opt.targetclim(1) = newlim; -end -if isempty(tag) - fprintf('contrast limits updated to [%.03f %.03f]\n', opt.clim); -elseif strcmp(tag, 'rel') - fprintf('realigned contrast limits updated to [%.03f %.03f]\n', opt.realignclim); -elseif strcmp(tag, 'tar') - fprintf('target cfontrast limits updated to [%.03f %.03f]\n', opt.targetclim); -end -setappdata(h, 'opt', opt); -cb_redraw(h); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function cb_maxslider(h5, eventdata) - -tag = get(h5, 'tag'); -newlim = get(h5, 'value'); -h = getparent(h5); -opt = getappdata(h, 'opt'); -if isempty(tag) - opt.clim(2) = newlim; -elseif strcmp(tag, 'rel') - opt.realignclim(2) = newlim; -elseif strcmp(tag, 'tar') - opt.targetclim(2) = newlim; -end -if isempty(tag) - fprintf('contrast limits updated to [%.03f %.03f]\n', opt.clim); -elseif strcmp(tag, 'rel') - fprintf('realigned contrast limits updated to [%.03f %.03f]\n', opt.realignclim); -elseif strcmp(tag, 'tar') - fprintf('target contrast limits updated to [%.03f %.03f]\n', opt.targetclim); -end -setappdata(h, 'opt', opt); -cb_redraw(h); diff --git a/external/fieldtrip/ft_movieplotER.m b/external/fieldtrip/ft_movieplotER.m index f94b384f..3889870c 100644 --- a/external/fieldtrip/ft_movieplotER.m +++ b/external/fieldtrip/ft_movieplotER.m @@ -68,7 +68,7 @@ % do the general setup of the function ft_defaults ft_preamble init -ft_preamble provenance +ft_preamble provenance data % the ft_abort variable is set to true or false in ft_preamble_init if ft_abort diff --git a/external/fieldtrip/ft_multiplotTFR.m b/external/fieldtrip/ft_multiplotTFR.m index 4b10723c..5480866b 100644 --- a/external/fieldtrip/ft_multiplotTFR.m +++ b/external/fieldtrip/ft_multiplotTFR.m @@ -14,11 +14,14 @@ % The configuration can have the following parameters: % cfg.parameter = field to be represented as color (default depends on data.dimord) % 'powspctrm' or 'cohspctrm' -% cfg.maskparameter = field in the data to be used for opacity masking of data -% cfg.maskstyle = style used to masking, 'opacity', 'saturation', 'outline' or 'colormix' (default = 'opacity') -% use 'saturation' or 'outline' when saving to vector-format (like *.eps) to avoid all -% sorts of image-problems (currently only possible with a white backgroud) -% cfg.maskalpha = alpha value between 0 (transparant) and 1 (opaque) used for masking areas dictated by cfg.maskparameter (default = 1) +% cfg.maskparameter = field in the data to be used for masking of data, can be logical (e.g. significant data points) or numerical (e.g. t-values). +% (not possible for mean over multiple channels, or when input contains multiple subjects +% or trials) +% cfg.maskstyle = style used to masking, 'opacity', 'saturation', or 'outline' (default = 'opacity') +% 'outline' can only be used with a logical cfg.maskparameter +% use 'saturation' or 'outline' when saving to vector-format (like *.eps) to avoid all sorts of image-problems +% cfg.maskalpha = alpha value between 0 (transparent) and 1 (opaque) used for masking areas dictated by cfg.maskparameter (default = 1) +% (will be ignored in case of numeric cfg.maskparameter or if cfg.maskstyle = 'outline') % cfg.masknans = 'yes' or 'no' (default = 'yes') % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.ylim = 'maxmin' or [ymin ymax] (default = 'maxmin') @@ -166,7 +169,6 @@ cfg = ft_checkconfig(cfg, 'renamedval', {'directionality', 'feedforward', 'outflow'}); cfg = ft_checkconfig(cfg, 'renamedval', {'zlim', 'absmax', 'maxabs'}); cfg = ft_checkconfig(cfg, 'unused', {'cohtargetchannel'}); -% cfg = ft_checkconfig(cfg, 'deprecated', {'xparam', 'yparam'}); % set the defaults cfg.parameter = ft_getopt(cfg, 'parameter', 'powspctrm'); @@ -187,7 +189,7 @@ cfg.fontsize = ft_getopt(cfg, 'fontsize', 8); cfg.fontweight = ft_getopt(cfg, 'fontweight'); cfg.interactive = ft_getopt(cfg, 'interactive', 'yes'); -cfg.hotkeys = ft_getopt(cfg, 'hotkeys', 'no'); +cfg.hotkeys = ft_getopt(cfg, 'hotkeys', 'yes'); cfg.renderer = ft_getopt(cfg, 'renderer'); % let MATLAB decide on default cfg.orient = ft_getopt(cfg, 'orient', 'landscape'); cfg.maskalpha = ft_getopt(cfg, 'maskalpha', 1); @@ -395,16 +397,27 @@ % the usual data is chan_freq_time, but other dimords should also work dimtok = tokenize(dimord, '_'); datamatrix = data.(cfg.parameter); -[c, ia, ib] = intersect(dimtok, {'chan', yparam, xparam}); -datamatrix = permute(datamatrix, ia); +[c, ia, ib] = intersect({'chan', yparam, xparam}, dimtok, 'stable'); +datamatrix = permute(datamatrix, ib); datamatrix = datamatrix(selchan, sely, selx); if ~isempty(cfg.maskparameter) % one value for each channel-freq-time point maskmatrix = data.(cfg.maskparameter)(selchan, sely, selx); - if cfg.maskalpha ~= 1 - maskmatrix( maskmatrix) = 1; + if islogical(maskmatrix) && any(strcmp(cfg.maskstyle, {'saturation', 'opacity'})) + maskmatrix = double(maskmatrix); maskmatrix(~maskmatrix) = cfg.maskalpha; + elseif isnumeric(maskmatrix) + if strcmp(cfg.maskstyle, 'outline') + error('Outline masking with a numeric cfg.maskparameter is not supported. Please use a logical mask instead.') + end + if cfg.maskalpha ~= 1 + warning(sprintf('Using field "%s" for masking, cfg.maskalpha is ignored.', cfg.maskparameter)) + end + % scale mask between 0 and 1 + minval = min(maskmatrix(:)); + maxval = max(maskmatrix(:)); + maskmatrix = (maskmatrix - minval) / (maxval-minval); end else % create an Nx0x0 matrix @@ -478,6 +491,7 @@ end % show comment +comment_handle = []; if istrue(cfg.showcomment) k = find(strcmp('COMNT', cfg.layout.label)); if ~isempty(k) @@ -490,7 +504,7 @@ comment = sprintf('%0s\nylim=[%.3g %.3g]', comment, ymin, ymax); comment = sprintf('%0s\nzlim=[%.3g %.3g]', comment, zmin, zmax); end - ft_plot_text(cfg.layout.pos(k, 1), cfg.layout.pos(k, 2), sprintf(comment), 'FontSize', cfg.fontsize, 'FontWeight', cfg.fontweight); + comment_handle = ft_plot_text(cfg.layout.pos(k, 1), cfg.layout.pos(k, 2), sprintf(comment), 'FontSize', cfg.fontsize, 'FontWeight', cfg.fontweight); end end @@ -581,6 +595,7 @@ info.(ident).dataname = dataname; info.(ident).cfg = cfg; info.(ident).data = data; + info.(ident).commenth = comment_handle; guidata(gcf, info); set(gcf, 'WindowButtonUpFcn', {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotTFR}, 'event', 'WindowButtonUpFcn'}); set(gcf, 'WindowButtonDownFcn', {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotTFR}, 'event', 'WindowButtonDownFcn'}); @@ -644,14 +659,41 @@ function select_singleplotTFR(label, varargin) % SUBFUNCTION which handles hot keys in the current plot %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function key_sub(handle, eventdata, varargin) -incr = (max(caxis)-min(caxis)) /10; -% symmetrically scale color bar down by 10 percent -if strcmp(eventdata.Key, 'uparrow') - caxis([min(caxis)-incr max(caxis)+incr]); - % symmetrically scale color bar up by 10 percent -elseif strcmp(eventdata.Key, 'downarrow') - caxis([min(caxis)+incr max(caxis)-incr]); - % resort to minmax of data for colorbar -elseif strcmp(eventdata.Key, 'm') - caxis([data varargin{2}]); -end +ident = get(gca, 'tag'); +info = guidata(gcf); + +climits = caxis; +incr_c = abs(climits(2) - climits(1)) /10; + +newz = climits; +if length(eventdata.Modifier) == 1 && strcmp(eventdata.Modifier{:}, 'control') + % TRANSLATE by 10% + switch eventdata.Key + case 'pageup' + newz = [climits(1)+incr_c climits(2)+incr_c]; + case 'pagedown' + newz = [climits(1)-incr_c climits(2)-incr_c]; + end % switch +else + % ZOOM by 10% + switch eventdata.Key + case 'pageup' + newz = [climits(1)-incr_c climits(2)+incr_c]; + case 'pagedown' + newz = [climits(1)+incr_c climits(2)-incr_c]; + case 'm' + newz = [varargin{1} varargin{2}]; + end % switch +end % if + +% update the color axis +caxis(newz); + +if ~isempty(ident) && isfield(info.(ident), 'commenth') && ~isempty(info.(ident).commenth) + commentstr = get(info.(ident).commenth, 'string'); + sel = contains(commentstr, 'zlim'); + if any(sel) + commentstr{sel} = sprintf('%0s=[%.3g %.3g]', 'zlim', newz(1), newz(2)); + set(info.(ident).commenth, 'string', commentstr); + end +end \ No newline at end of file diff --git a/external/fieldtrip/ft_mvaranalysis.m b/external/fieldtrip/ft_mvaranalysis.m index 4a351f8e..39f7c3e6 100644 --- a/external/fieldtrip/ft_mvaranalysis.m +++ b/external/fieldtrip/ft_mvaranalysis.m @@ -150,7 +150,7 @@ if ~any(strcmp(cfg.channel, 'all')) && isfield(cfg, 'channelcmb') ft_warning('cfg.channelcmb defined, overriding cfg.channel setting and computing over bivariate pairs'); else - % select trials of interest + % select trials of interest tmpcfg = []; tmpcfg.channel = cfg.channel; data = ft_selectdata(tmpcfg, data); @@ -174,24 +174,24 @@ if isempty(cfg.toi) && isempty(cfg.t_ftimwin) % fit model to entire data segment - - % check whether this is allowed - nsmp = cellfun('size', data.trial, 2); - if all(nsmp==nsmp(1)) - oktoolbox = {'bsmart' 'biosig'}; - else - oktoolbox = 'biosig'; % bsmart does not work with variable trials - end - - if ~ismember(cfg.method, oktoolbox) - error('fitting the mvar-model is not possible with the ''%s'' toolbox',cfg.method); - end - latency = [-inf inf]; + + % check whether this is allowed + nsmp = cellfun('size', data.trial, 2); + if all(nsmp==nsmp(1)) + oktoolbox = {'bsmart' 'biosig'}; + else + oktoolbox = 'biosig'; % bsmart does not work with variable trials + end + + if ~ismember(cfg.method, oktoolbox) + error('fitting the mvar-model is not possible with the ''%s'' toolbox',cfg.method); + end + latency = [-inf inf]; elseif ~isempty(cfg.toi) && ~isempty(cfg.t_ftimwin) % do sliding window approach for k = 1:numel(cfg.toi) - latency(k,:) = cfg.toi + cfg.t_ftimwin.*[-0.5 0.5-1./data.fsample]; - end + latency(k,:) = cfg.toi(k) + cfg.t_ftimwin.*[-0.5 0.5] + [0 -1./data.fsample]; + end else ft_error('cfg should contain both cfg.toi and cfg.t_ftimwin'); end @@ -207,7 +207,6 @@ if ~keeptap, ft_error('not keeping tapers is not possible yet'); end if dojack && keeprpt, ft_error('you cannot simultaneously keep trials and do jackknifing'); end -tfwin = round(data.fsample.*cfg.t_ftimwin); ntrl = length(data.trial); ntoi = size(latency, 1); @@ -215,7 +214,7 @@ chanindx = match_str(data.label, cfg.channel); nchan = length(chanindx); label = data.label(chanindx); - + ncmb = nchan*nchan; cmbindx1 = repmat(chanindx(:), [1 nchan]); cmbindx2 = repmat(chanindx(:)', [nchan 1]); @@ -226,15 +225,15 @@ for k = 1:size(cmbindx,1) [tmp, cmbindx(k,:)] = match_str(cfg.channelcmb(k,:)', data.label); end - - + + nchan = 2; label = data.label(cmbindx); - + ncmb = nchan*nchan; labelcmb = cell(0,2); cmb = cfg.channelcmb; - + for k = 1:size(cmbindx,1) labelcmb{end+1,1} = [cmb{k,1},'[',cmb{k,1},cmb{k,2},']']; labelcmb{end ,2} = [cmb{k,1},'[',cmb{k,1},cmb{k,2},']']; @@ -247,6 +246,7 @@ end end +tfwin = round(cfg.t_ftimwin.*data.fsample); %---think whether this makes sense at all if strcmp(cfg.taper, 'dpss') % create a sequence of DPSS (Slepian) tapers @@ -262,6 +262,7 @@ end ntap = size(tap,1); + %---preprocess data if necessary -> changed 20150224, JM does not think %this step is necessary: it creates problems downstream if the time axes of %the trials are different @@ -283,7 +284,7 @@ %---ensemble mean subtraction if strcmp(cfg.ems, 'yes') % to be implemented - error('ensemble mean subtraction is not yet implemented here'); + error('ensemble mean subtraction is not yet implemented here'); end %---zscore @@ -305,7 +306,7 @@ end datavg = sumval./numsmp; datstd = sqrt(sumsqr./numsmp - (sumval./numsmp).^2); - + data.trial = data.trial(trlindx); data.time = data.time(trlindx); ntrl = length(trlindx); @@ -334,10 +335,10 @@ coeffs = zeros(1, 2*nchan, size(cmbindx,1), cfg.order, ntoi, ntap); noisecov = zeros(1, 2*nchan, size(cmbindx,1), ntoi, ntap); elseif dounivariate && (keeprpt || dojack) - error('doing univariate model fits in combination with multiple replicates is not yet possible'); + error('doing univariate model fits in combination with multiple replicates is not yet possible'); elseif dounivariate - coeffs = zeros(1, nchan, cfg.order, ntoi, ntap); - noisecov = zeros(1, nchan, ntoi, ntap); + coeffs = zeros(1, nchan, cfg.order, ntoi, ntap); + noisecov = zeros(1, nchan, ntoi, ntap); elseif (keeprpt || dojack) coeffs = zeros(length(data.trial), nchan, nchan, cfg.order, ntoi, ntap); noisecov = zeros(length(data.trial), nchan, nchan, ntoi, ntap); @@ -350,26 +351,43 @@ ft_progress('init', cfg.feedback, 'computing AR-model'); for j = 1:ntoi - if ~isequal(latency(j,:),[-inf inf]) - ft_progress(j/ntoi, 'processing timewindow %d from %d\n', j, ntoi); + if ~isequal(latency(j,:),[-inf inf]) + ft_progress(j/ntoi, 'processing timewindow %d from %d\n', j, ntoi); tmpcfg = []; - tmpcfg.toilim = latency(j,:); - tmpdata = ft_redefinetrial(tmpcfg, data); - else - tmpdata = data; - end - - tmpnsmp = cellfun('size', tmpdata.trial, 2); - if ntoi>1 && strcmp(cfg.method, 'bsmart') - % ensure all segments to be of equal length - if ~all(tmpnsmp==tmpnsmp(1)) - error('the epochs are of unequal length, possibly due to numerical time axis issues, or due to partial artifacts, use cfg.method=''biosig'''); - end - ix = find(tmpnsmp==mode(tmpnsmp), 1, 'first'); - cfg.toi(j) = mean(tmpdata.time{ix}([1 end]))+0.5./data.fsample; %FIXME think about this - end - - + tmpcfg.toilim = latency(j,:); + tmpdata = ft_redefinetrial(tmpcfg, data); + else + tmpdata = data; + end + + tmpnsmp = cellfun('size', tmpdata.trial, 2); + tfwin = tmpnsmp(1); + %---think whether this makes sense at all + if strcmp(cfg.taper, 'dpss') + % create a sequence of DPSS (Slepian) tapers + % ensure that the input arguments are double precision + tap = double_dpss(tfwin,tfwin*(cfg.tapsmofrq./tmpdata.fsample))'; + tap = tap(1,:); %only use first 'zero-order' taper + elseif strcmp(cfg.taper, 'sine') + tap = sine_taper(tfwin, tfwin*(cfg.tapsmofrq./tmpdata.fsample))'; + tap = tap(1,:); + else + tap = window(cfg.taper, tfwin)'; + tap = tap./norm(tap); + end + ntap = size(tap,1); + + + if ntoi>1 && strcmp(cfg.method, 'bsmart') + % ensure all segments to be of equal length + if ~all(tmpnsmp==tmpnsmp(1)) + error('the epochs are of unequal length, possibly due to numerical time axis issues, or due to partial artifacts, use cfg.method=''biosig'''); + end + ix = find(tmpnsmp==mode(tmpnsmp), 1, 'first'); + cfg.toi(j) = mean(tmpdata.time{ix}([1 end]))+0.5./data.fsample; %FIXME think about this + end + + %---create cell-array indexing which original trials should go into each replicate rpt = {}; nrpt = numel(tmpdata.trial); @@ -386,102 +404,102 @@ rpt{1} = 1:numel(tmpdata.trial); nrpt = 1; end - - for rlop = 1:nrpt - - if dobvar % bvar - for m = 1:ntap - %---construct data-matrix - for k = 1:size(cmbindx,1) - dat = catnan(tmpdata.trial, cmbindx(k,:), rpt{rlop}, tap(m,:), nnans, dobvar); - - %---estimate autoregressive model - switch cfg.method - case 'biosig' - [ar, rc, pe] = mvar(dat', cfg.order, cfg.mvarmethod); - - %---compute noise covariance - tmpnoisecov = pe(:,nchan*cfg.order+1:nchan*(cfg.order+1)); - case 'bsmart' - [ar, tmpnoisecov] = armorf(dat, numel(rpt{rlop}), size(tmpdata.trial{1},2), cfg.order); - ar = -ar; %convention is swapped sign with respect to biosig - %FIXME check which is which: X(t) = A1*X(t-1) + ... + An*X(t-n) + E - %the other is then X(t) + A1*X(t-1) + ... + An*X(t-n) = E - end - coeffs(rlop,:,k,:,j,m) = reshape(ar, [nchan*2 cfg.order]); - - %---rescale noisecov if necessary - if dozscore, % FIX ME for bvar - noisecov(rlop,:,k,:,j,m) = diag(datstd)*tmpnoisecov*diag(datstd); - else - noisecov(rlop,:,k,j,m) = reshape(tmpnoisecov,[1 4]); - end - dof(rlop,:,j) = numel(rpt{rlop}); - end - end - else % mvar - for m = 1:ntap - %---construct data-matrix - dat = catnan(tmpdata.trial, chanindx, rpt{rlop}, tap(m,:), nnans, dobvar); - - %---estimate autoregressive model - if dounivariate - - %---loop across the channels - for p = 1:size(dat,1) - - switch cfg.method - case 'biosig' - [ar, rc, pe] = mvar(dat(p,:)', cfg.order, cfg.mvarmethod); - - %---compute noise covariance - tmpnoisecov = pe(:,cfg.order+1:(cfg.order+1)); - case 'bsmart' - [ar, tmpnoisecov] = armorf(dat(p,:), numel(rpt{rlop}), size(tmpdata.trial{1},2), cfg.order); - ar = -ar; %convention is swapped sign with respect to biosig - %FIXME check which is which: X(t) = A1*X(t-1) + ... + An*X(t-n) + E - %the other is then X(t) + A1*X(t-1) + ... + An*X(t-n) = E - end - coeffs(rlop,p,:,j,m) = reshape(ar, [1 cfg.order]); - - %---rescale noisecov if necessary - if dozscore - noisecov(rlop,p,j,m) = diag(datstd)*tmpnoisecov*diag(datstd); - else - noisecov(rlop,p,j,m) = tmpnoisecov; - end - dof(rlop,:,j) = numel(rpt{rlop}); - end - - else - switch cfg.method - case 'biosig' - [ar, rc, pe] = mvar(dat', cfg.order, cfg.mvarmethod); - - %---compute noise covariance - tmpnoisecov = pe(:,nchan*cfg.order+1:nchan*(cfg.order+1)); - case 'bsmart' - [ar, tmpnoisecov] = armorf(dat, numel(rpt{rlop}), size(tmpdata.trial{1},2), cfg.order); - ar = -ar; %convention is swapped sign with respect to biosig - %FIXME check which is which: X(t) = A1*X(t-1) + ... + An*X(t-n) + E - %the other is then X(t) + A1*X(t-1) + ... + An*X(t-n) = E - end - coeffs(rlop,:,:,:,j,m) = reshape(ar, [nchan nchan cfg.order]); - - %---rescale noisecov if necessary - if dozscore - noisecov(rlop,:,:,j,m) = diag(datstd)*tmpnoisecov*diag(datstd); - else - noisecov(rlop,:,:,j,m) = tmpnoisecov; - end - dof(rlop,:,j) = numel(rpt{rlop}); - end %---dounivariate - - end %---tapers - end - - end %---replicates - + + for rlop = 1:nrpt + + if dobvar % bvar + for m = 1:ntap + %---construct data-matrix + for k = 1:size(cmbindx,1) + dat = catnan(tmpdata.trial, cmbindx(k,:), rpt{rlop}, tap(m,:), nnans, dobvar); + + %---estimate autoregressive model + switch cfg.method + case 'biosig' + [ar, rc, pe] = mvar(dat', cfg.order, cfg.mvarmethod); + + %---compute noise covariance + tmpnoisecov = pe(:,nchan*cfg.order+1:nchan*(cfg.order+1)); + case 'bsmart' + [ar, tmpnoisecov] = armorf(dat, numel(rpt{rlop}), size(tmpdata.trial{1},2), cfg.order); + ar = -ar; %convention is swapped sign with respect to biosig + %FIXME check which is which: X(t) = A1*X(t-1) + ... + An*X(t-n) + E + %the other is then X(t) + A1*X(t-1) + ... + An*X(t-n) = E + end + coeffs(rlop,:,k,:,j,m) = reshape(ar, [nchan*2 cfg.order]); + + %---rescale noisecov if necessary + if dozscore, % FIX ME for bvar + noisecov(rlop,:,k,:,j,m) = diag(datstd)*tmpnoisecov*diag(datstd); + else + noisecov(rlop,:,k,j,m) = reshape(tmpnoisecov,[1 4]); + end + dof(rlop,:,j) = numel(rpt{rlop}); + end + end + else % mvar + for m = 1:ntap + %---construct data-matrix + dat = catnan(tmpdata.trial, chanindx, rpt{rlop}, tap(m,:), nnans, dobvar); + + %---estimate autoregressive model + if dounivariate + + %---loop across the channels + for p = 1:size(dat,1) + + switch cfg.method + case 'biosig' + [ar, rc, pe] = mvar(dat(p,:)', cfg.order, cfg.mvarmethod); + + %---compute noise covariance + tmpnoisecov = pe(:,cfg.order+1:(cfg.order+1)); + case 'bsmart' + [ar, tmpnoisecov] = armorf(dat(p,:), numel(rpt{rlop}), size(tmpdata.trial{1},2), cfg.order); + ar = -ar; %convention is swapped sign with respect to biosig + %FIXME check which is which: X(t) = A1*X(t-1) + ... + An*X(t-n) + E + %the other is then X(t) + A1*X(t-1) + ... + An*X(t-n) = E + end + coeffs(rlop,p,:,j,m) = reshape(ar, [1 cfg.order]); + + %---rescale noisecov if necessary + if dozscore + noisecov(rlop,p,j,m) = diag(datstd)*tmpnoisecov*diag(datstd); + else + noisecov(rlop,p,j,m) = tmpnoisecov; + end + dof(rlop,:,j) = numel(rpt{rlop}); + end + + else + switch cfg.method + case 'biosig' + [ar, rc, pe] = mvar(dat', cfg.order, cfg.mvarmethod); + + %---compute noise covariance + tmpnoisecov = pe(:,nchan*cfg.order+1:nchan*(cfg.order+1)); + case 'bsmart' + [ar, tmpnoisecov] = armorf(dat, numel(rpt{rlop}), size(tmpdata.trial{1},2), cfg.order); + ar = -ar; %convention is swapped sign with respect to biosig + %FIXME check which is which: X(t) = A1*X(t-1) + ... + An*X(t-n) + E + %the other is then X(t) + A1*X(t-1) + ... + An*X(t-n) = E + end + coeffs(rlop,:,:,:,j,m) = reshape(ar, [nchan nchan cfg.order]); + + %---rescale noisecov if necessary + if dozscore + noisecov(rlop,:,:,j,m) = diag(datstd)*tmpnoisecov*diag(datstd); + else + noisecov(rlop,:,:,j,m) = tmpnoisecov; + end + dof(rlop,:,j) = numel(rpt{rlop}); + end %---dounivariate + + end %---tapers + end + + end %---replicates + end %---tois ft_progress('close'); @@ -509,11 +527,11 @@ noisecov = reshape(noisecov, [siz(2) * siz(3) siz(4)]); mvardata.labelcmb = labelcmb; elseif dounivariate - mvardata.dimord = 'chan_lag'; + mvardata.dimord = 'chan_lag'; mvardata.label = label; - siz = [size(coeffs) 1]; - coeffs = reshape(coeffs, siz(2:end)); - siz = [size(noisecov) 1]; + siz = [size(coeffs) 1]; + coeffs = reshape(coeffs, siz(2:end)); + siz = [size(noisecov) 1]; if ~all(siz==1) noisecov = reshape(noisecov, siz(2:end)); end @@ -528,46 +546,46 @@ mvardata.fsampleorig = data.fsample; switch cfg.output - case 'parameters' - % no output requested, do not re-compile time-series data - - case {'model' 'residual'} - if keeprpt || dojack - error('reconstruction of the residuals with keeprpt or dojack is not yet implemented'); - end - - dataout = keepfields(data, {'hdr','grad','fsample','trialinfo','label','cfg'}); - trial = cell(1,numel(data.trial)); - time = cell(1,numel(data.time)); - for k = 1:numel(data.trial) - if strcmp(cfg.output, 'model') - trial{k} = zeros(size(data.trial{k},1), size(data.trial{k},2)-cfg.order); - else - trial{k} = data.trial{k}(:, (cfg.order+1):end); - end - time{k} = data.time{k}((cfg.order+1):end); - for m = 1:cfg.order - if dounivariate - P = diag(mvardata.coeffs(:,m)); - else - P = mvardata.coeffs(:,:,m); - end - - if strcmp(cfg.output, 'residual') - P = -P; - end - - trial{k} = trial{k} + P * data.trial{k}(:,(cfg.order+1-m):(end-m)); - end - end - dataout.trial = trial; - dataout.time = time; + case 'parameters' + % no output requested, do not re-compile time-series data + + case {'model' 'residual'} + if keeprpt || dojack + error('reconstruction of the residuals with keeprpt or dojack is not yet implemented'); + end + + dataout = keepfields(data, {'hdr','grad','fsample','trialinfo','label','cfg'}); + trial = cell(1,numel(data.trial)); + time = cell(1,numel(data.time)); + for k = 1:numel(data.trial) + if strcmp(cfg.output, 'model') + trial{k} = zeros(size(data.trial{k},1), size(data.trial{k},2)-cfg.order); + else + trial{k} = data.trial{k}(:, (cfg.order+1):end); + end + time{k} = data.time{k}((cfg.order+1):end); + for m = 1:cfg.order + if dounivariate + P = diag(mvardata.coeffs(:,m)); + else + P = mvardata.coeffs(:,:,m); + end + + if strcmp(cfg.output, 'residual') + P = -P; + end + + trial{k} = trial{k} + P * data.trial{k}(:,(cfg.order+1-m):(end-m)); + end + end + dataout.trial = trial; + dataout.time = time; cfg.coeffs = mvardata.coeffs; - cfg.noisecov = mvardata.noisecov; + cfg.noisecov = mvardata.noisecov; mvardata = dataout; clear dataout; - otherwise - error('output ''%s'' is not supported', cfg.output); + otherwise + error('output ''%s'' is not supported', cfg.output); end % do the general cleanup and bookkeeping at the end of the function @@ -600,14 +618,14 @@ endsmp = sumsmp(k+1) + (k-1)*nnans; end if ~dobvar && isempty(taper) - datamatrix(:,begsmp:endsmp) = datacells{trials(k)}(chanindx,:); - elseif ~dobvar && ~isempty(taper) - % FIXME this will crash with variable data length and fixed length - % taper + datamatrix(:,begsmp:endsmp) = datacells{trials(k)}(chanindx,:); + elseif ~dobvar && ~isempty(taper) + % FIXME this will crash with variable data length and fixed length + % taper datamatrix(:,begsmp:endsmp) = datacells{trials(k)}(chanindx,:).*taper(ones(nchan,1),:); - elseif dobvar && isempty(taper) - datamatrix(:,begsmp:endsmp) = datacells{trials(k)}(chanindx',:); - elseif dobvar && ~isempty(taper) + elseif dobvar && isempty(taper) + datamatrix(:,begsmp:endsmp) = datacells{trials(k)}(chanindx',:); + elseif dobvar && ~isempty(taper) datamatrix(:,begsmp:endsmp) = datacells{trials(k)}(chanindx',:).*taper(ones(nchan,1),:); end end diff --git a/external/fieldtrip/ft_prepare_headmodel.m b/external/fieldtrip/ft_prepare_headmodel.m index bf822868..c3ad1787 100644 --- a/external/fieldtrip/ft_prepare_headmodel.m +++ b/external/fieldtrip/ft_prepare_headmodel.m @@ -64,6 +64,7 @@ % % CONCENTRICSPHERES % cfg.tissue see above; in combination with 'seg' input +% cfg.order (optional) % cfg.fitind (optional) % % LOCALSPHERES @@ -79,6 +80,7 @@ % % SINGLESHELL % cfg.tissue see above; in combination with 'seg' input; default options are 'brain' or 'scalp' +% cfg.order (optional) % % SINGLESPHERE % cfg.tissue see above; in combination with 'seg' input; default options are 'brain' or 'scalp'; must be only 1 value @@ -117,7 +119,7 @@ % Copyright (C) 2011, Cristiano Micheli % Copyright (C) 2011-2012, Jan-Mathijs Schoffelen, Robert Oostenveld -% Copyright (C) 2013, Robert Oostenveld, Johanna Zumer +% Copyright (C) 2013-2018, Robert Oostenveld, Johanna Zumer % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -146,7 +148,7 @@ ft_defaults ft_preamble init ft_preamble trackconfig -ft_preamble provenance +ft_preamble provenance data % the ft_abort variable is set to true or false in ft_preamble_init if ft_abort @@ -177,7 +179,6 @@ % other options cfg.numvertices = ft_getopt(cfg, 'numvertices', 3000); cfg.isolatedsource = ft_getopt(cfg, 'isolatedsource'); % used for dipoli and openmeeg -cfg.fitind = ft_getopt(cfg, 'fitind'); % used for concentricspheres cfg.point = ft_getopt(cfg, 'point'); % used for halfspace cfg.submethod = ft_getopt(cfg, 'submethod'); % used for halfspace cfg.feedback = ft_getopt(cfg, 'feedback'); @@ -262,7 +263,7 @@ tmpcfg.tissue = cfg.tissue; geometry = ft_prepare_mesh(tmpcfg, data); else - ft_error('Either a segmentated MRI or data with closed triangulated mesh is required as data input for the bemcp, dipoli or openmeeg method'); + ft_error('Either a segmented MRI or data with closed triangulated mesh is required as data input for the bemcp, dipoli or openmeeg method'); end if strcmp(cfg.method, 'bemcp') @@ -279,10 +280,13 @@ elseif strcmp(cfg.method, 'dipoli') headmodel = ft_headmodel_dipoli(geometry, 'conductivity', cfg.conductivity, 'isolatedsource', cfg.isolatedsource); else - headmodel = ft_headmodel_openmeeg(geometry, 'conductivity', cfg.conductivity, 'isolatedsource', cfg.isolatedsource); + headmodel = ft_headmodel_openmeeg(geometry, 'conductivity', cfg.conductivity, 'isolatedsource', cfg.isolatedsource, 'tissue', cfg.tissue); end case 'concentricspheres' + cfg.fitind = ft_getopt(cfg, 'fitind'); + cfg.order = ft_getopt(cfg, 'order'); + % the low-level functions needs surface points, triangles are not needed if input_mesh || input_pos geometry = data; @@ -304,7 +308,7 @@ ft_error('You must give a mesh, segmented MRI, sensor data type, or cfg.headshape'); end - headmodel = ft_headmodel_concentricspheres(geometry, 'conductivity', cfg.conductivity, 'fitind', cfg.fitind); + headmodel = ft_headmodel_concentricspheres(geometry, 'conductivity', cfg.conductivity, 'fitind', cfg.fitind, 'order', cfg.order); case 'halfspace' if input_mesh || input_pos @@ -325,7 +329,7 @@ case {'localspheres' 'singlesphere' 'singleshell'} cfg.grad = ft_getopt(cfg, 'grad'); % used for localspheres - % these three methods all require a set of surface points + % these three methods all require a single set of surface points if input_mesh || input_pos geometry = data; elseif input_seg @@ -342,16 +346,28 @@ try tmpcfg.tissue = 'brain'; geometry = ft_prepare_mesh(tmpcfg, data); + catch + me = lasterror; + if isequal(me.identifier, 'MATLAB:mex:ErrInvalidMEXFile') + % SPM8 mex file issues are common on macOS, these should not remain invisible + rethrow(me); + end end end if isempty(geometry) try tmpcfg.tissue = 'scalp'; geometry = ft_prepare_mesh(tmpcfg, data); + catch + me = lasterror; + if isequal(me.identifier, 'MATLAB:mex:ErrInvalidMEXFile') + % SPM8 mex file issues are common on macOS, these should not remain invisible + rethrow(me); + end end end if isempty(geometry) - ft_error('please specificy cfg.tissue and pass an appropriate segmented MRI as input data') + ft_error('please specify cfg.tissue and pass an appropriate segmented MRI as input data') end end elseif input_elec @@ -387,6 +403,7 @@ % construct the volume conduction model headmodel = ft_headmodel_singlesphere(geometry, 'conductivity', cfg.conductivity); end % headmodel + case 'localspheres' if ~isempty(cfg.headmodel) % read the volume conduction model from a CTF *.hdm file @@ -408,14 +425,19 @@ end headmodel = ft_headmodel_localspheres(geometry, cfg.grad, 'feedback', cfg.feedback, 'radius', cfg.radius, 'maxradius', cfg.maxradius, 'baseline', cfg.baseline, 'singlesphere', cfg.singlesphere); end % headmodel + case 'singleshell' + cfg.order = ft_getopt(cfg, 'order'); if ~isfield(geometry, 'tri') tmpcfg = []; tmpcfg.headshape = geometry; geometry = ft_prepare_mesh(tmpcfg); end - headmodel = ft_headmodel_singleshell(geometry); - end + headmodel = ft_headmodel_singleshell(geometry, 'order', cfg.order); + + otherwise + ft_error('unsupported method %s', cfg.method); + end % switch method case {'simbio'} if input_elec || isfield(data, 'pos') || input_mesh diff --git a/external/fieldtrip/ft_prepare_layout.m b/external/fieldtrip/ft_prepare_layout.m index 6b9c1ddb..321b262e 100644 --- a/external/fieldtrip/ft_prepare_layout.m +++ b/external/fieldtrip/ft_prepare_layout.m @@ -90,8 +90,12 @@ % undocumented and non-recommended option (for SPM only) % cfg.style string, '2d' or '3d' (default = '2d') +% undocumented, because inconsistent with cfg.rotate +% cfg.center = string, can be 'yes' or 'no' (default = 'no') +% cfg.width = [] or number +% cfg.height = [] or number -% Copyright (C) 2007-2013, Robert Oostenveld +% Copyright (C) 2007-2018, Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -143,7 +147,8 @@ % set default configuration options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -cfg.rotate = ft_getopt(cfg, 'rotate', []); % [] => rotation is determined based on the type of sensors +cfg.rotate = ft_getopt(cfg, 'rotate', []); % [] => default rotation is determined based on the type of sensors +cfg.center = ft_getopt(cfg, 'translate', 'no'); cfg.style = ft_getopt(cfg, 'style', '2d'); cfg.projection = ft_getopt(cfg, 'projection', 'polar'); cfg.layout = ft_getopt(cfg, 'layout', []); @@ -169,17 +174,23 @@ cfg.mri = ft_getopt(cfg, 'mri', []); cfg.outline = ft_getopt(cfg, 'outline', []); % default is handled below cfg.mask = ft_getopt(cfg, 'mask', []); % default is handled below +cfg.width = ft_getopt(cfg, 'width', []); +cfg.height = ft_getopt(cfg, 'height', []); -if isempty(cfg.skipscale) && ischar(cfg.layout) && any(strcmp(cfg.layout, {'ordered', 'vertical', 'horizontal', 'butterfly', 'circular', '1column', '2column', '3column', '4column', '5column', '6column', '7column', '8column', '9column', '1row', '2row', '3row', '4row', '5row', '6row', '7row', '8row', '9row'})) - cfg.skipscale = 'yes'; -else - cfg.skipscale = 'no'; +if isempty(cfg.skipscale) + if ischar(cfg.layout) && any(strcmp(cfg.layout, {'ordered', 'vertical', 'horizontal', 'butterfly', 'circular', '1column', '2column', '3column', '4column', '5column', '6column', '7column', '8column', '9column', '1row', '2row', '3row', '4row', '5row', '6row', '7row', '8row', '9row'})) + cfg.skipscale = 'yes'; + else + cfg.skipscale = 'no'; + end end -if isempty(cfg.skipcomnt) && ischar(cfg.layout) && any(strcmp(cfg.layout, {'ordered', 'vertical', 'horizontal', 'butterfly', 'circular', '1column', '2column', '3column', '4column', '5column', '6column', '7column', '8column', '9column', '1row', '2row', '3row', '4row', '5row', '6row', '7row', '8row', '9row'})) - cfg.skipcomnt = 'yes'; -else - cfg.skipcomnt = 'no'; +if isempty(cfg.skipcomnt) + if ischar(cfg.layout) && any(strcmp(cfg.layout, {'ordered', 'vertical', 'horizontal', 'butterfly', 'circular', '1column', '2column', '3column', '4column', '5column', '6column', '7column', '8column', '9column', '1row', '2row', '3row', '4row', '5row', '6row', '7row', '8row', '9row'})) + cfg.skipcomnt = 'yes'; + else + cfg.skipcomnt = 'no'; + end end if isempty(cfg.outline) @@ -314,7 +325,7 @@ layout.height = ones(nchan,1) * 1.0; layout.mask = {}; layout.outline = {}; - + elseif isequal(cfg.layout, 'vertical') || isequal(cfg.layout,'horizontal') if hasdata && ~isempty(data) % look at the data to determine the overlapping channels @@ -443,7 +454,7 @@ layout.mask = {}; layout.outline = {}; - + elseif isequal(cfg.layout, 'ordered') if hasdata % look at the data to determine the overlapping channels @@ -488,7 +499,7 @@ layout.mask = {}; layout.outline = {}; - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % try to generate layout from other configuration options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -499,7 +510,7 @@ cfg.layout = [cfg.layout '.mat']; if exist(cfg.layout, 'file') - fprintf('layout file without .mat (or .lay) extension specified, appending .mat\n'); + ft_info('layout file without .mat (or .lay) extension specified, appending .mat\n'); layout = ft_prepare_layout(cfg); return; else @@ -510,7 +521,7 @@ elseif ft_filetype(cfg.layout, 'matlab') - fprintf('reading layout from file %s\n', cfg.layout); + ft_info('reading layout from file %s\n', cfg.layout); if ~exist(cfg.layout, 'file') ft_error('the specified layout file %s was not found', cfg.layout); end @@ -526,7 +537,7 @@ elseif ft_filetype(cfg.layout, 'layout') if exist(cfg.layout, 'file') - fprintf('reading layout from file %s\n', cfg.layout); + ft_info('reading layout from file %s\n', cfg.layout); layout = readlay(cfg.layout); else [p, f, x] = fileparts(cfg.layout); @@ -538,73 +549,73 @@ elseif ~ft_filetype(cfg.layout, 'layout') % assume that cfg.layout is an electrode file - fprintf('creating layout from sensor description file %s\n', cfg.layout); + ft_info('creating layout from sensor description file %s\n', cfg.layout); sens = ft_read_sens(cfg.layout); layout = sens2lay(sens, cfg.rotate, cfg.projection, cfg.style, cfg.overlap, cfg.viewpoint, cfg.boxchannel); end elseif ischar(cfg.gradfile) - fprintf('creating layout from gradiometer file %s\n', cfg.gradfile); + ft_info('creating layout from gradiometer file %s\n', cfg.gradfile); sens = ft_read_sens(cfg.gradfile, 'senstype', 'meg'); layout = sens2lay(sens, cfg.rotate, cfg.projection, cfg.style, cfg.overlap, cfg.viewpoint, cfg.boxchannel); elseif ~isempty(cfg.grad) && isstruct(cfg.grad) - fprintf('creating layout from cfg.grad\n'); + ft_info('creating layout from cfg.grad\n'); sens = ft_datatype_sens(cfg.grad); layout = sens2lay(sens, cfg.rotate, cfg.projection, cfg.style, cfg.overlap, cfg.viewpoint, cfg.boxchannel); elseif isfield(data, 'grad') && isstruct(data.grad) - fprintf('creating layout from data.grad\n'); + ft_info('creating layout from data.grad\n'); sens = ft_datatype_sens(data.grad); layout = sens2lay(sens, cfg.rotate, cfg.projection, cfg.style, cfg.overlap, cfg.viewpoint, cfg.boxchannel); elseif ischar(cfg.elecfile) - fprintf('creating layout from electrode file %s\n', cfg.elecfile); + ft_info('creating layout from electrode file %s\n', cfg.elecfile); sens = ft_read_sens(cfg.elecfile, 'senstype', 'eeg'); layout = sens2lay(sens, cfg.rotate, cfg.projection, cfg.style, cfg.overlap, cfg.viewpoint, cfg.boxchannel); elseif ~isempty(cfg.elec) && isstruct(cfg.elec) - fprintf('creating layout from cfg.elec\n'); + ft_info('creating layout from cfg.elec\n'); sens = ft_datatype_sens(cfg.elec); layout = sens2lay(sens, cfg.rotate, cfg.projection, cfg.style, cfg.overlap, cfg.viewpoint, cfg.boxchannel); elseif isfield(data, 'elec') && isstruct(data.elec) - fprintf('creating layout from data.elec\n'); + ft_info('creating layout from data.elec\n'); sens = ft_datatype_sens(data.elec); layout = sens2lay(sens, cfg.rotate, cfg.projection, cfg.style, cfg.overlap, cfg.viewpoint, cfg.boxchannel); elseif ischar(cfg.optofile) - fprintf('creating layout from optode file %s\n', cfg.optofile); + ft_info('creating layout from optode file %s\n', cfg.optofile); sens = ft_read_sens(cfg.optofile, 'senstype', 'nirs'); if (hasdata) - layout = opto2lay(sens, data.label); + layout = opto2lay(sens, data.label, cfg.rotate); else - layout = opto2lay(sens, sens.label); + layout = opto2lay(sens, sens.label, cfg.rotate); end elseif ~isempty(cfg.opto) && isstruct(cfg.opto) - fprintf('creating layout from cfg.opto\n'); + ft_info('creating layout from cfg.opto\n'); sens = cfg.opto; if (hasdata) - layout = opto2lay(sens, data.label); + layout = opto2lay(sens, data.label, cfg.rotate); else - layout = opto2lay(sens, sens.label); + layout = opto2lay(sens, sens.label, cfg.rotate); end elseif isfield(data, 'opto') && isstruct(data.opto) - fprintf('creating layout from data.opto\n'); + ft_info('creating layout from data.opto\n'); sens = data.opto; if (hasdata) - layout = opto2lay(sens, data.label); + layout = opto2lay(sens, data.label, cfg.rotate); else - layout = opto2lay(sens, sens.label); + layout = opto2lay(sens, sens.label, cfg.rotate); end elseif (~isempty(cfg.image) || ~isempty(cfg.mesh)) && isempty(cfg.layout) % deal with image file if ~isempty(cfg.image) - fprintf('reading background image from %s\n', cfg.image); + ft_info('reading background image from %s\n', cfg.image); [p, f, e] = fileparts(cfg.image); switch e case '.mat' @@ -928,6 +939,16 @@ layout.height = layout.height(chansel); end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% overrule the width and height when required +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if ~isempty(cfg.width) + layout.width(:) = cfg.width; +end +if ~isempty(cfg.height) + layout.height(:) = cfg.height; +end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % check whether the outline and mask are available, create them if needed %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -942,8 +963,15 @@ sel = setdiff(1:length(layout.label), [ind_scale ind_comnt]); % these are excluded for scaling x = layout.pos(sel,1); y = layout.pos(sel,2); - xrange = range(x); - yrange = range(y); + if istrue(cfg.center) + % the following centers all electrodes around zero + xrange = range(x); + yrange = range(y); + else + % the following prevent topography distortion in case electrodes are not evenly distributed over the whole head + xrange = 2*( max(max(x),abs(min(x)) )); + yrange = 2*( max(max(y),abs(min(y)) )); + end if xrange==0 xrange = 1; end @@ -1084,7 +1112,7 @@ % to write the layout to a .mat or text file, you can use this code snippet if ~isempty(cfg.output) && ~strcmpi(cfg.style, '3d') - fprintf('writing layout to ''%s''\n', cfg.output); + ft_info('writing layout to ''%s''\n', cfg.output); if strcmpi(cfg.output((end-3):end), '.mat') save(cfg.output,'layout'); else @@ -1177,14 +1205,14 @@ sens.chanpos = chanposold; end % In case not all the locations have NaNs it might still be useful to plot them - % But perhaps it'd be better to have any(any + % But perhaps it'd be better to have any elseif any(all(isnan(sens.chanpos))) [sel1, sel2] = match_str(sens.label, sens.labelold); sens.chanpos = chanposold(sel2, :); sens.label = sens.labelold(sel2); end -fprintf('creating layout for %s system\n', ft_senstype(sens)); +ft_info('creating layout for %s system\n', ft_senstype(sens)); % apply rotation, but only if viewpoint is not used specifically if isempty(viewpoint) @@ -1319,26 +1347,47 @@ % SUBFUNCTION % convert 2D optode positions into 2D layout %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function layout = opto2lay(opto, label) +function layout = opto2lay(opto, label, rotatez) + +if isempty(rotatez) + rotatez = 90; +end + layout = []; -layout.pos = []; -layout.label = {}; -layout.width = []; +layout.pos = []; +layout.label = {}; +layout.width = []; layout.height = []; [rxnames, rem] = strtok(label, {'-', ' '}); -[txnames, rem] = strtok(rem, {'-', ' '}); +[txnames, rem] = strtok(rem, {'-', ' '}); for i=1:numel(label) % create average positions rxid = ismember(opto.fiberlabel, rxnames(i)); txid = ismember(opto.fiberlabel, txnames(i)); layout.pos(i, :) = opto.fiberpos(rxid, :)/2 + opto.fiberpos(txid, :)/2; - layout.label(end+1) = label(i); - layout.width(end+1) = 1; - layout.height(end+1) = 1; end +layout.label = label; +layout.width = ones(numel(label),1); +layout.height = ones(numel(label),1); + +% apply the rotation around the z-axis +layout.pos = ft_warp_apply(rotate([0 0 rotatez]), layout.pos, 'homogenous'); + +% prevent the circle-with-ears-and-nose to be added +layout.outline = {}; + +% construct a mask for topographic interpolation +pos1 = layout.pos; pos1(:,1) = pos1(:,1)-layout.width; +pos2 = layout.pos; pos2(:,1) = pos2(:,1)+layout.width; +pos3 = layout.pos; pos3(:,2) = pos3(:,2)-layout.height; +pos4 = layout.pos; pos4(:,2) = pos4(:,2)+layout.height; +pos = [pos1; pos2; pos3; pos4]; +indx = convhull(pos); +layout.mask{1} = pos(indx,:); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION @@ -1353,7 +1402,7 @@ y = xy(2,:); l=1; -i=1; %filler +i=1; % filler mindist = mindist/0.999; % limits the number of loops while (~isempty(i) && l<50) xdiff = repmat(x,length(x),1) - repmat(x',1,length(x)); @@ -1386,7 +1435,7 @@ % the viewpoint and coordsys. See also ELPROJ and COORDSYS2LABEL %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function pos = getorthoviewpos(pos, coordsys, viewpoint) -% see also +% see also if size(pos,2)~=3 ft_error('XYZ coordinates are required to obtain the orthographic projections based on a viewpoint') @@ -1432,7 +1481,6 @@ ft_error('orthographic projection using coordinate system "%s" is not supported', coordsys) end % switch coordsys - % extract xy pos = ft_warp_apply(transmat, pos, 'homogenous'); pos = pos(:,[1 2]); @@ -1494,7 +1542,7 @@ if ~isempty(cfg.headshape) if ischar(cfg.headshape) && exist(cfg.headshape, 'file') - fprintf('reading headshape from file %s\n', cfg.headshape); + ft_info('reading headshape from file %s\n', cfg.headshape); outlbase = ft_read_headshape(cfg.headshape); elseif isstruct(cfg.headshape) outlbase = cfg.headshape; @@ -1503,7 +1551,7 @@ end elseif ~isempty(cfg.mri) if ischar(cfg.mri) && exist(cfg.mri, 'file') - fprintf('reading MRI from file %s\n', cfg.mri); + ft_info('reading MRI from file %s\n', cfg.mri); outlbase = ft_read_mri(cfg.mri); elseif ft_datatype(cfg.mri, 'volume') outlbase = cfg.mri; @@ -1554,10 +1602,10 @@ rotatez = 0; end end - braincoords = ft_warp_apply(rotate([0 0 rotatez]), braincoords, 'homogenous'); + braincoords = ft_warp_apply(rotate([0 0 rotatez]), braincoords, 'homogenous'); braincoords = elproj(braincoords, cfg.projection); end - + % get outline k = boundary(braincoords,.8); outline{i} = braincoords(k,:); diff --git a/external/fieldtrip/ft_prepare_leadfield.m b/external/fieldtrip/ft_prepare_leadfield.m index 1ca4487f..a5f645e8 100644 --- a/external/fieldtrip/ft_prepare_leadfield.m +++ b/external/fieldtrip/ft_prepare_leadfield.m @@ -47,6 +47,24 @@ % whether the lower rank leadfield is projected back onto the original % linear subspace, or not. % +% Depending on the type of headmodel, some additional options may be +% specified. +% +% For OPENMEEG based headmodels: +% cfg.openmeeg.batchsize = scalar (default 100e3), number of dipoles +% for which the leadfield is computed in a +% single call to the low-level code. Trades off +% memory efficiency for speed. +% cfg.openmeeg.dsm = 'no'/'yes', reuse existing DSM if provided +% cfg.openmeeg.keepdsm = 'no'/'yes', option to retain DSM (no by default) +% cfg.openmeeg.nonadaptive = 'no'/'yes' +% +% For SINGLESHELL based headmodels: +% cfg.singleshell.batchsize = scalar or 'all' (default 1), number of dipoles +% for which the leadfield is computed in a +% single call to the low-level code. Trades off +% memory efficiency for speed. +% % To facilitate data-handling and distributed computing you can use % cfg.inputfile = ... % If you specify this option the input data will be read from a *.mat @@ -115,6 +133,7 @@ % check if the input cfg is valid for this function cfg = ft_checkconfig(cfg, 'renamed', {'hdmfile', 'headmodel'}); cfg = ft_checkconfig(cfg, 'renamed', {'vol', 'headmodel'}); +cfg = ft_checkconfig(cfg, 'renamed', {'om', 'openmeeg'}); % set the defaults cfg.normalize = ft_getopt(cfg, 'normalize', 'no'); @@ -134,7 +153,7 @@ % this code expects the inside to be represented as a logical array cfg.grid = ft_checkconfig(cfg.grid, 'renamed', {'pnt' 'pos'}); -cfg = ft_checkconfig(cfg, 'index2logical', 'yes'); +cfg = ft_checkconfig(cfg, 'inside2logical', 'yes'); if strcmp(cfg.sel50p, 'yes') && strcmp(cfg.lbex, 'yes') ft_error('subspace projection with either lbex or sel50p is mutually exclusive'); @@ -169,54 +188,115 @@ end end +% find the indices of all grid points that are inside the brain +insideindx = find(grid.inside); + if ft_voltype(headmodel, 'openmeeg') % repeated system calls to the openmeeg executable makes it rather slow % calling it once is much more efficient fprintf('calculating leadfield for all positions at once, this may take a while...\n'); - - % find the indices of all grid points that are inside the brain - insideindx = find(grid.inside); + + if(~isfield(cfg,'openmeeg')) + cfg.openmeeg = []; + end + batchsize = ft_getopt(cfg.openmeeg, 'batchsize',100e3); % number of voxels per DSM batch; set to e.g. 1000 if not much RAM available + dsm = ft_getopt(cfg.openmeeg, 'dsm'); % reuse existing DSM if provided + keepdsm = ft_getopt(cfg.openmeeg, 'keepdsm', 'no'); % retain DSM + nonadaptive = ft_getopt(cfg.openmeeg, 'nonadaptive', 'no'); + ndip = length(insideindx); - ok = false(1,ndip); - batchsize = ndip; - - while ~all(ok) - % find the first one that is not yet done - begdip = find(~ok, 1); - % define a batch of dipoles to jointly deal with - enddip = min((begdip+batchsize-1), ndip); % don't go beyond the end - batch = begdip:enddip; - try - lf = ft_compute_leadfield(grid.pos(insideindx(batch),:), sens, headmodel, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam); - ok(batch) = true; - catch - ok(batch) = false; - % the "catch me" syntax is broken on MATLAB74, this fixes it - me = lasterror; - if ~isempty(findstr(me.message, 'Output argument "dsm" (and maybe others) not assigned during call to')) - % it does not fit in memory, split the problem in two halves and try once more - batchsize = floor(batchsize/500); - continue - else - rethrow(me); - end % handling this particular error + numchunks = ceil(ndip/batchsize); + if(numchunks > 1) + if istrue(keepdsm) + ft_warning('Keeping DSM output not supported when the computation is split into batches') end - - % reassign the large leadfield matrix over the single grid locations - for i=1:length(batch) - sel = (3*i-2):(3*i); % 1:3, 4:6, ... - dipindx = insideindx(batch(i)); - grid.leadfield{dipindx} = lf(:,sel); + keepdsm = false; + end + + try + % DSM computation is computationally intensive: + % As it can be reused with same voxel grid (i.e. if voxels are defined in + % MRI coordinates rather than MEG coordinates), optionally save result. + % Dense voxel grids may require several gigabytes of RAM, so optionally + % split into smaller batches + + [h2sens,ds2sens] = ft_sensinterp_openmeeg(grid.pos(insideindx,:), headmodel, sens); + + % use pre-existing DSM if present + if(~isempty(dsm)) + lf = ds2sens + h2sens*headmodel.mat*dsm; + else + lf = zeros(size(ds2sens)); % pre-allocate Msensors x Nvoxels + + for ii = 1:numchunks + % select grid positions for this batch + diprange = (((ii-1)*batchsize + 1):(min((ii)*batchsize,ndip))); + % remap with 3 orientations per position + diprangeori = [((ii-1)*3*batchsize + 1):(min((ii)*3*batchsize,3*ndip))]; + dsm = ft_sysmat_openmeeg(grid.pos(insideindx(diprange),:), headmodel, sens, nonadaptive); + lf(:,diprangeori) = ds2sens(:,diprangeori) + h2sens*headmodel.mat*dsm; + + if istrue(keepdsm) + % retain DSM in cfg if desired + cfg.openmeeg.dsm = dsm; + end + + dipindx = insideindx(diprange); + end end - - clear lf - - end % while - + catch + me = lasterror; + rethrow(me); + end + + % apply montage, if applicable + if isfield(sens, 'tra') + lf = sens.tra * lf; + end + + % lead field computation already done, but pass to ft_compute_leadfield so that + % any post-computation options can be applied (e.g., normalization, etc.) + lf = ft_compute_leadfield(grid.pos(diprange,:), sens, headmodel, 'lf', lf, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject); + + % reshape result into grid.leadfield cell array + for i=1:ndip + grid.leadfield{insideindx(i)} = lf(:,3*(i-1) + [1:3]); + end + clear lf + +elseif ft_voltype(headmodel, 'singleshell') + cfg.singleshell = ft_getopt(cfg, 'singleshell', []); + batchsize = ft_getopt(cfg.singleshell, 'batchsize', 1); + if ischar(batchsize) && strcmp(batchsize, 'all') + batchsize = length(insideindx); + end + + dippos = grid.pos(insideindx,:); + ndip = length(insideindx); + numchunks = ceil(ndip/batchsize); + + ft_progress('init', cfg.feedback, 'computing leadfield'); + for k = 1:numchunks + ft_progress(k/numchunks, 'computing leadfield %d/%d\n', k, numchunks); + diprange = (((k-1)*batchsize + 1):(min(k*batchsize,ndip))); + tmp = ft_compute_leadfield(dippos(diprange,:), sens, headmodel, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject); + for i=1:length(diprange) + thisindx = insideindx(diprange(i)); + if istrue(cfg.backproject) + grid.leadfield{thisindx} = tmp(:,(i-1)*3+(1:3)); + else + grid.leadfield{thisindx} = tmp(:,(i-1)*cfg.reducerank+(1:cfg.reducerank)); + end + + if isfield(cfg, 'grid') && isfield(cfg.grid, 'mom') + % multiply with the normalized dipole moment to get the leadfield in the desired orientation + grid.leadfield{thisindx} = grid.leadfield{thisindx} * grid.mom(:,thisindx); + end + end + end + ft_progress('close'); + else - % find the indices of all grid points that are inside the brain - insideindx = find(grid.inside); - ft_progress('init', cfg.feedback, 'computing leadfield'); for i=1:length(insideindx) % compute the leadfield on all grid positions inside the brain diff --git a/external/fieldtrip/ft_prepare_mesh.m b/external/fieldtrip/ft_prepare_mesh.m index d1260eb6..5c7b08e1 100644 --- a/external/fieldtrip/ft_prepare_mesh.m +++ b/external/fieldtrip/ft_prepare_mesh.m @@ -133,8 +133,10 @@ % optionally downsample the anatomical volume and/or tissue segmentations tmpcfg = keepfields(cfg, {'downsample', 'showcallinfo'}); mri = ft_volumedownsample(tmpcfg, mri); - % restore the provenance information + % restore the provenance information and put back cfg.smooth + tmpsmooth = cfg.smooth; [cfg, mri] = rollback_provenance(cfg, mri); + cfg.smooth = tmpsmooth; end switch cfg.method diff --git a/external/fieldtrip/ft_prepare_montage.m b/external/fieldtrip/ft_prepare_montage.m index 4a6e18f7..deabe1e4 100644 --- a/external/fieldtrip/ft_prepare_montage.m +++ b/external/fieldtrip/ft_prepare_montage.m @@ -2,16 +2,20 @@ % FT_PREPARE_MONTAGE creates a referencing scheme based on the input configuration % options and the channels in the data structure. The resulting montage can be -% given as input to ft_apply_montage, or as cfg.montage to ft_preprocessing. +% given as input to FT_APPLY_MONTAGE, or as cfg.montage to FT_PREPROCESSING. % % Use as % montage = ft_prepare_montage(cfg, data) % % The configuration can contain the following fields: -% cfg.implicitref = 'label' or empty, add the implicit EEG reference as zeros (default = []) +% cfg.refmethod = 'avg', 'bioloar', 'comp' (default = 'avg') +% cfg.implicitref = string with the label of the implicit reference, or empty (default = []) % cfg.refchannel = cell-array with new EEG reference channel(s), this can be 'all' for a common average reference % -% See also FT_PREPROCESSING +% The implicitref option allows adding the implicit reference channel to the data as +% a channel with zeros. +% +% See also FT_PREPROCESSING, FT_APPLY_MONTAGE % Copyright (C) 2017, Robert Oostenveld % @@ -56,23 +60,25 @@ if ~hasdata data = struct([]); else - data = ft_checkdata(data); + data = ft_checkdata(data, 'datatype', {'raw', 'raw+comp', 'timelock', 'timelock+comp'}); end -% do a sanity check for incompatible options which are used in ft_preprocessing -cfg = ft_checkconfig(cfg, 'forbidden', {'refmethod', 'montage'}); +% do a sanity check for incompatible options which are used in ft_preprocessing and elsewhere +cfg = ft_checkconfig(cfg, 'forbidden', {'montage', 'method'}); % set default configuration options -cfg.reref = ft_getopt(cfg, 'reref', 'yes'); +cfg.refmethod = ft_getopt(cfg, 'refmethod', 'avg'); cfg.channel = ft_getopt(cfg, 'channel', 'all'); cfg.implicitref = ft_getopt(cfg, 'implicitref'); cfg.refchannel = ft_getopt(cfg, 'refchannel'); -assert(istrue(cfg.reref), 'cannot create a montage without cfg.reref=''yes'''); +if isfield(cfg, 'reref') + assert(istrue(cfg.reref), 'cannot create a montage without cfg.reref=''yes'''); +end % here the actual work starts if hasdata - cfg.channel = ft_channelselection(cfg.channel, data.label); + cfg.channel = ft_channelselection(cfg.channel, data.label); cfg.refchannel = ft_channelselection(cfg.refchannel, cat(1, data.label(:), cfg.implicitref)); else if ischar(cfg.channel) @@ -94,16 +100,51 @@ montage1.tra = eye(numel(montage1.labelnew), numel(montage1.labelold)); % the second montage serves to subtract the selected reference channels -montage2 = []; -montage2.labelold = montage1.labelnew; -montage2.labelnew = montage1.labelnew; -montage2.tra = eye(numel(montage2.labelnew)); -refsel = match_str(montage2.labelold, cfg.refchannel); -% subtract the mean of the reference channels -montage2.tra(:,refsel) = montage2.tra(:,refsel) - 1/numel(refsel); -% apply montage2 to montage1, the result is the combination of both -montage = ft_apply_montage(montage1, montage2); +switch cfg.refmethod + case 'avg' + % make a montage that subtracts the mean of all channels specified in cfg.refchannel + montage2 = []; + montage2.labelold = montage1.labelnew; + montage2.labelnew = montage1.labelnew; + montage2.tra = eye(numel(montage2.labelnew)); + refsel = match_str(montage2.labelold, cfg.refchannel); + montage2.tra(:,refsel) = montage2.tra(:,refsel) - 1/numel(refsel); + + % apply montage2 to montage1, the result is the combination of both + montage = ft_apply_montage(montage1, montage2); + + case 'bipolar' + % make a montage for the bipolar derivation of sequential channels + montage2 = []; + montage2.labelold = montage1.labelnew; + montage2.labelnew = strcat(montage1.labelnew(1:end-1),'-',montage1.labelnew(2:end)); + tra_neg = diag(-ones(numel(montage1.labelnew)-1,1), 1); + tra_plus = diag( ones(numel(montage1.labelnew)-1,1),-1); + montage2.tra = tra_neg(1:end-1,:)+tra_plus(2:end,:); + + % apply montage2 to montage1, the result is the combination of both + montage = ft_apply_montage(montage1, montage2); + + case 'comp' + assert(isempty(cfg.implicitref), 'implicitref not supported with refmethod=''%s''', cfg.refmethod); + % construct an linear projection from channels to components + montage = []; + montage.labelold = data.topolabel; + montage.labelnew = data.label; + montage.tra = data.unmixing; + + case 'invcomp' + assert(isempty(cfg.implicitref), 'implicitref not supported with refmethod=''%s''', cfg.refmethod); + % construct an linear projection from components to channels + montage = []; + montage.labelold = data.label; + montage.labelnew = data.topolabel; + montage.tra = data.topo; + + otherwise + error('unsupported refmethod=''%s''', cfg.refmethod); +end % do the general cleanup and bookkeeping at the end of the function ft_postamble provenance diff --git a/external/fieldtrip/ft_prepare_sourcemodel.m b/external/fieldtrip/ft_prepare_sourcemodel.m index 675a0046..aa48a195 100644 --- a/external/fieldtrip/ft_prepare_sourcemodel.m +++ b/external/fieldtrip/ft_prepare_sourcemodel.m @@ -115,7 +115,7 @@ ft_defaults ft_preamble init ft_preamble debug -ft_preamble provenance +ft_preamble provenance headmodel sens ft_preamble trackconfig % the ft_abort variable is set to true or false in ft_preamble_init @@ -149,7 +149,7 @@ cfg.grid.template = ft_checkconfig(cfg.grid.template, 'renamed', {'pnt' 'pos'}); end end -cfg = ft_checkconfig(cfg, 'index2logical', 'yes'); +cfg = ft_checkconfig(cfg, 'inside2logical', 'yes'); if ~isfield(cfg, 'headmodel') && nargin>1 % put it in the configuration structure @@ -628,7 +628,7 @@ mnigrid = fixinside(mnigrid); % spatial normalisation of mri and construction of subject specific dipole grid positions - tmpcfg = []; + tmpcfg = keepfields(cfg, {'spmversion', 'spmmethod'}); tmpcfg.nonlinear = cfg.grid.nonlinear; if isfield(cfg.grid, 'templatemri') tmpcfg.template = cfg.grid.templatemri; diff --git a/external/fieldtrip/ft_preprocessing.m b/external/fieldtrip/ft_preprocessing.m index 50fee354..4a536aa4 100644 --- a/external/fieldtrip/ft_preprocessing.m +++ b/external/fieldtrip/ft_preprocessing.m @@ -38,6 +38,7 @@ % The channels that will be read and/or preprocessed are specified with % cfg.channel = Nx1 cell-array with selection of channels (default = 'all'), % see FT_CHANNELSELECTION for details +% cfg.chantype = string or Nx1 cell-array with channel types to be read (only for NeuroOmega) % % The preprocessing options for the selected channels are specified with % cfg.lpfilter = 'no' or 'yes' lowpass filter (default = 'no') @@ -49,7 +50,7 @@ % cfg.lpfreq = lowpass frequency in Hz % cfg.hpfreq = highpass frequency in Hz % cfg.bpfreq = bandpass frequency range, specified as [lowFreq highFreq] in Hz -% cfg.bsfreq = bandstop frequency range, specified as [low high] in Hz +% cfg.bsfreq = bandstop frequency range, specified as [low high] in Hz (or as Nx2 matrix for notch filter) % cfg.dftfreq = line noise frequencies in Hz for DFT filter (default = [50 100 150]) % cfg.lpfiltord = lowpass filter order (default set in low-level function) % cfg.hpfiltord = highpass filter order (default set in low-level function) @@ -94,7 +95,7 @@ % cfg.hilbert = 'no', 'abs', 'complex', 'real', 'imag', 'absreal', 'absimag' or 'angle' (default = 'no') % cfg.rectify = 'no' or 'yes' (default = 'no') % cfg.precision = 'single' or 'double' (default = 'double') -% cfg.absdiff = 'no' or 'yes', computes absolute derivative (i.e.first derivative then rectify) +% cfg.absdiff = 'no' or 'yes', computes absolute derivative (i.e. first derivative then rectify) % % Prperocessing options that only apply to MEG data are % cfg.coordsys = string, 'head' or 'dewar' (default = 'head') @@ -103,7 +104,7 @@ % Preprocessing options that you should only use for EEG data are % cfg.reref = 'no' or 'yes' (default = 'no') % cfg.refchannel = cell-array with new EEG reference channel(s), this can be 'all' for a common average reference -% cfg.refmethod = 'avg' or 'median' (default = 'avg') +% cfg.refmethod = 'avg', 'median', or 'bipolar' for bipolar derivation of sequential channels (default = 'avg') % cfg.implicitref = 'label' or empty, add the implicit EEG reference as zeros (default = []) % cfg.montage = 'no' or a montage structure, see FT_APPLY_MONTAGE (default = 'no') % @@ -214,6 +215,7 @@ cfg.checkmaxfilter = ft_getopt(cfg, 'checkmaxfilter'); % this allows to read non-maxfiltered neuromag data recorded with internal active shielding cfg.montage = ft_getopt(cfg, 'montage', 'no'); cfg.updatesens = ft_getopt(cfg, 'updatesens', 'no'); % in case a montage or rereferencing is specified +cfg.chantype = ft_getopt(cfg, 'chantype', {}); %2017.10.10 AB required for NeuroOmega files % these options relate to the actual preprocessing, it is neccessary to specify here because of padding cfg.dftfilter = ft_getopt(cfg, 'dftfilter', 'no'); @@ -392,7 +394,9 @@ cfg = ft_checkconfig(cfg, 'renamedval', {'continuous', 'continuous', 'yes'}); % read the header - hdr = ft_read_header(cfg.headerfile, 'headerformat', cfg.headerformat, 'coordsys', cfg.coordsys, 'coilaccuracy', cfg.coilaccuracy, 'checkmaxfilter', istrue(cfg.checkmaxfilter)); + hdr = ft_read_header(cfg.headerfile, 'headerformat', cfg.headerformat,... + 'coordsys', cfg.coordsys, 'coilaccuracy', cfg.coilaccuracy,... + 'checkmaxfilter', istrue(cfg.checkmaxfilter), 'chantype', cfg.chantype); % this option relates to reading over trial boundaries in a pseudo-continuous dataset if ~isfield(cfg, 'continuous') @@ -649,10 +653,16 @@ if ~isempty(cfg.montage) && ~isequal(cfg.montage, 'no') montage = cfg.montage; elseif strcmp(cfg.reref, 'yes') - tmpcfg = keepfields(cfg, {'reref', 'implicitref', 'refchannel', 'channel'}); - montage = ft_prepare_montage(tmpcfg, data); + if strcmp(cfg.refmethod, 'bipolar') || strcmp(cfg.refmethod, 'avg') + tmpcfg = keepfields(cfg, {'refmethod', 'implicitref', 'refchannel', 'channel'}); + tmpcfg.showcallinfo = 'no'; + montage = ft_prepare_montage(tmpcfg, data); + else + % do not update the sensor description + montage = []; + end else - % do not update anything + % do not update the sensor description montage = []; end @@ -668,7 +678,7 @@ dataout.grad = ft_apply_montage(dataout.grad, montage, 'feedback', 'none', 'keepunused', 'no', 'balancename', bname); end if isfield(dataout, 'elec') - ft_info('applying the montage to the grad structure\n'); + ft_info('applying the montage to the elec structure\n'); dataout.elec = ft_apply_montage(dataout.elec, montage, 'feedback', 'none', 'keepunused', 'no', 'balancename', bname); end if isfield(dataout, 'opto') diff --git a/external/fieldtrip/ft_qualitycheck.m b/external/fieldtrip/ft_qualitycheck.m index b702cf8e..df2c8b34 100644 --- a/external/fieldtrip/ft_qualitycheck.m +++ b/external/fieldtrip/ft_qualitycheck.m @@ -83,17 +83,18 @@ %% ANALYSIS if strcmp(cfg.analyze,'yes') tic - + % checks cfg = ft_checkconfig(cfg, 'dataset2files', 'yes'); % translate into datafile+headerfile - + % these will be replaced by more appropriate values + info.filename = cfg.dataset; info.datasetname = 'unknown'; info.starttime = 'unknown'; info.startdate = 'unknown'; info.stoptime = 'unknown'; info.stopdate = 'unknown'; - + % the exportname is also used in the cron job exportname = qualitycheck_exportname(cfg.dataset); [iseeg, ismeg, isctf, fltp] = filetyper(cfg.dataset); @@ -103,22 +104,22 @@ info = read_ctf_hist(cfg.dataset); end end - + % add info info.event = ft_read_event(cfg.dataset); info.hdr = ft_read_header(cfg.dataset); info.filetype = fltp; - + % trial definition cfgdef = []; cfgdef.dataset = cfg.dataset; cfgdef.trialdef.triallength = 10; - %cfgdef.trialdef.ntrials = 3; + %cfgdef.trialdef.ntrials = 3; % for debugging cfgdef.continuous = 'yes'; cfgdef = ft_definetrial(cfgdef); ntrials = size(cfgdef.trl,1)-1; % remove last trial timeunit = cfgdef.trialdef.triallength; - + % channelselection for jump detection (all) and for FFT (brain) if ismeg allchans = ft_channelselection({'MEG','MEGREF'}, info.hdr.label); @@ -128,12 +129,17 @@ jumpthreshold = 1e-10; elseif iseeg allchans = ft_channelselection('EEG', info.hdr.label); + if isempty(allchans) + % some EEG systems and data files use non-standard channel names that are not detected automatically + ft_warning('no EEG channels detected, selecting all channels'); + allchans = info.hdr.label; + end chans = allchans; % brain allchanindx = match_str(info.hdr.label, allchans); chanindx = match_str(chans, allchans); jumpthreshold = 1e4; end - + % find headcoil channels if isctf % this fails for older CTF data sets Nx = strmatch('HLC0011', info.hdr.label); % x nasion coil @@ -150,20 +156,20 @@ headpos.label = {'Nx';'Ny';'Nz';'Lx';'Ly';'Lz';'Rx';'Ry';'Rz'}; headpos.avg = NaN(length(headpos.label), ntrials); headpos.grad = info.hdr.grad; - + if numel(cat(1,Nx,Ny,Nz,Lx,Ly,Lz,Rx,Ry,Rz))==9 hasheadpos = true; else hasheadpos = false; end - + end % if - + % analysis settings cfgredef = []; cfgredef.length = 1; cfgredef.overlap = 0; - + cfgfreq = []; cfgfreq.output = 'pow'; cfgfreq.channel = allchans; @@ -171,7 +177,7 @@ cfgfreq.taper = 'hanning'; cfgfreq.keeptrials = 'no'; cfgfreq.foilim = [0 min(info.hdr.Fs/2, 400)]; - + % output variables timelock.dimord = 'chan_time'; timelock.label = allchans; @@ -182,35 +188,35 @@ timelock.range = NaN(length(allchans), ntrials); % updated in loop timelock.min = NaN(length(allchans), ntrials); % updated in loop timelock.max = NaN(length(allchans), ntrials); % updated in loop - + freq.dimord = 'chan_freq_time'; freq.label = allchans; freq.freq = (cfgfreq.foilim(1):cfgfreq.foilim(2)); freq.time = (timeunit-timeunit/2:timeunit:timeunit*ntrials-timeunit/2); freq.powspctrm = NaN(length(allchans), length(freq.freq), ntrials); % updated in loop - + summary.dimord = 'chan_time'; summary.time = (timeunit-timeunit/2:timeunit:timeunit*ntrials-timeunit/2); summary.label = {'Mean';'Median';'Min';'Max';'Range';'HmotionN';'HmotionL';'HmotionR';'LowFreqPower';'LineFreqPower';'Jumps'}; summary.avg = NaN(length(summary.label), ntrials); % updated in loop - + % try add gradiometer info if isfield(info.hdr, 'grad') timelock.grad = info.hdr.grad; freq.grad = info.hdr.grad; summary.grad = info.hdr.grad; end - - + + % process trial by trial for t = 1:ntrials fprintf('analyzing trial %s of %s \n', num2str(t), num2str(ntrials)); - + % preprocess cfgpreproc = cfgdef; cfgpreproc.trl = cfgdef.trl(t,:); data = ft_preprocessing(cfgpreproc); clear cfgpreproc; - + % determine headposition if isctf && hasheadpos headpos.avg(1,t) = mean(data.trial{1,1}(Nx,:) * 100); % meter to cm @@ -223,36 +229,36 @@ headpos.avg(8,t) = mean(data.trial{1,1}(Ry,:) * 100); headpos.avg(9,t) = mean(data.trial{1,1}(Rz,:) * 100); end - + % update values timelock.avg(:,t) = mean(data.trial{1}(allchanindx,:),2); timelock.median(:,t) = median(data.trial{1}(allchanindx,:),2); timelock.range(:,t) = max(data.trial{1}(allchanindx,:),[],2) - min(data.trial{1}(allchanindx,:),[],2); timelock.min(:,t) = min(data.trial{1}(allchanindx,:),[],2); timelock.max(:,t) = max(data.trial{1}(allchanindx,:),[],2); - + % detect jumps for c = 1:size(data.trial{1}(allchanindx,:),1) timelock.jumps(c,t) = length(find(diff(data.trial{1,1}(allchanindx(c),:)) > jumpthreshold)); end - + % FFT and noise estimation redef = ft_redefinetrial(cfgredef, data); clear data; FFT = ft_freqanalysis(cfgfreq, redef); clear redef; freq.powspctrm(:,:,t) = FFT.powspctrm; summary.avg(9,t) = mean(mean(findpower(0, 2, FFT, chanindx))); % Low Freq Power summary.avg(10,t) = mean(mean(findpower(cfg.linefreq-1, cfg.linefreq+1, FFT, chanindx))); clear FFT; % Line Freq Power - + toc end % end of trial loop - + % determine headmotion: distance from initial trial (in cm) if isctf && hasheadpos summary.avg(6,:) = sqrt(sum((headpos.avg(1:3,:)-repmat(headpos.avg(1:3,1),1,size(headpos.avg,2))).^2,1)); % N summary.avg(7,:) = sqrt(sum((headpos.avg(4:6,:)-repmat(headpos.avg(4:6,1),1,size(headpos.avg,2))).^2,1)); % L summary.avg(8,:) = sqrt(sum((headpos.avg(7:9,:)-repmat(headpos.avg(7:9,1),1,size(headpos.avg,2))).^2,1)); % R end - + % summarize/mean and store variables of brain info only summary.avg(1,:) = mean(timelock.avg(chanindx,:),1); summary.avg(2,:) = mean(timelock.median(chanindx,:),1); @@ -260,7 +266,7 @@ summary.avg(4,:) = mean(timelock.max(chanindx,:),1); summary.avg(5,:) = mean(timelock.range(chanindx,:),1); summary.avg(11,:) = mean(timelock.jumps(chanindx,:),1); - + % save to .mat if strcmp(cfg.savemat, 'yes') if isctf && hasheadpos @@ -270,12 +276,12 @@ save(exportname, 'info','timelock','freq','summary'); end end - + end % end of analysis %% VISUALIZATION if strcmp(cfg.visualize, 'yes') - + % load data if strcmp(cfg.analyze, 'no') if ~isempty(cfg.matfile) @@ -286,15 +292,15 @@ fprintf('loading %s \n', exportname); load(exportname); end - + % determine number of 1-hour plots to be made nplots = ceil(length(freq.time)/(cfg.plotunit/10)); - + % create GUI-like figure(s) for p = 1:nplots fprintf('visualizing %s of %s \n', num2str(p), num2str(nplots)); toi = [p*cfg.plotunit-(cfg.plotunit-5) p*cfg.plotunit-5]; % select 1-hour chunks - + tmpcfg.latency = toi; temp_timelock = ft_selectdata(tmpcfg, timelock); temp_freq = ft_selectdata(tmpcfg, freq); @@ -307,7 +313,7 @@ draw_figure(info, temp_timelock, temp_freq, temp_summary, toi); clear temp_timelock; clear temp_freq; clear temp_summary; clear toi; end - + % export to .PNG and .PDF if strcmp(cfg.saveplot, 'yes') [pathstr,name,extr] = fileparts(exportname); @@ -417,11 +423,19 @@ function draw_figure(varargin) end % determine whether it is EEG or MEG -try -[iseeg, ismeg, isctf, fltp] = filetyper(timelock.cfg.dataset); -catch % in case the input is a matfile (and the dataset field does not exist): ugly workaround - [iseeg, ismeg, isctf, fltp] = filetyper(headpos.cfg.dataset); +if isfield(info, 'filename') % supported as of January 2018 + filename = info.filename; +elseif isfield(timelock.cfg, 'dataset') + filename = timelock.cfg.dataset; +elseif isfield(info.hdr.orig, 'FileName') + filename = info.hdr.orig.FileName; +elseif exist('headpos','var') && isfield(headpos.cfg.previous, 'dataset') + filename = headpos.cfg.previous.dataset; +else + error('could not determine the filename'); end +[iseeg, ismeg, isctf, fltp] = filetyper(filename); + if ismeg scaling = 1e15; % assuming data is in T and needs to become fT powscaling = scaling^2; @@ -551,7 +565,7 @@ function draw_figure(varargin) 'Units','normalized',... 'color','white',... 'Position',[.05 .08 .9 .52]); - + hmotions = ([summary.avg(8,:)' summary.avg(7,:)' summary.avg(6,:)'])*10; boxplot(h.HmotionAxes, hmotions, 'orientation', 'horizontal', 'notch', 'on'); set(h.HmotionAxes,'YTick',1:3); @@ -592,7 +606,7 @@ function draw_figure(varargin) 'Units','normalized',... 'color','white',... 'Position',[.08 .73 .89 .22]); - + plot(h.HmotionTimecourseAxes, summary.time, clipat(summary.avg(6,:)*10, 0, 10), ... summary.time, clipat(summary.avg(7,:)*10, 0, 10), ... summary.time, clipat(summary.avg(8,:)*10, 0, 10), 'LineWidth',2); @@ -743,7 +757,7 @@ function draw_figure(varargin) 'color','white',... 'Units','normalized',... 'Position',[0.4 0.05 0.55 0.4]); - + MEGchans = ft_channelselection('MEG', timelock.label); MEGchanindx = match_str(timelock.label, MEGchans); cfgtopo = []; diff --git a/external/fieldtrip/ft_redefinetrial.m b/external/fieldtrip/ft_redefinetrial.m index b3f7a48f..dd5d12d5 100644 --- a/external/fieldtrip/ft_redefinetrial.m +++ b/external/fieldtrip/ft_redefinetrial.m @@ -26,7 +26,7 @@ % For selecting a specific subsection of (i.e. cut out a time window % of interest) you can select a time window in seconds that is common % in all trials -% cfg.toilim = [tmin tmax] to specify a latency window in seconds +% cfg.toilim = [tmin tmax] to specify a latency window in seconds, can be Nx2 vector % % Alternatively you can specify the begin and end sample in each trial % cfg.begsample = single number or Nx1 vector, expressed in samples relative to the start of the input trial @@ -119,22 +119,28 @@ % select trials of interest if ~strcmp(cfg.trials, 'all') - if fb, fprintf('selecting %d trials\n', length(cfg.trials)); end - + if fb + if islogical(cfg.trials) + fprintf('selecting %d trials\n', sum(cfg.trials)); + else + fprintf('selecting %d trials\n', length(cfg.trials)); + end + end + % select trials of interest tmpcfg = keepfields(cfg, {'trials', 'showcallinfo'}); data = ft_selectdata(tmpcfg, data); % restore the provenance information [cfg, data] = rollback_provenance(cfg, data); - + if length(cfg.offset)>1 && length(cfg.offset)~=length(cfg.trials) - cfg.offset=cfg.offset(cfg.trials); + cfg.offset = cfg.offset(cfg.trials); end if length(cfg.begsample)>1 && length(cfg.begsample)~=length(cfg.trials) - cfg.begsample=cfg.begsample(cfg.trials); + cfg.begsample = cfg.begsample(cfg.trials); end if length(cfg.endsample)>1 && length(cfg.endsample)~=length(cfg.trials) - cfg.endsample=cfg.endsample(cfg.trials); + cfg.endsample = cfg.endsample(cfg.trials); end end Ntrial = numel(data.trial); @@ -153,34 +159,41 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % select a latency window from each trial %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + if numel(cfg.toilim) == 2 + % specified as single [tstart tend] vector + % expand into Ntrial X 2 + cfg.toilim = repmat(cfg.toilim(:)', Ntrial, 1); + end + begsample = zeros(Ntrial,1); endsample = zeros(Ntrial,1); skiptrial = false(Ntrial,1); for i=1:Ntrial - if cfg.toilim(1)>data.time{i}(end) || cfg.toilim(2)data.time{i}(end) || cfg.toilim(i,2)= dataold.sampleinfo(:,1)); % Determines which old trials are present in new trials + iTrlorig = find(begsample(iTrl) <= dataold.sampleinfo(:,2) & endsample(iTrl) >= dataold.sampleinfo(:,1)); % Determines which old trials are present in new trials - if size(cfg.trl,2)>3 %In case user specified a trialinfo + if size(cfg.trl,2)>3 % In case user specified a trialinfo data.trialinfo(iTrl,:) = cfg.trl(iTrl,4:end); elseif isfield(dataold,'trialinfo') % If old data has trialinfo - if (numel(iTrlorig) == 1 ... % only 1 old trial to copy trialinfo from, or + if (numel(iTrlorig) == 1 ... % only 1 old trial to copy trialinfo from, or || size(unique(dataold.trialinfo(iTrlorig,:),'rows'),1)) ... % all old trialinfo rows are identical && ~any(diff(dataold.sampleinfo(:,1))<=0) % and the trials are consecutive segments data.trialinfo(iTrl,:) = dataold.trialinfo(iTrlorig(1),:); @@ -267,43 +287,62 @@ ft_error('Old trialinfo cannot be combined into new trialinfo, please specify trialinfo in cfg.trl(:,4)'); end end - end %for iTrl - + end % for iTrl + % adjust the sampleinfo in the output if isfield(dataold, 'sampleinfo') - % adjust the trial definition - data.sampleinfo = trl(:, 1:2); + % adjust the sample information + data.sampleinfo = [begsample endsample]; end - + elseif ~isempty(cfg.length) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % cut the existing trials into segments of the specified length %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - + data = ft_checkdata(data, 'hassampleinfo', 'yes'); - + % create dummy trl-matrix and recursively call ft_redefinetrial nsmp = round(cfg.length*data.fsample); nshift = round((1-cfg.overlap)*nsmp); - - newtrl = zeros(0,3); + + newtrl = zeros(0,4); for k = 1:numel(data.trial) - offset = time2offset(data.time{k}, data.fsample); - tmp1 = [data.sampleinfo(k,:) offset]; - tmp2 = (tmp1(1):nshift:(tmp1(2)+1-nsmp))'; - if ~isempty(tmp2) - tmp2(:,2) = tmp2 + nsmp - 1; - tmp2(:,3) = tmp2(:,1) + offset - tmp2(1,1); - newtrl = [newtrl; tmp2]; + begsample = data.sampleinfo(k,1); + endsample = data.sampleinfo(k,2); + offset = time2offset(data.time{k}, data.fsample); + thistrl = (begsample:nshift:(endsample+1-nsmp))'; + if ~isempty(thistrl) % the trial might be too short + thistrl(:,2) = thistrl(:,1) + nsmp - 1; + thistrl(:,3) = thistrl(:,1) + offset - thistrl(1,1); + thistrl(:,4) = k; % keep the trial number in the 4th column, this is needed further down + newtrl = cat(1, newtrl, thistrl); end end - - tmpcfg = keepfields(cfg, {'showcallinfo'}); + clear begsample endsample offset + + tmpcfg = keepfields(cfg, {'showcallinfo', 'feedback'}); tmpcfg.trl = newtrl; + + if isfield(data, 'trialinfo') && ~istable(data.trialinfo) + % replace the trial number with the original trial information + tmpcfg.trl = [newtrl(:,1:3) data.trialinfo(newtrl(:,4),:)]; + elseif isfield(data, 'trialinfo') && istable(data.trialinfo) + % construct the trl matrix as a table + begsample = newtrl(:,1); + endsample = newtrl(:,2); + offset = newtrl(:,3); + tmpcfg.trl = [table(begsample, endsample, offset) data.trialinfo(newtrl(:,4),:)]; + elseif ~isfield(data, 'trialinfo') + % discard the trial number + tmpcfg.trl = newtrl(:,1:3); + end + + data = removefields(data, {'trialinfo'}); % these are in the additional columns of tmpcfg.trl data = ft_redefinetrial(tmpcfg, data); % restore the provenance information [cfg, data] = rollback_provenance(cfg, data); - + end % processing the realignment or data selection if ~isempty(cfg.minlength) @@ -324,7 +363,7 @@ data.time = data.time(~skiptrial); data.trial = data.trial(~skiptrial); if isfield(data, 'sampleinfo'), data.sampleinfo = data.sampleinfo(~skiptrial, :); end - if isfield(data, 'trialinfo'), data.trialinfo = data.trialinfo(~skiptrial, :); end + if isfield(data, 'trialinfo'), data.trialinfo = data.trialinfo (~skiptrial, :); end if fb, fprintf('removing %d trials that are too short\n', sum(skiptrial)); end end @@ -345,7 +384,7 @@ % do the general cleanup and bookkeeping at the end of the function ft_postamble debug ft_postamble trackconfig -if ~isempty(cfg.trl) +if ~isempty(cfg.trl) && exist('dataold', 'var') % the input data has been renamed to dataold ft_postamble previous dataold else diff --git a/external/fieldtrip/ft_regressconfound.m b/external/fieldtrip/ft_regressconfound.m index 2e5718e3..00d53b92 100644 --- a/external/fieldtrip/ft_regressconfound.m +++ b/external/fieldtrip/ft_regressconfound.m @@ -167,8 +167,10 @@ tmp(:,i,:,:,:) = dat{i}(:,:,:,:); end dat = tmp; + haspermuted = false; else dat = permute(dat, [rptdim datdim]); + haspermuted = true; end dat = reshape(dat, nrpt, []); @@ -250,9 +252,15 @@ switch cfg.output case 'residual' dataout.(cfg.parameter) = reshape(Yc, [nrpt dimsiz(datdim)]); % either powspctrm, trial, or pow + if haspermuted + dataout.(cfg.parameter) = ipermute(dataout.(cfg.parameter), [rptdim datdim]); + end clear Yc case 'beta' dataout.beta = reshape(beta, [nconf, dimsiz(datdim)]); + if haspermuted + dataout.beta = ipermute(dataout.beta, [rptdim datdim]); + end if strcmp(cfg.statistics, 'yes') % beta statistics fprintf('performing statistics on the regression weights \n'); dfe = nrpt - nconf; % degrees of freedom @@ -266,11 +274,18 @@ % FIXME: drop in replace tcdf from the statfun/private dir dataout.stat = reshape(tval, [nconf dimsiz(datdim)]); dataout.prob = reshape(prob, [nconf dimsiz(datdim)]); + if haspermuted + dataout.stat = ipermute(dataout.stat, [rptdim datdim]); + dataout.prob = ipermute(dataout.prob, [rptdim datdim]); + end clear tval prob end case 'model' dataout.model = keepfields(datain, {'label', 'time', 'freq', 'pos', 'dim', 'transform', 'inside', 'outside', 'trialinfo', 'sampleinfo', 'dimord'}); dataout.model.(cfg.parameter) = reshape(model, [nrpt, dimsiz(datdim)]); + if haspermuted + dataout.model.(cfg.parameter) = ipermute(dataout.model.(cfg.parameter), [rptdim datdim]); + end otherwise error('output ''%s'' is not supported', cfg.output); end diff --git a/external/fieldtrip/ft_rejectartifact.m b/external/fieldtrip/ft_rejectartifact.m index 95cc3133..a30886d0 100644 --- a/external/fieldtrip/ft_rejectartifact.m +++ b/external/fieldtrip/ft_rejectartifact.m @@ -1,14 +1,14 @@ function [cfg] = ft_rejectartifact(cfg, data) % FT_REJECTARTIFACT removes data segments containing artifacts. It returns a -% configuration structure with a modified trial definition which can be -% used for preprocessing of only the clean data. +% configuration structure with a modified trial definition which can be used for +% preprocessing of only the clean data. % -% You should start by detecting the artifacts in the data using the -% function FT_ARTIFACT_xxx where xxx is the type of artifact. Subsequently -% FT_REJECTARTIFACT looks at the detected artifacts and removes them from -% the trial definition or from the data. In case you wish to replace bad -% parts by nans, you have to specify data as an input parameter. +% You should start by detecting the artifacts in the data using the function +% FT_ARTIFACT_xxx where xxx is the type of artifact. Subsequently FT_REJECTARTIFACT +% looks at the detected artifacts and removes them from the trial definition or from +% the data. In case you wish to replace bad parts by nans, you have to specify data +% as an input parameter. % % Use as % cfg = ft_rejectartifact(cfg) @@ -17,27 +17,29 @@ % with the data as obtained from FT_PREPROCESSING % % The following configuration options are supported: -% cfg.artfctdef.reject = 'none', 'partial','nan', or 'complete' (default = 'complete') +% cfg.artfctdef.reject = 'none', 'partial', 'complete', 'nan', or 'value' (default = 'complete') % cfg.artfctdef.minaccepttim = when using partial rejection, minimum length % in seconds of remaining trial (default = 0.1) -% cfg.artfctdef.crittoilim = when using complete rejection, reject -% trial only when artifacts occur within -% this time window (default = whole trial) -% (only works with in-memory data, since trial time axes are unknown for data on disk) +% cfg.artfctdef.crittoilim = when using complete rejection, reject trial only when artifacts occur within +% this time window (default = whole trial). This only works with in-memory data, +% since trial time axes are unknown for data on disk. % cfg.artfctdef.feedback = 'yes' or 'no' (default = 'no') +% cfg.artfctdef.invert = 'yes' or 'no' (default = 'no') +% cfg.artfctdef.value = scalar value to replace the data in the artifact segments (default = nan) % cfg.artfctdef.eog.artifact = Nx2 matrix with artifact segments, this is added to the cfg by using FT_ARTIFACT_EOG % cfg.artfctdef.jump.artifact = Nx2 matrix with artifact segments, this is added to the cfg by using FT_ARTIFACT_JUMP % cfg.artfctdef.muscle.artifact = Nx2 matrix with artifact segments, this is added to the cfg by using FT_ARTIFACT_MUSCLE % cfg.artfctdef.zvalue.artifact = Nx2 matrix with artifact segments, this is added to the cfg by using FT_ARTIFACT_ZVALUE -% cfg.artfctdef.xxx.artifact = Nx2 matrix with artifact segments, this should be added by your own artifact detection function +% cfg.artfctdef.visual.artifact = Nx2 matrix with artifact segments, this is added to the cfg by using FT_DATABROWSER +% cfg.artfctdef.xxx.artifact = Nx2 matrix with artifact segments, this could be added by your own artifact detection function % -% A trial that contains an artifact can be rejected completely or -% partially. In case of partial rejection, a minimum length of the -% resulting sub-trials can be specified. +% A trial that contains an artifact can be rejected completely or partially. In case +% of partial rejection, a minimum length of the resulting sub-trials can be +% specified using minaccepttim. % % Output: -% If cfg is used as the only input parameter, a cfg with a new trl is the output. -% If cfg and data are both input parameters, a new raw data structure with only the clean data segments is the output. +% If cfg is used as the only input parameter, the output is a cfg structure with an updated trl. +% If cfg and data are both input parameters, the output is an updated raw data structure with only the clean data segments. % % To facilitate data-handling and distributed computing you can use % cfg.inputfile = ... @@ -45,8 +47,8 @@ % file on disk. This mat files should contain only a single variable named 'data', % corresponding to the input structure. % -% See also FT_ARTIFACT_EOG, FT_ARTIFACT_MUSCLE, FT_ARTIFACT_JUMP, FT_ARTIFACT_MANUAL, -% FT_ARTIFACT_THRESHOLD, FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG +% See also FT_ARTIFACT_EOG, FT_ARTIFACT_MUSCLE, FT_ARTIFACT_JUMP, FT_ARTIFACT_THRESHOLD, +% FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG, FT_DATABROWSER, FT_REJECTVISUAL % Undocumented local options: % cfg.headerfile @@ -63,7 +65,7 @@ % cfg.artfctdef.writerej = filename of rejection file % cfg.artfctdef.type = cell-array with strings, e.g. {'eog', 'muscle' 'jump'} -% Copyright (C) 2003-2007, Robert Oostenveld +% Copyright (C) 2003-2018, Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -111,12 +113,14 @@ cfg = ft_checkconfig(cfg, 'dataset2files', 'yes'); % set the defaults -if ~isfield(cfg, 'artfctdef'), cfg.artfctdef = []; end -if ~isfield(cfg.artfctdef,'type'), cfg.artfctdef.type = {}; end -if ~isfield(cfg.artfctdef,'reject'), cfg.artfctdef.reject = 'complete'; end -if ~isfield(cfg.artfctdef,'minaccepttim'), cfg.artfctdef.minaccepttim = 0.1; end -if ~isfield(cfg.artfctdef,'crittoilim'), cfg.artfctdef.crittoilim = []; end -if ~isfield(cfg.artfctdef,'feedback'), cfg.artfctdef.feedback = 'no'; end +cfg.artfctdef = ft_getopt(cfg, 'artfctdef'); +cfg.artfctdef.type = ft_getopt(cfg.artfctdef, 'type', {}); +cfg.artfctdef.reject = ft_getopt(cfg.artfctdef, 'reject', 'complete'); +cfg.artfctdef.minaccepttim = ft_getopt(cfg.artfctdef, 'minaccepttim', 0.1); +cfg.artfctdef.crittoilim = ft_getopt(cfg.artfctdef, 'crittoilim', []); +cfg.artfctdef.feedback = ft_getopt(cfg.artfctdef, 'feedback', 'no'); +cfg.artfctdef.invert = ft_getopt(cfg.artfctdef, 'invert', 'no'); +cfg.artfctdef.value = ft_getopt(cfg.artfctdef, 'value', nan); % convert from old-style to new-style configuration if isfield(cfg,'reject') @@ -256,8 +260,8 @@ cfg.artfctdef.type = cfg.artfctdef.type(:)'; % If bad parts are to be filled with nans, make sure data is available -if strcmp(cfg.artfctdef.reject, 'nan') && ~hasdata - ft_error('If bad parts are to be filled with nans, input data has to be specified'); +if ~hasdata && (strcmp(cfg.artfctdef.reject, 'nan') || strcmp(cfg.artfctdef.reject, 'value')) + ft_error('If bad parts are to be filled with nans or another value, the input data has to be specified'); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -290,6 +294,7 @@ fprintf('detected %3d %s artifacts\n', num, dum{i}); end end + % update the configuration to reflect the artifacts types that were scanned cfg.artfctdef.type = dum(sel); @@ -382,9 +387,15 @@ legend({'defined trials', cfg.artfctdef.type{:}}); end % feedback -% convert to logical, NOTE: this is required for the following code +% convert to logical, this is required for the subsequent code rejectall = (rejectall~=0); +% invert the artifact selection +if istrue(cfg.artfctdef.invert) + fprintf('inverting selection of clean/artifactual data\n'); + rejectall = ~rejectall; +end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % write the rejection to an EEP format file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -409,12 +420,13 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % remove the trials that (partially) coincide with a rejection mark %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -if strcmp(cfg.artfctdef.reject, 'partial') || strcmp(cfg.artfctdef.reject, 'complete') || strcmp(cfg.artfctdef.reject, 'nan') +if any(strcmp(cfg.artfctdef.reject, {'partial', 'complete', 'nan', 'value'})) trialok = []; count_complete_reject = 0; count_partial_reject = 0; count_nan = 0; + count_value = 0; count_outsidecrit = 0; trlCompletelyRemovedInd = []; @@ -427,13 +439,19 @@ if all(not(rejecttrial)) % the whole trial is good trialok = [trialok; trl(trial,:)]; - + elseif all(rejecttrial) && strcmp(cfg.artfctdef.reject, 'nan') % the whole trial is bad, but it is requested to be replaced with nans data.trial{trial}(:,rejecttrial) = nan; count_nan = count_nan + 1; trialok = [trialok; trl(trial,:)]; % Mark the trial as good as nothing will be removed - + + elseif all(rejecttrial) && strcmp(cfg.artfctdef.reject, 'value') + % the whole trial is bad, but it is requested to be replaced with a specific value + data.trial{trial}(:,rejecttrial) = cfg.artfctdef.value; + count_value = count_value + 1; + trialok = [trialok; trl(trial,:)]; % Mark the trial as good as nothing will be removed + elseif all(rejecttrial) % the whole trial is bad count_complete_reject = count_complete_reject + 1; @@ -482,12 +500,20 @@ data.trial{trial}(:,rejecttrial) = nan; count_nan = count_nan + 1; trialok = [trialok; trl(trial,:)]; % Mark the trial as good as nothing will be removed + + elseif any(rejecttrial) && strcmp(cfg.artfctdef.reject, 'value') + % Some part of the trial is bad, replace bad part with specified value + data.trial{trial}(:,rejecttrial) = cfg.artfctdef.value; + count_value = count_value + 1; + trialok = [trialok; trl(trial,:)]; % Mark the trial as good as nothing will be removed + end end % for each trial fprintf('rejected %3d trials completely\n', count_complete_reject); fprintf('rejected %3d trials partially\n', count_partial_reject); fprintf('filled parts of %3d trials with nans\n', count_nan); + fprintf('filled parts of %3d trials with the specified value\n', count_value); if (checkCritToi) fprintf('retained %3d trials with artifacts outside critical window\n', count_outsidecrit); end diff --git a/external/fieldtrip/ft_rejectcomponent.m b/external/fieldtrip/ft_rejectcomponent.m index c8359890..d5be0a70 100644 --- a/external/fieldtrip/ft_rejectcomponent.m +++ b/external/fieldtrip/ft_rejectcomponent.m @@ -119,18 +119,18 @@ if hasdata && strcmp(cfg.demean, 'yes') % optionally perform baseline correction on each trial - fprintf('baseline correcting data \n'); + ft_info('baseline correcting data \n'); for trial=1:numel(data.trial) data.trial{trial} = ft_preproc_baselinecorrect(data.trial{trial}); end end % set the rejected component amplitudes to zero -fprintf('removing %d components\n', length(reject)); +ft_info('removing %d components\n', length(reject)); if ~hasdata - fprintf('keeping %d components\n', ncomps-length(reject)); + ft_info('keeping %d components\n', ncomps-length(reject)); else - fprintf('keeping %d components\n', nchans-length(reject)); + ft_info('keeping %d components\n', nchans-length(reject)); end % create a projection matrix by subtracting the subspace spanned by the @@ -175,7 +175,7 @@ end % if hasdata % apply the linear projection to the data -data = ft_apply_montage(data, montage, 'keepunused', keepunused, 'feedback', cfg.feedback); +data = ft_apply_montage(data, montage, 'keepunused', keepunused, 'feedback', cfg.feedback, 'showcallinfo', cfg.showcallinfo); if isfield(data, 'grad') sensfield = 'grad'; @@ -190,7 +190,7 @@ % apply the linear projection also to the sensor description if ~isempty(sensfield) if strcmp(cfg.updatesens, 'yes') - fprintf('also applying the backprojection matrix to the %s structure\n', sensfield); + ft_info('also applying the backprojection matrix to the %s structure\n', sensfield); % the balance field is needed to keep the sequence of linear projections if ~isfield(data.(sensfield), 'balance') @@ -231,7 +231,7 @@ data.(sensfield) = sens; else - fprintf('not applying the backprojection matrix to the %s structure\n', sensfield); + ft_info('not applying the backprojection matrix to the %s structure\n', sensfield); % simply copy it over comp.(sensfield) = data.(sensfield); end diff --git a/external/fieldtrip/ft_resampledata.m b/external/fieldtrip/ft_resampledata.m index 3b2eb8e5..be7fbf8d 100644 --- a/external/fieldtrip/ft_resampledata.m +++ b/external/fieldtrip/ft_resampledata.m @@ -7,13 +7,13 @@ % % The data should be organised in a structure as obtained from % the FT_PREPROCESSING function. The configuration should contain -% cfg.resamplefs = frequency at which the data will be resampled (default = 256 Hz) -% cfg.detrend = 'no' or 'yes', detrend the data prior to resampling (no default specified, see below) -% cfg.demean = 'no' or 'yes', whether to apply baseline correction (default = 'no') -% cfg.baselinewindow = [begin end] in seconds, the default is the complete trial (default = 'all') -% cfg.feedback = 'no', 'text', 'textbar', 'gui' (default = 'text') -% cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') -% cfg.sampleindex = 'no' or 'yes', add a channel with the original sample indices (default = 'no') +% cfg.resamplefs = frequency at which the data will be resampled (default = 256 Hz) +% cfg.detrend = 'no' or 'yes', detrend the data prior to resampling (no default specified, see below) +% cfg.demean = 'no' or 'yes', whether to apply baseline correction (default = 'no') +% cfg.baselinewindow = [begin end] in seconds, the default is the complete trial (default = 'all') +% cfg.feedback = 'no', 'text', 'textbar', 'gui' (default = 'text') +% cfg.trials = 'all' or a selection given as a 1xN vector (default = 'all') +% cfg.sampleindex = 'no' or 'yes', add a channel with the original sample indices (default = 'no') % % Instead of specifying cfg.resamplefs, you can also specify a time axis on % which you want the data to be resampled. This is usefull for merging data @@ -48,7 +48,7 @@ % files should contain only a single variable, corresponding with the % input/output structure. % -% See also FT_PREPROCESSING +% See also FT_PREPROCESSING, RESAMPLE, DOWNSAMPLE, INTERP1 % Copyright (C) 2003-2006, FC Donders Centre, Markus Siegel % Copyright (C) 2004-2009, FC Donders Centre, Robert Oostenveld @@ -93,32 +93,38 @@ % check if the input cfg is valid for this function cfg = ft_checkconfig(cfg, 'renamed', {'blc', 'demean'}); +cfg = ft_checkconfig(cfg, 'renamed', {'resamplemethod', 'method'}); +cfg = ft_checkconfig(cfg, 'renamed', {'fsample', 'resamplefs'}); % set the defaults cfg.resamplefs = ft_getopt(cfg, 'resamplefs', []); cfg.time = ft_getopt(cfg, 'time', {}); +cfg.factor = ft_getopt(cfg, 'factor', {}); cfg.detrend = ft_getopt(cfg, 'detrend', 'no'); cfg.demean = ft_getopt(cfg, 'demean', 'no'); cfg.baselinewindow = ft_getopt(cfg, 'baselinewindow', 'all'); cfg.feedback = ft_getopt(cfg, 'feedback', 'text'); cfg.trials = ft_getopt(cfg, 'trials', 'all', 1); -cfg.method = ft_getopt(cfg, 'method', 'pchip'); +cfg.method = ft_getopt(cfg, 'method', []); cfg.sampleindex = ft_getopt(cfg, 'sampleindex', 'no'); -% give the user control over whether to use resample (applies anti-aliasing filter) or downsample (does not apply filter) -cfg.resamplemethod = ft_getopt(cfg, 'resamplemethod', 'resample'); - % store original datatype convert = ft_datatype(data); % check if the input data is valid for this function, this will convert it to raw if needed data = ft_checkdata(data, 'datatype', {'raw+comp', 'raw'}, 'feedback', 'yes'); -% set default resampling frequency -if isempty(cfg.resamplefs) && isempty(cfg.time) - cfg.resamplefs = 256; +if isempty(cfg.method) && ~isempty(cfg.time) + % see INTERP1, shape-preserving piecewise cubic interpolation + cfg.method = 'pchip'; +elseif isempty(cfg.method) + % see RESAMPLE + cfg.method = 'resample'; end +usefsample = any(strcmp(cfg.method, {'resample', 'downsample', 'decimate', 'mean', 'median'})); +usetime = ~usefsample; + % select trials of interest tmpcfg = keepfields(cfg, {'trials', 'showcallinfo'}); data = ft_selectdata(tmpcfg, data); @@ -140,36 +146,24 @@ data = rmfield(data, 'sampleinfo'); end -usefsample = ~isempty(cfg.resamplefs); -usetime = ~isempty(cfg.time); - if usefsample && usetime ft_error('you should either specify cfg.resamplefs or cfg.time') end -% whether to use downsample() or resample() -if strcmp(cfg.resamplemethod, 'resample') - usedownsample = 0; -elseif strcmp(cfg.resamplemethod, 'downsample') - ft_warning('using cfg.resamplemethod = ''downsample'', only use this if you have applied an anti-aliasing filter prior to downsampling!'); - usedownsample = 1; -else - ft_error('unknown resamplemethod ''%s''', cfg.resamplemethod); -end - % remember the original sampling frequency in the configuration cfg.origfs = double(data.fsample); if usefsample %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % resample based on new sampling frequency + % resample/downsample based on new sampling frequency %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + ntr = length(data.trial); - + ft_progress('init', cfg.feedback, 'resampling data'); [fsorig, fsres] = rat(cfg.origfs./cfg.resamplefs);%account for non-integer fs cfg.resamplefs = cfg.origfs.*(fsres./fsorig);%get new fs exact - + % make sure that the resampled time axes are aligned (this is to avoid % rounding errors in the time axes). this procedure relies on the % fact that resample assumes all data outside the data window to be zero @@ -181,110 +175,145 @@ end minsmp = min(firstsmp); padsmp = round((firstsmp-minsmp).*cfg.origfs); - + nchan = numel(data.label); if any(padsmp~=0) ft_warning('not all of the trials have the same original time axis: to avoid rounding issues in the resampled time axes, data will be zero-padded to the left prior to resampling'); end - + + if any(strcmp(cfg.method, {'downsample', 'mean', 'median'})) + ft_warning('using cfg.method = ''%s'', only use this if you have applied an anti-aliasing filter prior to downsampling!', cfg.method); + end + + if any(strcmp(cfg.method, {'decimate', 'downsample', 'mean', 'median'})) + if mod(fsorig, fsres) ~= 0 + ft_error('new sampling rate needs to be a proper divisor of original sampling rate'); + end + end + for itr = 1:ntr ft_progress(itr/ntr, 'resampling data in trial %d from %d\n', itr, ntr); if istrue(cfg.detrend) data.trial{itr} = ft_preproc_detrend(data.trial{itr}); end - + % Compute mean based on a window (cfg.baselinewindow) or the whole % trial. This does not support nan values anymore. Fix in - % ft_preproc_plyremoval. + % ft_preproc_polyremoval. % previously --> bsl = nanmean(data.trial{itr},2); if ~strcmp(cfg.baselinewindow, 'all') - begsample = ceil(data.fsample*cfg.baselinewindow(1))+find(data.time{itr}==0); - endsample = floor(data.fsample*cfg.baselinewindow(2))-1+find(data.time{itr}==0); - [~,bsl] = ft_preproc_baselinecorrect(data.trial{itr}, begsample, endsample); + begsample = ceil(data.fsample*cfg.baselinewindow(1))+find(data.time{itr}==0); + endsample = floor(data.fsample*cfg.baselinewindow(2))-1+find(data.time{itr}==0); + [dum,bsl] = ft_preproc_baselinecorrect(data.trial{itr}, begsample, endsample); else - [~,bsl] = ft_preproc_baselinecorrect(data.trial{itr}); + [dum,bsl] = ft_preproc_baselinecorrect(data.trial{itr}); end % always remove the mean to avoid edge effects when there's a strong - % offset, the cfg.demean option is dealt with below + % offset, the cfg.demean option is dealt with below data.trial{itr} = data.trial{itr} - bsl(:,ones(1,size(data.trial{itr},2))); - + % pad the data with zeros to the left data.trial{itr} = [zeros(nchan, padsmp(itr)) data.trial{itr}]; data.time{itr} = [data.time{itr}(1)-(padsmp(itr):-1:1)./cfg.origfs data.time{itr}]; - + % perform the resampling - if usedownsample - if mod(fsorig, fsres) ~= 0 - ft_error('when using cfg.resamplemethod = ''downsample'', new sampling rate needs to be a proper divisor of original sampling rate'); - end - + if strcmp(cfg.method, 'downsample') if isa(data.trial{itr}, 'single') % temporary convert this trial to double precision data.trial{itr} = transpose(single(downsample(double(transpose(data.trial{itr})),fsorig/fsres))); else data.trial{itr} = transpose(downsample(transpose(data.trial{itr}),fsorig/fsres)); end - - else % resample (default) + + elseif strcmp(cfg.method, 'resample') if isa(data.trial{itr}, 'single') % temporary convert this trial to double precision data.trial{itr} = transpose(single(resample(double(transpose(data.trial{itr})),fsres,fsorig))); else data.trial{itr} = transpose(resample(transpose(data.trial{itr}),fsres,fsorig)); end + + elseif strcmp(cfg.method, 'decimate') + if isa(data.trial{itr}, 'single') + % temporary convert this trial to double precision + data.trial{itr} = transpose(single(my_decimate(double(transpose(data.trial{itr})),fsorig/fsres))); + else + data.trial{itr} = transpose(my_decimate(transpose(data.trial{itr}),fsorig/fsres)); + end + + elseif strcmp(cfg.method, 'mean') + if isa(data.trial{itr}, 'single') + % temporary convert this trial to double precision + data.trial{itr} = transpose(single(my_mean(double(transpose(data.trial{itr})),fsorig/fsres))); + else + data.trial{itr} = transpose(my_mean(transpose(data.trial{itr}),fsorig/fsres)); + end + + elseif strcmp(cfg.method, 'median') + if isa(data.trial{itr}, 'single') + % temporary convert this trial to double precision + data.trial{itr} = transpose(single(my_median(double(transpose(data.trial{itr})),fsorig/fsres))); + else + data.trial{itr} = transpose(my_median(transpose(data.trial{itr}),fsorig/fsres)); + end + + else + ft_error('unknown method ''%s''', cfg.method); end - + % update the time axis nsmp = size(data.trial{itr},2); - data.time{itr} = data.time{itr}(1) + (0:(nsmp-1))/cfg.resamplefs; - - %un-pad the data + origtime = data.time{itr}; + data.time{itr} = origtime(1) + (0:(nsmp-1))/cfg.resamplefs; + % the first sample does not exactly remain at the same latency, but can be shifted by a sub-sample amount + shift = mean(origtime) - mean(data.time{itr}); + data.time{itr} = data.time{itr} + shift; + + % un-pad the data begindx = ceil(cfg.resamplefs.*padsmp(itr)./cfg.origfs) + 1; data.time{itr} = data.time{itr}(begindx:end); data.trial{itr} = data.trial{itr}(:, begindx:end); - + % add back the mean if ~strcmp(cfg.demean, 'yes') data.trial{itr} = data.trial{itr} + bsl(:,ones(1,numel(data.time{itr}))); end - + end % for itr ft_progress('close'); - + % specify the new sampling frequency in the output data.fsample = cfg.resamplefs; - + elseif usetime %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % resample based on new time axes for each trial + % resample based on the specified new time axes for each trial %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ntr = length(data.trial); - + ft_progress('init', cfg.feedback, 'resampling data'); for itr = 1:ntr ft_progress(itr/ntr, 'resampling data in trial %d from %d\n', itr, ntr); - + if istrue(cfg.detrend) data.trial{itr} = ft_preproc_detrend(data.trial{itr}); end - - % Compute mean based on a window (cfg.baselinewindow) or the whole % trial. This does not support nan values anymore. Fix in - % ft_preproc_plyremoval. + % ft_preproc_polyremoval. % previously --> bsl = nanmean(data.trial{itr},2); if ~strcmp(cfg.baselinewindow, 'all') - begsample = ceil(data.fsample*cfg.baselinewindow(1))+find(data.time{itr}==0); - endsample = floor(data.fsample*cfg.baselinewindow(2))-1+find(data.time{itr}==0); - [dum,bsl] = ft_preproc_baselinecorrect(data.trial{itr}, begsample, endsample); + begsample = ceil(data.fsample*cfg.baselinewindow(1))+find(data.time{itr}==0); + endsample = floor(data.fsample*cfg.baselinewindow(2))-1+find(data.time{itr}==0); + [dum,bsl] = ft_preproc_baselinecorrect(data.trial{itr}, begsample, endsample); else - [dum,bsl] = ft_preproc_baselinecorrect(data.trial{itr}); + [dum,bsl] = ft_preproc_baselinecorrect(data.trial{itr}); end % always remove the mean to avoid edge effects when there's a strong - % offset, the cfg.demean option is dealt with below + % offset, the cfg.demean option is dealt with below data.trial{itr} = data.trial{itr} - bsl(:,ones(1,size(data.trial{itr},2))); - + % perform the resampling if length(data.time{itr})>1 data.trial{itr} = interp1(data.time{itr}', data.trial{itr}', cfg.time{itr}', cfg.method)'; @@ -293,20 +322,20 @@ end % update the time axis data.time{itr} = cfg.time{itr}; - + % add back the mean if ~strcmp(cfg.demean, 'yes') data.trial{itr} = data.trial{itr} + bsl(:,ones(1,numel(data.time{itr}))); end - + end % for itr ft_progress('close'); - + % specify the new sampling frequency in the output t1 = cfg.time{1}(1); t2 = cfg.time{1}(2); data.fsample = 1/(t2-t1); - + end % if usefsample or usetime ft_info('original sampling rate = %d Hz\nnew sampling rate = %d Hz\n', cfg.origfs, data.fsample); @@ -326,3 +355,42 @@ ft_postamble provenance data ft_postamble history data ft_postamble savevar data + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION that decimates along the columns +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function y = my_decimate(x, varargin) +[n, m] = size(x); +% decimate the first column +y = decimate(x(:,1), varargin{:}); +if m>1 + % increase the size of the output matrix + y(:,m) = 0; + % decimate the subsequent columns + for i=2:m + y(:,i) = decimate(x(:,i), varargin{:}); + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION that does a block-wise average along the columns +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function y = my_mean(x, r) +[n, m] = size(x); +n = n - mod(n,r); +x = x(1:n,:); +x = reshape(x, [r n/r m]); +y = mean(x, 1); +y = reshape(y, [n/r m]); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION that does a block-wise median along the columns +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function y = my_median(x, r) +[n, m] = size(x); +n = n - mod(n,r); +x = x(1:n,:); +x = reshape(x, [r n/r m]); +y = median(x, 1); +y = reshape(y, [n/r m]); diff --git a/external/fieldtrip/ft_respiration.m b/external/fieldtrip/ft_respiration.m new file mode 100644 index 00000000..cda3c434 --- /dev/null +++ b/external/fieldtrip/ft_respiration.m @@ -0,0 +1,221 @@ +function [dataout] = ft_respiration(cfg, datain) + +% FT_RESPIRATION estimates the respiration rate from a respiration belt, temperature +% sensor, movement sensor or from the heart rate. It returns a new data structure +% with a continuous representation of the rate and phase. +% +% Use as +% dataout = ft_respiration(cfg, data) +% where the input data is a structure as obtained from FT_PREPROCESSING. +% +% The configuration structure has the following options +% cfg.channel = selected channel for processing, see FT_CHANNELSELECTION +% cfg.peakseparation = scalar, time in seconds +% cfg.envelopewindow = scalar, time in seconds +% cfg.feedback = 'yes' or 'no' +% The input data can be preprocessed on the fly using +% cfg.preproc.bpfilter = 'yes' or 'no' (default = 'yes') +% cfg.preproc.bpfreq = [low high], filter frequency in Hz +% +% See also FT_HEARTRATE, FT_ELECTRODERMALACTIVITY, FT_HEADMOVEMENT, FT_REGRESSCONFOUND + +% Copyright (C) 2018, Robert Oostenveld, DCCN +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% the initial part deals with parsing the input options and data +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% these are used by the ft_preamble/ft_postamble function and scripts +ft_revision = '$Id$'; +ft_nargin = nargin; +ft_nargout = nargout; + +% the ft_preamble function works by calling a number of scripts from +% fieldtrip/utility/private that are able to modify the local workspace + +ft_defaults +ft_preamble init +ft_preamble debug +ft_preamble loadvar datain +ft_preamble provenance datain +ft_preamble trackconfig + +% the ft_abort variable is set to true or false in ft_preamble_init +if ft_abort + % do not continue function execution in case the outputfile is present and the user indicated to keep it + return +end + +% check if the input data is valid for this function, the input data must be raw +datain = ft_checkdata(datain, 'datatype', 'raw', 'feedback', 'yes'); + +% set the default options +cfg.channel = ft_getopt(cfg, 'channel', {}); +cfg.envelopewindow = ft_getopt(cfg, 'envelopewindow', []); % in seconds +cfg.peakseparation = ft_getopt(cfg, 'peakseparation', 3); % in seconds +cfg.feedback = ft_getopt(cfg, 'feedback', 'yes'); +cfg.preproc = ft_getopt(cfg, 'preproc', []); +% the expected respiration rate is around 0.40 Hz +cfg.preproc.bpfilter = ft_getopt(cfg.preproc, 'bpfilter', 'yes'); +cfg.preproc.bpfilttype = ft_getopt(cfg.preproc, 'bpfilttype', 'but'); +cfg.preproc.bpfiltdir = ft_getopt(cfg.preproc, 'bpfiltdir', 'twopass'); +cfg.preproc.bpfiltord = ft_getopt(cfg.preproc, 'bpfiltord', 2); +cfg.preproc.bpfreq = ft_getopt(cfg.preproc, 'bpfreq', [1/3 3] * 0.40); % in Hz + +% copy some of the fields over to the new data structure +dataout = keepfields(datain, {'time', 'fsample', 'sampleinfo', 'trialinfo'}); +dataout.label = {'respirationrate', 'respirationphase', 'respirationonset'}; +dataout.trial = {}; % this is to be determined in the main code + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% the actual computation is done in the middle part +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +cfg.channel = ft_channelselection(cfg.channel, datain.label); +assert(numel(cfg.channel)==1, 'you should specify exactly one channel'); + +chansel = strcmp(datain.label, cfg.channel{1}); +fsample = datain.fsample; + +for trllop=1:numel(datain.trial) + dat = datain.trial{trllop}(chansel,:); + label = datain.label(chansel); + time = datain.time{trllop}; + + if ~isempty(cfg.peakseparation) + [yupper,ylower] = envelope(dat, round(cfg.peakseparation*fsample), 'peaks'); + elseif ~isempty(cfg.envelopewindow) + [yupper,ylower] = envelope(dat, round(cfg.envelopewindow*fsample), 'rms'); + end + + if istrue(cfg.feedback) + figure + subplot(5,1,1) + hold on + plot(time, dat) + plot(time, yupper, 'g'); + plot(time, ylower, 'g'); + xlim([min(time) max(time)]) + xlabel('time (s)'); + title(sprintf('original, trial %d', trllop)) + end + + if ~isempty(cfg.preproc) + % apply the preprocessing to the selected channel + [dat, label, time, cfg.preproc] = preproc(dat, label, time, cfg.preproc, 0, 0); + end + + if ~isempty(cfg.peakseparation) + [yupper,ylower] = envelope(dat, round(cfg.peakseparation*fsample), 'peaks'); + elseif ~isempty(cfg.envelopewindow) + [yupper,ylower] = envelope(dat, round(cfg.envelopewindow*fsample), 'rms'); + end + + if istrue(cfg.feedback) + subplot(5,1,2) + hold on + plot(time, dat) + plot(time, yupper, 'g'); + plot(time, ylower, 'g'); + xlim([min(time) max(time)]) + xlabel('time (s)'); + title('filtered') + end + + dat = (dat - ylower) ./ (yupper - ylower); + + if ~isempty(cfg.peakseparation) + [yupper,ylower] = envelope(dat, round(cfg.peakseparation*fsample), 'peaks'); + elseif ~isempty(cfg.envelopewindow) + [yupper,ylower] = envelope(dat, round(cfg.envelopewindow*fsample), 'rms'); + end + + if istrue(cfg.feedback) + subplot(5,1,3) + hold on + plot(time, dat) + plot(time, yupper, 'g'); + plot(time, ylower, 'g'); + xlim([min(time) max(time)]) + xlabel('time (s)'); + title('locally rescaled') + end + + x = angle(hilbert(dat)); + % apply the same preprocessing to the phase timeseries + y = preproc(x, label, time, cfg.preproc, 0, 0); + % find the downward going zero-crossings + onset = findzerocrossing(y, fsample/10); + + % construct a continuous channel with the rate and the phase + rate = nan(size(dat)); + phase = nan(size(dat)); + for i=1:length(onset)-1 + begsample = onset(i); + endsample = onset(i+1); + rate(begsample:endsample) = fsample/(endsample-begsample); + phase(begsample:endsample) = linspace(-pi, pi, (endsample-begsample+1)); + end + % also construct a boolean channel with a pulse at the respiration onset + tmp = zeros(size(dat)); + tmp(onset) = 1; + + % add the continuous channels to the output structure + dataout.trial{trllop} = [rate; phase; tmp]; + + if istrue(cfg.feedback) + subplot(5,1,4) + plot(time, rate) + xlim([min(time) max(time)]) + xlabel('time (s)'); + ylabel('rate (Hz)'); + end + + if istrue(cfg.feedback) + subplot(5,1,5) + plot(time, phase) + xlim([min(time) max(time)]) + xlabel('time (s)'); + ylabel('phase'); + end + + ft_info('breathing rate in trial %d: mean=%.2f, min=%.2f, max=%.2f\n', trllop, nanmean(rate), nanmin(rate), nanmax(rate)); + +end % for trllop + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% deal with the output +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ft_postamble debug +ft_postamble trackconfig +ft_postamble previous datain +ft_postamble provenance dataout +ft_postamble history dataout +ft_postamble savevar dataout + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function indx = findzerocrossing(x, n) +bool = x(1:(end-n))>0 & x((1+n):end)<0; +indx = find(diff([0 bool])>0); +indx = indx + ceil(n/2); diff --git a/external/fieldtrip/ft_scalpcurrentdensity.m b/external/fieldtrip/ft_scalpcurrentdensity.m index bbf5a029..2375ee6d 100644 --- a/external/fieldtrip/ft_scalpcurrentdensity.m +++ b/external/fieldtrip/ft_scalpcurrentdensity.m @@ -149,7 +149,7 @@ dtype = ft_datatype(data); % check if the input data is valid for this function -data = ft_checkdata(data, 'datatype', 'raw', 'feedback', 'yes', 'iseeg','yes','ismeg',[]); +data = ft_checkdata(data, 'datatype', 'raw', 'feedback', 'yes','ismeg',[]); % select trials of interest tmpcfg = keepfields(cfg, {'trials', 'showcallinfo'}); diff --git a/external/fieldtrip/ft_singleplotER.m b/external/fieldtrip/ft_singleplotER.m index 3e82e958..1e31a255 100644 --- a/external/fieldtrip/ft_singleplotER.m +++ b/external/fieldtrip/ft_singleplotER.m @@ -262,7 +262,7 @@ assert(~isempty(cfg.trials), 'empty specification of cfg.trials for data with repetitions'); end -% parse cfg.channel +% parse cfg.channel if isfield(cfg, 'channel') && isfield(varargin{1}, 'label') cfg.channel = ft_channelselection(cfg.channel, varargin{1}.label); elseif isfield(cfg, 'channel') && isfield(varargin{1}, 'labelcmb') @@ -395,9 +395,9 @@ ymax = []; for i=1:Ndata % Select the channels in the data that match with the layout and that are selected for plotting - dat = mean(varargin{i}.(cfg.parameter)(selchan,selx),1); % mean over channels, as that is what will be plotted - ymin = min([ymin min(min(min(dat)))]); - ymax = max([ymax max(max(max(dat)))]); + dat = nanmean(varargin{i}.(cfg.parameter)(selchan,selx),1); % mean over channels, as that is what will be plotted + ymin = nanmin([ymin nanmin(nanmin(nanmin(dat)))]); + ymax = nanmax([ymax nanmax(nanmax(nanmax(dat)))]); end if strcmp(cfg.ylim, 'maxabs') % handle maxabs, make y-axis center on 0 ymax = max([abs(ymax) abs(ymin)]); @@ -450,9 +450,13 @@ end end -% set xlim and ylim: -xlim([xmin xmax]); -ylim([ymin ymax]); +% set xlim and ylim +if xmin~=xmax + xlim([xmin xmax]); +end +if ymin~=ymax + ylim([ymin ymax]); +end % adjust mask box extents to ymin/ymax if ~isempty(cfg.maskparameter) @@ -613,4 +617,3 @@ function key_sub(handle, eventdata, varargin) ylim([varargin{3} varargin{4}]) end % switch end % if - diff --git a/external/fieldtrip/ft_singleplotTFR.m b/external/fieldtrip/ft_singleplotTFR.m index d672c586..8fd1cf10 100644 --- a/external/fieldtrip/ft_singleplotTFR.m +++ b/external/fieldtrip/ft_singleplotTFR.m @@ -11,12 +11,14 @@ % % The configuration can have the following parameters: % cfg.parameter = field to be plotted on z-axis, e.g. 'powspcrtrm' (default depends on data.dimord) -% cfg.maskparameter = field in the data to be used for masking of data +% cfg.maskparameter = field in the data to be used for masking of data, can be logical (e.g. significant data points) or numerical (e.g. t-values). % (not possible for mean over multiple channels, or when input contains multiple subjects % or trials) -% cfg.maskstyle = style used to masking, 'opacity', 'saturation', 'outline' or 'colormix' (default = 'opacity') +% cfg.maskstyle = style used to masking, 'opacity', 'saturation', or 'outline' (default = 'opacity') +% 'outline' can only be used with a logical cfg.maskparameter % use 'saturation' or 'outline' when saving to vector-format (like *.eps) to avoid all sorts of image-problems -% cfg.maskalpha = alpha value between 0 (transparant) and 1 (opaque) used for masking areas dictated by cfg.maskparameter (default = 1) +% cfg.maskalpha = alpha value between 0 (transparent) and 1 (opaque) used for masking areas dictated by cfg.maskparameter (default = 1) +% (will be ignored in case of numeric cfg.maskparameter or if cfg.maskstyle = 'outline') % cfg.masknans = 'yes' or 'no' (default = 'yes') % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.ylim = 'maxmin' or [ymin ymax] (default = 'maxmin') @@ -103,7 +105,7 @@ ft_defaults ft_preamble init ft_preamble debug -ft_preamble provenance +ft_preamble provenance data ft_preamble trackconfig % the ft_abort variable is set to true or false in ft_preamble_init @@ -124,7 +126,6 @@ cfg = ft_checkconfig(cfg, 'renamed', {'channelname', 'channel'}); cfg = ft_checkconfig(cfg, 'renamed', {'cohrefchannel', 'refchannel'}); cfg = ft_checkconfig(cfg, 'renamed', {'zparam', 'parameter'}); -cfg = ft_checkconfig(cfg, 'deprecated', {'xparam', 'yparam'}); % Set the defaults cfg.baseline = ft_getopt(cfg, 'baseline', 'no'); @@ -175,8 +176,8 @@ assert((hastime && hasfreq), 'please use ft_singleplotER for time-only or frequency-only data'); -xparam = 'time'; -yparam = 'freq'; +xparam = ft_getopt(cfg, 'xparam', 'time'); +yparam = ft_getopt(cfg, 'yparam', 'freq'); % check whether rpt/subj is present and remove if necessary dimord = getdimord(data, cfg.parameter); @@ -223,7 +224,6 @@ [cfg, data] = rollback_provenance(cfg, data); cfg.channel = tmpchannel; - if isfield(tmpvar, cfg.maskparameter) && ~isfield(data, cfg.maskparameter) % the mask parameter is not present after ft_selectdata, because it is % not included in all input arguments. Make the same selection and copy @@ -325,15 +325,26 @@ % the usual data is chan_freq_time, but other dimords should also work dimtok = tokenize(dimord, '_'); datamatrix = data.(cfg.parameter); -[c, ia, ib] = intersect(dimtok, {'chan', yparam, xparam}); -datamatrix = permute(datamatrix, ia); +[c, ia, ib] = intersect({'chan', yparam, xparam}, dimtok, 'stable'); +datamatrix = permute(datamatrix, ib); datamatrix = datamatrix(selchan, sely, selx); if ~isempty(cfg.maskparameter) maskmatrix = data.(cfg.maskparameter)(selchan, sely, selx); - if cfg.maskalpha ~= 1 - maskmatrix( maskmatrix) = 1; + if islogical(maskmatrix) && any(strcmp(cfg.maskstyle, {'saturation', 'opacity'})) + maskmatrix = double(maskmatrix); maskmatrix(~maskmatrix) = cfg.maskalpha; + elseif isnumeric(maskmatrix) + if strcmp(cfg.maskstyle, 'outline') + error('Outline masking with a numeric cfg.maskparameter is not supported. Please use a logical mask instead.') + end + if cfg.maskalpha ~= 1 + warning(sprintf('Using field "%s" for masking, cfg.maskalpha is ignored.', cfg.maskparameter)) + end + % scale mask between 0 and 1 + minval = min(maskmatrix(:)); + maxval = max(maskmatrix(:)); + maskmatrix = (maskmatrix - minval) / (maxval-minval); end else % create an Nx0x0 matrix diff --git a/external/fieldtrip/ft_sourceanalysis.m b/external/fieldtrip/ft_sourceanalysis.m index e8bde478..e877fca6 100644 --- a/external/fieldtrip/ft_sourceanalysis.m +++ b/external/fieldtrip/ft_sourceanalysis.m @@ -182,7 +182,7 @@ cfg = ft_checkconfig(cfg, 'renamedval', {'method', 'dics_cohrefchan', 'dics'}); cfg = ft_checkconfig(cfg, 'renamedval', {'method', 'dics_cohrefdip', 'dics'}); cfg = ft_checkconfig(cfg, 'forbidden', {'parallel', 'trials'}); -cfg = ft_checkconfig(cfg, 'forbidden', {'foi', 'toi'}); +cfg = ft_checkconfig(cfg, 'forbidden', {'foi', 'toi'}); cfg = ft_checkconfig(cfg, 'renamed', {'hdmfile', 'headmodel'}); cfg = ft_checkconfig(cfg, 'renamed', {'vol', 'headmodel'}); diff --git a/external/fieldtrip/ft_sourcedescriptives.m b/external/fieldtrip/ft_sourcedescriptives.m index 6cf9c61e..005877f1 100644 --- a/external/fieldtrip/ft_sourcedescriptives.m +++ b/external/fieldtrip/ft_sourcedescriptives.m @@ -544,7 +544,7 @@ Cdr = lambda1(csd(dipsel, refsel)); source.avg.coh(i) = abs(Cdr).^2 ./ (Pd*Pr); case 'canonical' - [ccoh, c2, v1, v2] = cancorr(csd, dipsel, refsel); + [ccoh, c2, v1, v2] = ft_connectivity_cancorr(csd, dipsel, refsel); [cmax, indmax] = max(ccoh); source.avg.coh(i) = ccoh(indmax); otherwise diff --git a/external/fieldtrip/ft_sourceinterpolate.m b/external/fieldtrip/ft_sourceinterpolate.m index a7a3c0e8..bb53fa75 100644 --- a/external/fieldtrip/ft_sourceinterpolate.m +++ b/external/fieldtrip/ft_sourceinterpolate.m @@ -200,13 +200,12 @@ if ~isUnstructuredAna && cfg.downsample~=1 % downsample the anatomical volume tmpcfg = keepfields(cfg, {'downsample', 'showcallinfo'}); - orgcfg.parameter = cfg.parameter; tmpcfg.parameter = 'anatomy'; anatomical = ft_volumedownsample(tmpcfg, anatomical); - % restore the provenance information + % restore the provenance information and put back cfg.parameter + tmpparameter = cfg.parameter; [cfg, anatomical] = rollback_provenance(cfg, anatomical); - % restore the original parameter, it should not be 'anatomy' - cfg.parameter = orgcfg.parameter; + cfg.parameter = tmpparameter; end % collect the functional volumes that should be converted diff --git a/external/fieldtrip/ft_sourcemovie.m b/external/fieldtrip/ft_sourcemovie.m index 775aa5eb..5f462619 100644 --- a/external/fieldtrip/ft_sourcemovie.m +++ b/external/fieldtrip/ft_sourcemovie.m @@ -1,14 +1,15 @@ function [cfg, M] = ft_sourcemovie(cfg, source, source2) % FT_SOURCEMOVIE displays the source reconstruction on a cortical mesh -% and allows the user to scroll through time with a movie +% and allows the user to scroll through time with a movie. % % Use as % ft_sourcemovie(cfg, source) -% where the input source data is obtained from FT_SOURCEANALYSIS and cfg is -% a configuratioun structure that should contain +% where the input source data is obtained from FT_SOURCEANALYSIS, or a +% a parcellated source structure (i.e. contains a brainordinate field) and +% cfg is a configuration structure that should contain % -% cfg.funparameter = string, functional parameter that is color coded +% cfg.funparameter = string, functional parameter that is color coded (default = 'avg.pow') % cfg.maskparameter = string, functional parameter that is used for opacity (default = []) % % To facilitate data-handling and distributed computing you can use @@ -17,29 +18,11 @@ % file on disk. This mat files should contain only a single variable named 'data', % corresponding to the input structure. % -% See also FT_SOURCEPLOT, FT_SOURCEINTERPOLATE, FT_MOVIEPLOTER, FT_MOVIEPLOTTFR - -% Undocumented options: -% cfg.parcellation +% See also FT_SOURCEPLOT, FT_SOURCEINTERPOLATE, FT_SOURCEPARCELLATE % Copyright (C) 2011-2015, Robert Oostenveld % Copyright (C) 2012-2014, Jorn Horschig -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . +% Copyright (C) 2018, Jan-Mathijs Schoffelen % % $Id$ @@ -47,7 +30,6 @@ % the initial part deals with parsing the input options and data %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% these are used by the ft_preamble/ft_postamble function and scripts ft_revision = '$Id$'; ft_nargin = nargin; ft_nargout = nargout; @@ -55,104 +37,53 @@ % do the general setup of the function ft_defaults ft_preamble init +if ft_abort + return +end ft_preamble debug ft_preamble loadvar source ft_preamble provenance source ft_preamble trackconfig -% the ft_abort variable is set to true or false in ft_preamble_init -if ft_abort - return -end - -% the data can be passed as input argument or can be read from disk -hassource2 = exist('source2', 'var'); - -% check if the input data is valid for this function +% ensure that the input data is valid for this function, this will also do +% backward-compatibility conversions of old data that for example was +% read from an old *.mat file source = ft_checkdata(source, 'datatype', 'source', 'feedback', 'yes'); -% check if the input cfg is valid for this function -cfg = ft_checkconfig(cfg, 'renamed', {'zparam', 'funparameter'}); -cfg = ft_checkconfig(cfg, 'renamed', {'parameter', 'funparameter'}); -cfg = ft_checkconfig(cfg, 'renamed', {'mask', 'maskparameter'}); -cfg = ft_checkconfig(cfg, 'required', 'funparameter'); - -% these are not needed any more, once the source structure has a proper dimord -% cfg = ft_checkconfig(cfg, 'deprecated', 'xparam'); -% cfg = ft_checkconfig(cfg, 'deprecated', 'yparam'); - % get the options -xlim = ft_getopt(cfg, 'xlim'); -ylim = ft_getopt(cfg, 'ylim'); -zlim = ft_getopt(cfg, 'zlim'); -olim = ft_getopt(cfg, 'alim'); % don't use alim as variable name -cfg.xparam = ft_getopt(cfg, 'xparam'); % default is dealt with below -cfg.yparam = ft_getopt(cfg, 'yparam'); % default is dealt with below -cfg.funparameter = ft_getopt(cfg, 'funparameter'); +xlim = ft_getopt(cfg, 'time'); +ylim = ft_getopt(cfg, 'frequency'); +zlim = ft_getopt(cfg, 'funcolorlim'); +olim = ft_getopt(cfg, 'opacitylim'); % don't use alim as variable name +cfg.xparam = ft_getopt(cfg, 'xparam', 'time'); % use time as default +cfg.yparam = ft_getopt(cfg, 'yparam'); % default is dealt with below +cfg.funparameter = ft_getopt(cfg, 'funparameter', 'avg.pow'); % use power as default +cfg.funcolormap = ft_getopt(cfg, 'funcolormap', 'jet'); cfg.maskparameter = ft_getopt(cfg, 'maskparameter'); -cfg.renderer = ft_getopt(cfg, 'renderer', 'opengl'); -cfg.title = ft_getopt(cfg, 'title'); -cfg.parcellation = ft_getopt(cfg, 'parcellation'); - -% select the functional and the mask parameter -cfg.funparameter = parameterselection(cfg.funparameter, source); -cfg.maskparameter = parameterselection(cfg.maskparameter, source); - -% only a single parameter should be selected -if ~isempty(cfg.funparameter) && iscell(cfg.funparameter), cfg.funparameter = cfg.funparameter{1}; end -if ~isempty(cfg.maskparameter) && iscell(cfg.maskparameter), cfg.maskparameter = cfg.maskparameter{1}; end - -dimord = getdimord(source, cfg.funparameter); -dimtok = tokenize(dimord, '_'); -if numel(dimtok)==2 - % for example pos_freq or pos_time - if isempty(cfg.xparam), cfg.xparam = dimtok{2}; end -elseif numel(dimtok)==3 - % for example pos_freq_time - if isempty(cfg.yparam), cfg.yparam = dimtok{2}; end % frequency along vertical axis by default - if isempty(cfg.xparam), cfg.xparam = dimtok{3}; end % time along horizontal axis by default, this is also the time dimension in the movie +if isempty(cfg.yparam) && isfield(source, 'freq') + % the default is freq (if present) + cfg.yparam = 'freq'; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % the actual computation is done in the middle part %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% the user specifies xparam and yparam, it may be needed to permute the dimensions -[dum, order] = match_str(dimtok, {'pos', cfg.xparam, cfg.yparam}); - -if ~hassource2 - fun = getsubfield(source, cfg.funparameter); - fun = permute(fun, order); - -elseif hassource2 && isfield(source2, 'pos') - fun = getsubfield(source, cfg.funparameter); - fun2 = getsubfield(source2, cfg.funparameter); - fun = permute(fun, order); - fun2 = permute(fun2, order); - -elseif hassource2 - % assume the first data argument to be a parcellation, and the second a parcellated structure - tmp = getsubfield(source2, cfg.funparameter); - tmp = permute(tmp, order); +if nargin==2 + fun = getsubfield(source, cfg.funparameter); % might be avg.pow +elseif nargin>2 && isfield(source2, 'pos') + % in this case two conditions of data seem to have been inputted + fun = getsubfield(source, cfg.funparameter); % might be avg.pow + fun2 = getsubfield(source2, cfg.funparameter); +elseif nargin>2 + % assume the first data argument to be a parcellation, and the second a + % parcellated structure - siz = [size(tmp) 1]; - fun = zeros([size(source.pos, 1), siz(2:end)]); - parcels = source.(cfg.parcellation); - parcelslabel = source.([cfg.parcellation,'label']); - for k = 1:numel(source2.label) - sel = match_str(source.([cfg.parcellation,'label']), source2.label{k}); - if ~isempty(sel) - sel = source.(cfg.parcellation)==sel; - fun(sel,:,:) = repmat(tmp(k,:,:), [sum(sel) 1]); - end - end - source.(cfg.xparam) = source2.(cfg.xparam); - if ~isempty(cfg.yparam) - source.(cfg.yparam) = source2.(cfg.yparam); - end + % THIS DOES NOT EXIST ANYMORE: IF PARCELLATED DATA IS IN THE INPUT, IT + % WILL BE UNPARCELLATED AUTOMATICALLY BY FT_CHECKDATA + ft_error('bogus'); end - if size(source.pos)~=size(fun,1) ft_error('inconsistent number of vertices in the cortical mesh'); end @@ -163,23 +94,20 @@ if ~isempty(cfg.maskparameter) && ischar(cfg.maskparameter) mask = double(getsubfield(source, cfg.maskparameter)); - mask = permute(mask, order); else mask = 0.5*ones(size(fun)); end xparam = source.(cfg.xparam); if length(xparam)~=size(fun,2) - ft_error('inconsistent size of "%s" compared to "%s"', cfg.funparameter, cfg.xparam); + error('inconsistent size of "%s" compared to "%s"', cfg.funparameter, cfg.xparam); end if ~isempty(cfg.yparam) yparam = source.(cfg.yparam); - if length(yparam)~=size(fun,3) - ft_error('inconsistent size of "%s" compared to "%s"', cfg.funparameter, cfg.yparam); + if length(cfg.yparam)~=size(fun,3) + error('inconsistent size of "%s" compared to "%s"', cfg.funparameter, cfg.yparam); end -else - yparam = []; end if isempty(xlim) @@ -192,7 +120,7 @@ % update the configuration cfg.xlim = xparam([xbeg xend]); -if ~isempty(yparam) +if ~isempty(cfg.yparam) if isempty(ylim) ylim(1) = min(yparam); ylim(2) = max(yparam); @@ -200,7 +128,7 @@ ybeg = nearest(yparam, ylim(1)); yend = nearest(yparam, ylim(2)); % update the configuration - cfg.ylim = xparam([xbeg xend]); + cfg.ylim = yparam([ybeg yend]); hasyparam = true; else % this allows us not to worry about the yparam any more @@ -215,8 +143,8 @@ xparam = xparam(xbeg:xend); yparam = yparam(ybeg:yend); fun = fun(:,xbeg:xend,ybeg:yend); -if hassource2 && isfield(source2, 'pos') - fun2 = fun2(:,xbeg:xend,ybeg:yend); +if exist('fun2', 'var') + fun2 = fun2(:,xbeg:xend,ybeg:yend); end mask = mask(:,xbeg:xend,ybeg:yend); clear xbeg xend ybeg yend @@ -225,7 +153,7 @@ zlim(1) = min(fun(:)); zlim(2) = max(fun(:)); % update the configuration - cfg.zlim = zlim; + cfg.funcolorlim = zlim; end if isempty(olim) @@ -236,7 +164,7 @@ olim(2) = 1; end % update the configuration - %cfg.alim = olim; + cfg.opacitylim = olim; end % collect the data and the options to be used in the figure @@ -246,41 +174,35 @@ opt.xval = 0; opt.yval = 0; opt.dat = fun; -opt.mask = abs(mask); +opt.mask = mask; opt.pos = source.pos; opt.tri = source.tri; -if isfield(source, 'inside') - opt.vindx = source.inside(:); -else - opt.vindx = 1:size(opt.pos,1); -end +opt.vindx = source.inside(:); opt.speed = 1; opt.record = 0; opt.threshold = 0; opt.frame = 0; opt.cleanup = false; -if exist('parcels', 'var'), opt.parcellation = parcels; end -if exist('parcelslabel', 'var'), opt.parcellationlabel = parcelslabel; end +if isfield(source, 'parcellation') + opt.parcellation = source.parcellation; + opt.parcellationlabel = source.parcellationlabel; +end + % add functional data of optional third input to the opt structure % FIXME here we should first check whether the meshes correspond! -if hassource2 && isfield(source2, 'pos') +if nargin>2 && isfield(source2, 'pos') opt.dat2 = fun2; - opt.dat1 = opt.dat; end -%% start building the figure -h = figure; +% get a handle to a figure +h = figure; set(h, 'color', [1 1 1]); -set(h, 'visible', 'on'); -set(h, 'renderer', cfg.renderer); set(h, 'toolbar', 'figure'); +set(h, 'visible', 'on'); set(h, 'CloseRequestFcn', @cb_quitbutton); -set(h, 'position', [100 200 700 500]); -set(h, 'windowbuttondownfcn', @cb_getposition); -if ~isempty(cfg.title) - title(cfg.title); -end +set(h, 'position', [100 50 1000 600]); +set(h, 'windowbuttondownfcn', @cb_getposition); % get timer object t = timer; @@ -291,88 +213,72 @@ playbutton = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'play', 'userdata', 'p'); recordbutton = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'record', 'userdata', 'r'); quitbutton = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'quit', 'userdata', 'q'); -if isfield(opt, 'dat2') - displaybutton = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'display: var1', 'userdata', 'f'); -end - -thrmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', 'downarrow'); -thr = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'threshold', 'userdata', 't'); -thrplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', 'uparrow'); -spdmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', 'shift+downarrow'); -spd = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'speed','userdata', 's'); -spdplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', 'shift+uparrow'); -clim = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'colorlim', 'userdata', 'z'); -climminmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'leftarrow'); -climmaxmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+leftarrow'); -climminplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'rightarrow'); -climmaxplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+rightarrow'); -sliderx = uicontrol('parent', h, 'units', 'normalized', 'style', 'slider', 'string', sprintf('%s = ', cfg.xparam)); -stringx = uicontrol('parent', h, 'units', 'normalized', 'style', 'text'); -slidery = uicontrol('parent', h, 'units', 'normalized', 'style', 'slider', 'string', sprintf('%s = ', cfg.yparam)); -stringy = uicontrol('parent', h, 'units', 'normalized', 'style', 'text'); -stringz = uicontrol('parent', h, 'units', 'normalized', 'style', 'text'); -stringp = uicontrol('parent', h, 'units', 'normalized', 'style', 'text'); - -if isfield(opt,'dat2') - set(displaybutton, 'position', [0.005 0.34 0.18 0.05], 'callback', @cb_keyboard); -end - -set(cambutton, 'position', [0.095 0.28 0.09 0.05], 'callback', @cb_keyboard); -set(quitbutton, 'position', [0.005 0.28 0.09 0.05], 'callback', @cb_keyboard); -set(playbutton, 'position', [0.005 0.22 0.09 0.05], 'callback', @cb_keyboard); -set(recordbutton, 'position', [0.095 0.22 0.09 0.05], 'callback', @cb_keyboard); -set(thrmin, 'position', [0.005 0.16 0.03 0.05], 'callback', @cb_keyboard); -set(thr, 'position', [0.035 0.16 0.12 0.05], 'callback', @cb_keyboard); -set(thrplus, 'position', [0.155 0.16 0.03 0.05], 'callback', @cb_keyboard); + +thrminmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'downarrow'); +thrminplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+downarrow'); +thr = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'threshold', 'userdata', 't'); +thrplusmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'shift+uparrow'); +thrplusplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'uparrow'); + +spdmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', 'shift+downarrow'); +spd = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'speed','userdata', 's'); +spdplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', 'shift+uparrow'); +climminmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'leftarrow'); +climminplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+leftarrow'); +clim = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'colorlim', 'userdata', 'z'); +climplusmin = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'shift+rightarrow'); +climplusplus = uicontrol('parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'rightarrow'); +sliderx = uicontrol('parent', h, 'units', 'normalized', 'style', 'slider', 'string', sprintf('%s = ', cfg.xparam)); +stringx = uicontrol('parent', h, 'units', 'normalized', 'style', 'text'); +slidery = uicontrol('parent', h, 'units', 'normalized', 'style', 'slider', 'string', sprintf('%s = ', cfg.yparam)); +stringy = uicontrol('parent', h, 'units', 'normalized', 'style', 'text'); +stringz = uicontrol('parent', h, 'units', 'normalized', 'style', 'text'); + +set(cambutton, 'position', [0.095 0.28 0.09 0.05 ], 'callback', @cb_keyboard); +set(quitbutton, 'position', [0.005 0.28 0.09 0.05 ], 'callback', @cb_keyboard); +set(playbutton, 'position', [0.005 0.22 0.09 0.05 ], 'callback', @cb_keyboard); +set(recordbutton, 'position', [0.095 0.22 0.09 0.05 ], 'callback', @cb_keyboard); +set(thrminmin, 'position', [0.005 0.16 0.03 0.025], 'callback', @cb_keyboard); +set(thrminplus, 'position', [0.005 0.185 0.03 0.025], 'callback', @cb_keyboard); +set(thr, 'position', [0.035 0.16 0.12 0.05 ], 'callback', @cb_keyboard); +set(thrplusmin, 'position', [0.155 0.16 0.03 0.025], 'callback', @cb_keyboard); +set(thrplusplus, 'position', [0.155 0.185 0.03 0.025], 'callback', @cb_keyboard); set(climminmin, 'position', [0.005 0.10 0.03 0.025], 'callback', @cb_keyboard); -set(climmaxmin, 'position', [0.005 0.125 0.03 0.025], 'callback', @cb_keyboard); -set(clim, 'position', [0.035 0.10 0.12 0.05], 'callback', @cb_keyboard); -set(climminplus, 'position', [0.155 0.10 0.03 0.025], 'callback', @cb_keyboard); -set(climmaxplus, 'position', [0.155 0.125 0.03 0.025], 'callback', @cb_keyboard); -set(spdmin, 'position', [0.005 0.04 0.03 0.05], 'callback', @cb_keyboard); -set(spd, 'position', [0.035 0.04 0.12 0.05], 'callback', @cb_keyboard); -set(spdplus, 'position', [0.155 0.04 0.03 0.05], 'callback', @cb_keyboard); -set(sliderx, 'position', [0.02 0.4 0.3 0.03], 'callback', @cb_slider); % [0.200 0.04 0.78 0.03], 'callback', @cb_slider); -set(slidery, 'position', [0.350 0.5 0.03 0.35], 'callback', @cb_slider); -set(stringx, 'position', [0.800 0.93 0.18 0.03]); -set(stringy, 'position', [0.800 0.90 0.18 0.03]); -set(stringz, 'position', [0.650 0.96 0.33 0.03]); -set(stringp, 'position', [0.650 0.87 0.33 0.03]); +set(climminplus, 'position', [0.005 0.125 0.03 0.025], 'callback', @cb_keyboard); +set(clim, 'position', [0.035 0.10 0.12 0.05 ], 'callback', @cb_keyboard); +set(climplusmin, 'position', [0.155 0.10 0.03 0.025], 'callback', @cb_keyboard); +set(climplusplus, 'position', [0.155 0.125 0.03 0.025], 'callback', @cb_keyboard); +set(spdmin, 'position', [0.005 0.04 0.03 0.05 ], 'callback', @cb_keyboard); +set(spd, 'position', [0.035 0.04 0.12 0.05 ], 'callback', @cb_keyboard); +set(spdplus, 'position', [0.155 0.04 0.03 0.05 ], 'callback', @cb_keyboard); +set(sliderx, 'position', [0.02 0.35 0.38 0.03 ], 'callback', @cb_slider); +set(slidery, 'position', [0.200 0.005 0.78 0.03 ], 'callback', @cb_slider); +set(stringx, 'position', [0.750 0.90 0.18 0.03 ]); +set(stringy, 'position', [0.750 0.85 0.18 0.03 ]); +set(stringz, 'position', [0.60 0.95 0.33 0.03 ]); set(stringx, 'string', sprintf('%s = ', cfg.xparam)); set(stringy, 'string', sprintf('%s = ', cfg.yparam)); -set(stringz, 'string', sprintf('position = ')); -set(stringp, 'string', sprintf('parcel = ')); +set(stringz, 'string', sprintf('location = ')); set(stringx, 'horizontalalignment', 'right', 'backgroundcolor', [1 1 1]); set(stringy, 'horizontalalignment', 'right', 'backgroundcolor', [1 1 1]); set(stringz, 'horizontalalignment', 'right', 'backgroundcolor', [1 1 1]); -set(stringp, 'horizontalalignment', 'right', 'backgroundcolor', [1 1 1]); % create axes object to contain the mesh hx = axes; set(hx, 'position', [0.4 0.08 0.6 0.8]); set(hx, 'tag', 'mesh'); -if isfield(source, 'sulc') - vdat = source.sulc; - vdat = vdat-min(vdat); - vdat = vdat./max(vdat); - vdat = 0.1+0.3.*repmat(round(1-vdat),[1 3]); - hs1 = ft_plot_mesh(source, 'edgecolor', 'none', 'vertexcolor', vdat); -else - hs1 = ft_plot_mesh(source, 'edgecolor', 'none', 'facecolor', [0.5 0.5 0.5]); -end -lighting gouraud -siz = [size(opt.dat) 1]; -hs = ft_plot_mesh(source, 'edgecolor', 'none', 'vertexcolor', 0*opt.dat(:,ceil(siz(2)/2),ceil(siz(3)/2)));%, 'facealpha', 0*opt.mask(:,1,1)); +hs = ft_plot_mesh(source, 'edgecolor', 'none', 'facecolor', [0.5 0.5 0.5], 'vertexcolor', 0.*opt.dat(:,1,1), 'facealpha', 0.*opt.mask(:,1,1), 'clim', [0 1], 'alphalim', [0 1], 'alphamap', 'rampup', 'colormap', cfg.funcolormap, 'maskstyle', 'colormix'); + lighting gouraud +material dull cam1 = camlight('left'); cam2 = camlight('right'); -caxis(cfg.zlim); -%alim(cfg.alim); +caxis(cfg.funcolorlim); % create axis object to contain a time course hy = axes; -set(hy, 'position', [0.02 0.5 0.3 0.35]); +set(hy, 'position', [0.02 0.45 0.38 0.5]); set(hy, 'yaxislocation', 'right'); if ~hasyparam @@ -380,17 +286,16 @@ abc = axis; axis([opt.xparam(1) opt.xparam(end) abc(3:4)]); vline = plot(opt.xparam(1)*[1 1], abc(3:4), 'r'); + hline1 = plot(opt.xparam([1 end]), [0 0], 'k'); + hline2 = plot(opt.xparam([1 end]), [0 0], 'k'); - if hassource2 && isfield(source2, 'pos') + + if nargin>2 && isfield(source2, 'pos') tline2 = plot(opt.xparam, mean(opt.dat2(opt.vindx,:)), 'r'); hold on; end else - tline = imagesc(opt.xparam, opt.yparam, shiftdim(mean(opt.dat(opt.vindx,:,:)),1)'); axis xy; hold on; - abc = [opt.xparam([1 end]) opt.yparam([1 end])]; - vline = plot(opt.xparam(ceil(siz(2)/2)).*[1 1], abc(3:4)); - hline = plot(abc(1:2), opt.yparam(ceil(siz(3)/2)).*[1 1]); - %error('not yet implemented'); + error('not yet implemented'); end set(hy, 'tag', 'timecourse'); @@ -400,42 +305,33 @@ opt.hx = hx; % handle to the axes containing the mesh opt.hy = hy; % handle to the axes containing the timecourse opt.cam = [cam1 cam2]; % handles to the light objects -opt.vline = vline; % handle to the line in the ERF plot +opt.vline = vline; % handle to the vertical line in the ERF plot opt.tline = tline; % handle to the ERF -if exist('hline', 'var') - opt.hline = hline; -end -if hassource2 && isfield(source2, 'pos') - opt.tline2 = tline2; +if nargin>2 && isfield(source2, 'pos') + opt.tline2 = tline2; end +opt.hline1 = hline1; % handle for the horizontal line for upper threshold +opt.hline2 = hline2; % handle for hte horizontal line for lower threshold opt.playbutton = playbutton; % handle to the playbutton opt.recordbutton = recordbutton; % handle to the recordbutton opt.quitbutton = quitbutton; % handle to the quitbutton -try, opt.displaybutton = displaybutton; end +opt.threshold = [0 0]; -%opt.p = p; opt.t = t; -%opt.hx = hx; -%opt.hy = hy; opt.sliderx = sliderx; opt.slidery = slidery; opt.stringx = stringx; opt.stringy = stringy; opt.stringz = stringz; -opt.stringp = stringp; if ~hasyparam set(opt.slidery, 'visible', 'off'); set(opt.stringy, 'visible', 'off'); end -if ~exist('parcels', 'var') - set(opt.stringp, 'visible', 'off'); -end - setappdata(h, 'opt', opt); -while opt.cleanup==false +while opt.cleanup==0 uiwait(h); opt = getappdata(h, 'opt'); end @@ -447,6 +343,7 @@ delete(h); +% do the general cleanup and bookkeeping at the end of the function % do the general cleanup and bookkeeping at the end of the function ft_postamble debug ft_postamble trackconfig @@ -458,7 +355,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function cb_slider(h, eventdata) -persistent previous_valx previous_valy previous_vindx +persistent previous_valx previous_valy previous_vindx previous_clim previous_thr if isempty(previous_valx) previous_valx = 0; @@ -466,6 +363,12 @@ function cb_slider(h, eventdata) if isempty(previous_valy) previous_valy = 0; end +if isempty(previous_clim) + previous_clim = [0 1]; +end +if isempty(previous_thr) + previous_thr = [0 0]; +end h = getparent(h); opt = getappdata(h, 'opt'); @@ -479,54 +382,46 @@ function cb_slider(h, eventdata) valy = min(valy, size(opt.dat,3)); valy = max(valy, 1); -mask = opt.mask(:,valx,valy); -mask(opt.dat(:,valx,valy)opt.threshold(1)&dat0 + opt.threshold(2) = opt.cfg.funcolorlim(2).*0.1; + elseif thrval==0 + opt.threshold(2) = max(opt.dat(:)).*0.1; + else + opt.threshold(2) = thrval.*1.1; + end + setappdata(h, 'opt', opt); + cb_slider(h); + set(opt.hline1, 'YData', [1 1].*opt.threshold(2)); + case 'shift+uparrow' % enhance threshold + thrval = opt.threshold(2); + if thrval>0 + opt.threshold(2) = thrval.*0.9; + end setappdata(h, 'opt', opt); + cb_slider(h); + set(opt.hline1, 'YData', [1 1].*opt.threshold(2)); case 'downarrow' % lower threshold - opt.threshold = opt.threshold-0.01.*max(opt.dat(:)); + thrval = opt.threshold(1); + if thrval==0 && opt.cfg.funcolorlim(1)<0 + opt.threshold(1) = opt.cfg.funcolorlim(1).*0.1; + elseif thrval==0 + opt.threshold(1) = min(opt.dat(:)).*0.1; + else + opt.threshold(1) = thrval.*1.1; + end + setappdata(h, 'opt', opt); + cb_slider(h); + set(opt.hline2, 'YData', [1 1].*opt.threshold(1)); + case 'shift+downarrow' % lower threshold + thrval = opt.threshold(1); + if thrval<0 + opt.threshold(1) = thrval.*0.9; + end setappdata(h, 'opt', opt); - case 'shift+uparrow' % change speed + cb_slider(h); + set(opt.hline2, 'YData', [1 1].*opt.threshold(1)); + case 'ctrl+uparrow' % change speed opt.speed = opt.speed*sqrt(2); setappdata(h, 'opt', opt); - case 'shift+downarrow' + case 'ctrl+downarrow' opt.speed = opt.speed/sqrt(2); opt.speed = max(opt.speed, 1); % should not be smaller than 1 setappdata(h, 'opt', opt); - case 'ctrl+uparrow' % change channel - case 'C' % update camera position + case 'ctrl+rightarrow' % change channel + case 'C' % update camera position1.373e-14 camlight(opt.cam(1), 'left'); camlight(opt.cam(2), 'right'); case 'p' @@ -725,32 +673,27 @@ function cb_keyboard(h, eventdata) % select the threshold response = inputdlg('threshold', 'specify', 1, {num2str(opt.threshold)}); if ~isempty(response) - opt.threshold = str2double(response); - setappdata(h, 'opt', opt); + tok = tokenize(response{1}, ' '); + tok(cellfun(@isempty,tok)) = []; + for k = 1:numel(tok) + opt.threshold(1,k) = str2double(tok{k}); + end + setappdata(h, 'opt', opt); end + set(opt.hline1, 'YData', [1 1].*opt.threshold(2)); + set(opt.hline2, 'YData', [1 1].*opt.threshold(1)); case 'z' % select the colorlim - response = inputdlg('colorlim', 'specify', 1, {[num2str(opt.cfg.zlim(1)),' ',num2str(opt.cfg.zlim(2))]}); + response = inputdlg('colorlim', 'specify', 1, {num2str(opt.cfg.funcolorlim)}); if ~isempty(response) - [tok1, tok2] = strtok(response, ' '); - opt.cfg.zlim(1) = str2double(deblank(tok1)); - opt.cfg.zlim(2) = str2double(deblank(tok2)); - set(opt.hx, 'Clim', opt.cfg.zlim); - setappdata(h, 'opt', opt); - end - case 'f' - if isfield(opt, 'dat2') - if isequaln(opt.dat,opt.dat2) - opt.dat = opt.dat1; - set(opt.displaybutton, 'string', 'display: var1'); - end - if isequaln(opt.dat,opt.dat1) - opt.dat = opt.dat2; - set(opt.displaybutton, 'string', 'display: var2'); + tok = tokenize(response{1}, ' '); + tok(cellfun(@isempty,tok)) = []; + for k = 1:2 + opt.cfg.funcolorlim(1,k) = str2double(tok{k}); end + setappdata(h, 'opt', opt); + cb_slider(h); end - setappdata(h, 'opt', opt); - cb_slider(h); case 'control+control' % do nothing case 'shift+shift' @@ -759,7 +702,7 @@ function cb_keyboard(h, eventdata) % do nothing otherwise setappdata(h, 'opt', opt); - cb_help(h); + %cb_help(h); end cb_slider(h); uiresume(h); @@ -774,30 +717,3 @@ function cb_keyboard(h, eventdata) p = get(h, 'parent'); end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; - -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character; - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end diff --git a/external/fieldtrip/ft_sourceparcellate.m b/external/fieldtrip/ft_sourceparcellate.m index 3a5e8825..81d30cb8 100644 --- a/external/fieldtrip/ft_sourceparcellate.m +++ b/external/fieldtrip/ft_sourceparcellate.m @@ -104,7 +104,7 @@ source = ft_checkdata(source, 'datatype', 'source', 'inside', 'logical'); % ensure that the source and the parcellation are anatomically consistent -if ~isequaln(source.pos, parcellation.pos) +if ~isalmostequal(source.pos, parcellation.pos, 'abstol', 1000*eps) ft_error('the source positions are not consistent with the parcellation, please use FT_SOURCEINTERPOLATE'); end @@ -347,7 +347,7 @@ ft_postamble savevar parcel %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTIONS to complute something over the first dimension +% SUBFUNCTIONS to compute something over the first dimension %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function y = arraymean1(x, threshold) @@ -369,25 +369,46 @@ end function y = arraymedian1(x) -y = median(x,1); +if ~isempty(x) + y = median(x,1); +else + y = nan(1,size(x,2)); +end function y = arraymin1(x) -y = min(x,[], 1); - +if ~isempty(x) + y = min(x,[], 1); +else + y = nan(1,size(x,2)); +end + function y = arraymax1(x) -y = max(x,[], 1); - +if ~isempty(x) + y = max(x,[], 1); +else + y = nan(1,size(x,2)); +end + function y = arrayeig1(x) -siz = size(x); -x = reshape(x, siz(1), prod(siz(2:end))); -[u, s, v] = svds(x, 1); % x = u * s * v' -y = s(1,1) * v(:,1); % retain the largest eigenvector with appropriate scaling -y = reshape(y, [siz(2:end) 1]); % size should have at least two elements +if ~isempty(x) + siz = size(x); + x = reshape(x, siz(1), prod(siz(2:end))); + [u, s, v] = svds(x, 1); % x = u * s * v' + y = s(1,1) * v(:,1); % retain the largest eigenvector with appropriate scaling + y = reshape(y, [siz(2:end) 1]); % size should have at least two elements +else + siz = size(x); + y = nan([siz(2:end) 1]); +end function y = arraymaxabs1(x) -% take the value that is at max(abs(x)) -[dum,ix] = max(abs(x), [], 1); -y = x(ix); +if ~isempty(x) + % take the value that is at max(abs(x)) + [dum,ix] = max(abs(x), [], 1); + y = x(ix); +else + y = nan(1,size(x,2)); +end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTIONS to compute something over the first two dimensions diff --git a/external/fieldtrip/ft_sourceplot.m b/external/fieldtrip/ft_sourceplot.m index fdb03607..f1996338 100644 --- a/external/fieldtrip/ft_sourceplot.m +++ b/external/fieldtrip/ft_sourceplot.m @@ -30,7 +30,7 @@ function ft_sourceplot(cfg, functional, anatomical) % 'surface', plots the data on a 3D brain surface % 'glassbrain', plots a max-projection through the brain % 'vertex', plots the grid points or vertices scaled according to the functional value -% 'cloud', plot the data as point clouds scaled according to the functional value +% 'cloud', plot the data as clouds, spheres, or points scaled according to the functional value % % % cfg.anaparameter = string, field in data with the anatomical data (default = 'anatomy' if present in data) @@ -128,7 +128,8 @@ function ft_sourceplot(cfg, functional, anatomical) % 'auto', full range of data % [min max], coordinates of first and last slice in voxels % cfg.slicedim = dimension to slice 1 (x-axis) 2(y-axis) 3(z-axis) (default = 3) -% cfg.title = string, title of the figure window +% cfg.title = string, title of the plot +% cfg.figurename = string, title of the figure window % % When cfg.method='surface', the functional data will be rendered onto a cortical mesh % (can be an inflated mesh). If the input source data contains a tri-field (i.e. a @@ -166,13 +167,16 @@ function ft_sourceplot(cfg, functional, anatomical) % or an Nx3 or Nx1 array where N is the number of vertices % cfg.edgecolor = [r g b] values or string, for example 'brain', 'cortex', 'skin', 'black', 'red', 'r' % -% When cfg.method = 'cloud', the functional data will be rendered as -% point clouds around the sensor positions. These point clouds can either -% be viewed in 3D or as 2D slices. The 'anatomical' input may also consist of +% When cfg.method = 'cloud', the functional data will be rendered as as clouds (groups of points), spheres, or +% single points at each sensor position. These spheres or point clouds can either +% be viewed in 3D or as 2D slices. The 'anatomical' input may also consist of % a single or multiple triangulated surface mesh(es) in an Nx1 cell-array % to be plotted with the interpolated functional data (see FT_PLOT_CLOUD) % -% The following parameters apply to cfg.method='cloud' +% The following parameters apply to cfg.method='elec' +% cfg.cloudtype = 'point' plots a single point at each sensor position +% 'cloud' (default) plots each a group of spherically arranged points at each sensor position +% 'surf' plots a single spherical surface mesh at each sensor position % cfg.radius = scalar, maximum radius of cloud (default = 4) % cfg.colorgrad = 'white' or a scalar (e.g. 1), degree to which color of points in cloud % changes from its center @@ -277,7 +281,8 @@ function ft_sourceplot(cfg, functional, anatomical) cfg.maskparameter = ft_getopt(cfg, 'maskparameter', []); cfg.maskstyle = ft_getopt(cfg, 'maskstyle', 'opacity'); cfg.downsample = ft_getopt(cfg, 'downsample', 1); -cfg.title = ft_getopt(cfg, 'title', ''); +cfg.title = ft_getopt(cfg, 'title', []); +cfg.figurename = ft_getopt(cfg, 'figurename', []); cfg.atlas = ft_getopt(cfg, 'atlas', []); cfg.marker = ft_getopt(cfg, 'marker', []); cfg.markersize = ft_getopt(cfg, 'markersize', 5); @@ -702,7 +707,12 @@ function ft_sourceplot(cfg, functional, anatomical) h = figure('visible', cfg.visible); set(h, 'color', [1 1 1]); set(h, 'renderer', cfg.renderer); +if ~isempty(cfg.figurename) + % this appears as the name of the window + set(h, 'name', cfg.figurename); +end if ~isempty(cfg.title) + % this appears above the axes title(cfg.title); end @@ -1010,7 +1020,7 @@ function ft_sourceplot(cfg, functional, anatomical) set(h, 'windowbuttondownfcn', @cb_buttonpress); set(h, 'windowbuttonupfcn', @cb_buttonrelease); set(h, 'windowkeypressfcn', @cb_keyboard); - set(h, 'CloseRequestFcn', @cb_cleanup); + set(h, 'CloseRequestFcn', @cb_quit); % ensure that this is done in interactive mode set(h, 'renderer', cfg.renderer); @@ -1245,7 +1255,7 @@ function ft_sourceplot(cfg, functional, anatomical) % color + opacity into a single rgb value to speed up the rendering ft_plot_mesh(surf, 'edgecolor', cfg.edgecolor, 'facecolor', cfg.facecolor, 'vertexcolor', val, 'facealpha', maskval, 'clim', [fcolmin fcolmax], 'alphalim', [opacmin opacmax], 'alphamap', cfg.opacitymap, 'colormap', cfg.funcolormap, 'maskstyle', 'colormix'); - end + end end end @@ -1260,12 +1270,10 @@ function ft_sourceplot(cfg, functional, anatomical) % use a normal MATLAB colorbar hc = colorbar; if strcmp(cfg.maskstyle, 'opacity') - % functional values are according to original input values - set(hc, 'YLim', [fcolmin fcolmax]); + % functional values are according to original input values + set(hc, 'YLim', [fcolmin fcolmax]); else - % functional values have been transformed to be scaled - set(hc,'ticks',(0:0.1:1)); - set(hc,'ticklabels',round(100*linspace(fcolmin,fcolmax,numel(get(hc,'ticks'))'))./100); + % functional values have been transformed to be scaled end else ft_warning('no colorbar possible without functional data') @@ -1349,6 +1357,7 @@ function ft_sourceplot(cfg, functional, anatomical) % some defaults depend on the geometrical units scale = ft_scalingfactor('mm', functional.unit); % set the defaults for method=cloud + cfg.cloudtype = ft_getopt(cfg, 'cloudtype', 'cloud'); cfg.radius = ft_getopt(cfg, 'radius', 4*scale); cfg.rmin = ft_getopt(cfg, 'rmin', 1*scale); cfg.scalerad = ft_getopt(cfg, 'scalerad', 'yes'); @@ -1356,8 +1365,8 @@ function ft_sourceplot(cfg, functional, anatomical) cfg.ptdensity = ft_getopt(cfg, 'ptdensity', 20); cfg.ptgradient = ft_getopt(cfg, 'ptgradient', .5); cfg.colorgrad = ft_getopt(cfg, 'colorgrad', 'white'); + cfg.marker = ft_getopt(cfg, 'marker', '.'); cfg.slice = ft_getopt(cfg, 'slice', 'none'); - cfg.slicetype = ft_getopt(cfg, 'slicetype', 'point'); cfg.ori = ft_getopt(cfg, 'ori', 'y'); cfg.slicepos = ft_getopt(cfg, 'slicepos', 'auto'); cfg.nslices = ft_getopt(cfg, 'nslices', 1); @@ -1371,6 +1380,7 @@ function ft_sourceplot(cfg, functional, anatomical) cfg.edgecolor = ft_getopt(cfg, 'edgecolor', 'none'); cfg.facealpha = ft_getopt(cfg, 'facealpha', 1); cfg.edgealpha = ft_getopt(cfg, 'edgealpha', 0); + cfg.vertexcolor = ft_getopt(cfg, 'vertexcolor', 'curv'); % curvature-dependent mix of cortex_light and cortex_dark if ~hasanatomical; anatomical = {}; end if isUnstructuredFun @@ -1391,12 +1401,13 @@ function ft_sourceplot(cfg, functional, anatomical) 'radius', cfg.radius, 'rmin', cfg.rmin, 'scalerad', cfg.scalerad, ... 'ptsize', cfg.ptsize, 'ptdensity', cfg.ptdensity, 'ptgradient', cfg.ptgradient,... 'colorgrad', cfg.colorgrad, 'colormap', cfg.funcolormap, 'clim', [fcolmin fcolmax], ... - 'unit', functional.unit, 'slice', cfg.slice, 'slicetype', cfg.slicetype, ... + 'unit', functional.unit, 'slice', cfg.slice, 'cloudtype', cfg.cloudtype, ... 'ori', cfg.ori, 'slicepos', cfg.slicepos, 'nslices', cfg.nslices, 'minspace', cfg.minspace,... 'intersectcolor', cfg.intersectcolor, 'intersectlinestyle', cfg.intersectlinestyle, ... 'intersectlinewidth', cfg.intersectlinewidth, 'ncirc', cfg.ncirc, ... 'scalealpha', cfg.scalealpha, 'facecolor', cfg.facecolor, 'edgecolor', cfg.edgecolor,... - 'facealpha', cfg.facealpha, 'edgealpha', cfg.edgealpha); + 'facealpha', cfg.facealpha, 'edgealpha', cfg.edgealpha, 'marker', cfg.marker,... + 'vertexcolor', cfg.vertexcolor); if istrue(cfg.colorbar) if ~strcmp(cfg.slice, '2d') @@ -1685,8 +1696,9 @@ function cb_keyboard(h, eventdata) key = get(h, 'userdata'); else % determine the key that was pressed on the keyboard - key = parseKeyboardEvent(eventdata); + key = parsekeyboardevent(eventdata); end + % get focus back to figure if ~strcmp(get(h, 'type'), 'figure') set(h, 'enable', 'off'); @@ -1705,7 +1717,7 @@ function cb_keyboard(h, eventdata) key = ''; end -% the following code is largely shared with FT_VOLUMEREALIGN +% the following code is largely shared by FT_SOURCEPLOT, FT_VOLUMEREALIGN, FT_INTERACTIVEREALIGN, FT_MESHREALIGN, FT_ELECTRODEPLACEMENT switch key case {'' 'shift+shift' 'alt-alt' 'control+control' 'command-0'} % do nothing @@ -1721,7 +1733,7 @@ function cb_keyboard(h, eventdata) case 'q' setappdata(h, 'opt', opt); - cb_cleanup(h); + cb_quit(h); case {'i' 'j' 'k' 'm' 28 29 30 31 'leftarrow' 'rightarrow' 'uparrow' 'downarrow'} % TODO FIXME use leftarrow rightarrow uparrow downarrow % update the view to a new position @@ -1743,8 +1755,8 @@ function cb_keyboard(h, eventdata) setappdata(h, 'opt', opt); cb_redraw(h); + case {43 'add' 'shift+equal'} % + or numpad + % contrast scaling - case {43 'shift+equal'} % numpad + if isempty(opt.clim) opt.clim = [min(opt.ana(:)) max(opt.ana(:))]; end @@ -1755,7 +1767,8 @@ function cb_keyboard(h, eventdata) setappdata(h, 'opt', opt); cb_redraw(h); - case {45 'shift+hyphen'} % numpad - + case {45 'subtract' 'hyphen' 'shift+hyphen'} % - or numpad - + % contrast scaling if isempty(opt.clim) opt.clim = [min(opt.ana(:)) max(opt.ana(:))]; end @@ -1767,6 +1780,7 @@ function cb_keyboard(h, eventdata) cb_redraw(h); otherwise + % do nothing end % switch key @@ -1850,7 +1864,7 @@ function cb_getposition(h, eventdata) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function cb_cleanup(h, eventdata) +function cb_quit(h, eventdata) % opt = getappdata(h, 'opt'); % opt.quit = true; @@ -1868,30 +1882,3 @@ function cb_cleanup(h, eventdata) p = get(h, 'parent'); end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; - -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character; - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end diff --git a/external/fieldtrip/ft_sourcestatistics.m b/external/fieldtrip/ft_sourcestatistics.m index 7c1174f6..e25c5fda 100644 --- a/external/fieldtrip/ft_sourcestatistics.m +++ b/external/fieldtrip/ft_sourcestatistics.m @@ -76,20 +76,13 @@ ft_preamble loadvar varargin ft_preamble provenance varargin ft_preamble trackconfig +ft_preamble randomseed % the ft_abort variable is set to true or false in ft_preamble_init if ft_abort return end - -% check if the input cfg is valid for this function -cfg = ft_checkconfig(cfg, 'required', {'method', 'design'}); -cfg = ft_checkconfig(cfg, 'renamed', {'approach', 'method'}); - - -%%%%%%%%% - % check if the input cfg is valid for this function cfg = ft_checkconfig(cfg, 'required', {'method', 'design'}); cfg = ft_checkconfig(cfg, 'renamed', {'approach', 'method'}); @@ -119,7 +112,7 @@ end % ensure that the data in all inputs has the same channels, time-axis, etc. -tmpcfg = keepfields(cfg, {'frequency', 'avgoverfreq', 'latency', 'avgovertime', 'showcallinfo'}); +tmpcfg = keepfields(cfg, {'frequency', 'avgoverfreq', 'latency', 'avgovertime', 'avgoverpos', 'showcallinfo'}); [varargin{:}] = ft_selectdata(tmpcfg, varargin{:}); % restore the provenance information [cfg, varargin{:}] = rollback_provenance(cfg, varargin{:}); @@ -148,7 +141,7 @@ end end -if isfield(varargin{1}, 'inside'), +if isfield(varargin{1}, 'inside') cfg.inside = varargin{1}.inside; else cfg.inside = true(size(varargin{1}.pos,1),1); @@ -159,7 +152,6 @@ cfg.originside = cfg.inside; cfg.inside = repmat(cfg.inside, prod(cfg.dim)./numel(cfg.inside), 1); - if numel(cfg.dim)==1 cfg.dim(2) = 1; % add a trailing singleton dimensions end @@ -193,13 +185,13 @@ design = cfg.design; -% determine the function handle to the intermediate-level statistics function -if exist(['ft_statistics_' cfg.method], 'file') - statmethod = str2func(['ft_statistics_' cfg.method]); -else +% fetch function handle to the intermediate-level statistics function +statmethod = ft_getuserfun(cfg.method, 'statistics'); +if isempty(statmethod) ft_error('could not find the corresponding function for cfg.method="%s"\n', cfg.method); +else + fprintf('using "%s" for the statistical testing\n', func2str(statmethod)); end -fprintf('using "%s" for the statistical testing\n', func2str(statmethod)); % check that the design completely describes the data if size(dat,2) ~= size(cfg.design,2) @@ -236,7 +228,7 @@ tmp = nan+zeros(numel(cfg.inside),1); tmp(cfg.inside) = stat.(fn{i}); stat.(fn{i}) = tmp; - end + end if numel(stat.(fn{i}))==prod(datsiz) % reformat into the same dimensions as the input data stat.(fn{i}) = reshape(stat.(fn{i}), [datsiz 1]); @@ -250,10 +242,11 @@ stat = copyfields(varargin{1}, stat, {'freq', 'time', 'pos', 'dim', 'transform', 'tri', 'inside'}); % these were only present to inform the low-level functions -cfg = removefields(cfg, {'dim', 'dimord', 'tri', 'inside'}); +cfg = removefields(cfg, {'dimord', 'tri', 'dim', 'origdim', 'inside', 'originside'}); % do the general cleanup and bookkeeping at the end of the function ft_postamble debug +ft_postamble randomseed ft_postamble trackconfig ft_postamble previous varargin ft_postamble provenance stat diff --git a/external/fieldtrip/ft_statistics_analytic.m b/external/fieldtrip/ft_statistics_analytic.m index eac6e085..e3246fd9 100644 --- a/external/fieldtrip/ft_statistics_analytic.m +++ b/external/fieldtrip/ft_statistics_analytic.m @@ -58,6 +58,10 @@ % % $Id$ +% do a sanity check on the input data +assert(isnumeric(dat), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); +assert(isnumeric(design), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); + % check if the input cfg is valid for this function cfg = ft_checkconfig(cfg, 'renamedval', {'correctm', 'bonferoni', 'bonferroni'}); cfg = ft_checkconfig(cfg, 'renamedval', {'correctm', 'holms', 'holm'}); diff --git a/external/fieldtrip/ft_statistics_crossvalidate.m b/external/fieldtrip/ft_statistics_crossvalidate.m index a189f62f..7192b68e 100644 --- a/external/fieldtrip/ft_statistics_crossvalidate.m +++ b/external/fieldtrip/ft_statistics_crossvalidate.m @@ -42,6 +42,10 @@ % % $Id$ +% do a sanity check on the input data +assert(isnumeric(dat), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); +assert(isnumeric(design), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); + cfg.mva = ft_getopt(cfg, 'mva'); cfg.statistic = ft_getopt(cfg, 'statistic', {'accuracy', 'binomial'}); cfg.nfolds = ft_getopt(cfg, 'nfolds', 5); @@ -81,16 +85,37 @@ stat.model = cv.model; fn = fieldnames(stat.model{1}); -if any(strcmp(fn, 'weights')) - % create the 'encoding' matrix from the weights, as per Haufe 2014. - covdat = cov(dat'); - for i=1:length(stat.model) - W = stat.model{i}.weights; - M = dat'*W; - covM = cov(M); - stat.model{i}.weightsinv = covdat*W/covM; +if any(ismember(fn, {'weights', 'primal'})), + selfn = find(ismember(fn, {'weights', 'primal'})); + + % the mean subtraction is needed only once, but speeds up the covariance + % computation + dat = bsxfun(@minus, dat, nanmean(dat,2)); + dat_transp = dat.'; + for j=1:numel(selfn) + % create the 'encoding' matrix from the weights, as per Haufe 2014. + %covdat = cov(dat'); + for i=1:length(stat.model) + i + W = stat.model{i}.(fn{selfn}); + + sW = size(W); + sdat = size(dat); + if sW(2)==sdat(1) && sW(1)~=sdat(1) + W = transpose(W); + end + + M = dat'*W; + covM = cov(M); + WcovM = (W/covM)./(size(dat,2)-1); % with the correction term for the covariance computation + + %stat.model{i}.(sprintf('%sinv',fn{selfn})) = covdat*W/covM; + stat.model{i}.(sprintf('%sinv',fn{selfn})) = dat*(dat_transp*WcovM); + + end end end +fn = fieldnames(stat.model{1}); % update the fieldnames, because some might have been added fn = fieldnames(stat.model{1}); % may now also contain weightsinv for i=1:length(stat.model) diff --git a/external/fieldtrip/ft_statistics_montecarlo.m b/external/fieldtrip/ft_statistics_montecarlo.m index 52e1d45e..5c190c6a 100644 --- a/external/fieldtrip/ft_statistics_montecarlo.m +++ b/external/fieldtrip/ft_statistics_montecarlo.m @@ -46,7 +46,7 @@ % To include the channel dimension for clustering, you should specify % cfg.neighbours = neighbourhood structure, see FT_PREPARE_NEIGHBOURS % If you specify an empty neighbourhood structure, clustering will only be done -% in frequency and time (if available) and not over neighbouring channels. +% over frequency and/or time and not over neighbouring channels. % % The statistic that is computed for each sample in each random reshuffling % of the data is specified as @@ -100,8 +100,9 @@ % % $Id$ -% deal with the user specified random seed -ft_preamble randomseed +% do a sanity check on the input data +assert(isnumeric(dat), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); +assert(isnumeric(design), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); % check if the input cfg is valid for this function cfg = ft_checkconfig(cfg, 'renamed', {'factor', 'ivar'}); @@ -131,7 +132,7 @@ cfg.precondition = ft_getopt(cfg, 'precondition', []); % explicit check for option 'yes' in cfg.correctail. -if strcmp(cfg.correcttail,'yes') +if strcmp(cfg.correcttail, 'yes') ft_error('cfg.correcttail = ''yes'' is not allowed, use either ''prob'', ''alpha'' or ''no''') end @@ -276,7 +277,7 @@ if isstruct(statobs) % remember all details for later reference, continue to work with the statistic statfull = statobs; - statobs = getfield(statfull, 'stat'); + statobs = statobs.stat; else % remember the statistic for later reference, continue to work with the statistic statfull.stat = statobs; @@ -493,9 +494,5 @@ end end -% deal with the potential user specified randomseed -ft_postamble randomseed - warning(ws); % revert to original state - diff --git a/external/fieldtrip/ft_statistics_stats.m b/external/fieldtrip/ft_statistics_stats.m index fa3926ca..f5b50528 100644 --- a/external/fieldtrip/ft_statistics_stats.m +++ b/external/fieldtrip/ft_statistics_stats.m @@ -29,6 +29,9 @@ % 'kruskalwallis' % 'signtest' % 'signrank' +% 'pearson' +% 'kendall' +% 'spearman' % % See also TTEST, TTEST2, KRUSKALWALLIS, SIGNTEST, SIGNRANK @@ -56,6 +59,10 @@ % % $Id$ +% do a sanity check on the input data +assert(isnumeric(dat), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); +assert(isnumeric(design), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead'); + % test for the presence of the statistics toolbox ft_hastoolbox('stats', 1); @@ -63,284 +70,332 @@ cfg.feedback = ft_getopt(cfg, 'feedback', 'textbar'); switch cfg.statistic - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case {'ttest', 'ttest_samples_vs_const'} - - % set the defaults - cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); - cfg.tail = ft_getopt(cfg, 'tail', 0); - cfg.constantvalue = ft_getopt(cfg, 'constantvalue', 0); - - if ~any(size(design)==1) - ft_error('design matrix should only contain one factor (i.e. one row)'); - end - Ncond = length(unique(design)); - if Ncond>1 - ft_error('method "%s" is only supported for one condition', cfg.statistic); - end - Nobs = size(dat, 1); - Nrepl = size(dat, 2); % over all conditions - - h = zeros(Nobs, 1); - p = zeros(Nobs, 1); - s = zeros(Nobs, 1); - ci = zeros(Nobs, 2); - fprintf('number of observations %d\n', Nobs); - fprintf('number of replications %d\n', Nrepl); - - ft_progress('init', cfg.feedback); - for chan = 1:Nobs - ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); - [h(chan), p(chan), ci(chan, :), stats] = ttest_wrapper(dat(chan, :), cfg.constantvalue, cfg.alpha, cfg.tail); - s(chan) = stats.tstat; - end - ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case {'ttest2', 'ttest_2samples_by_timepoint'} - - % set the defaults - cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); - cfg.tail = ft_getopt(cfg, 'tail', 0); - - if size(design,1)~=1 - ft_error('design matrix should only contain one factor (i.e. one row)'); - end - Ncond = length(unique(design)); - if Ncond~=2 - ft_error('%s method is only supported for two conditions', cfg.statistic); - end - Nobs = size(dat, 1); - selA = find(design==design(1)); - selB = find(design~=design(1)); - Nrepl = [length(selA), length(selB)]; - - h = zeros(Nobs, 1); - p = zeros(Nobs, 1); - s = zeros(Nobs, 1); - ci = zeros(Nobs, 2); - fprintf('number of observations %d\n', Nobs); - fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); - - ft_progress('init', cfg.feedback); - for chan = 1:Nobs - ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); - [h(chan), p(chan), ci(chan, :), stats] = ttest2_wrapper(dat(chan, selA), dat(chan, selB), cfg.alpha, cfg.tail); - s(chan) = stats.tstat; - end - ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case {'paired-ttest'} - - % set the defaults - cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); - cfg.tail = ft_getopt(cfg, 'tail', 0); - - if ~any(size(design)==1) - ft_error('design matrix should only contain one factor (i.e. one row)'); - end - Ncond = length(unique(design)); - if Ncond~=2 - ft_error('method "%s" is only supported for two conditions', cfg.statistic); - end - Nobs = size(dat, 1); - selA = find(design==design(1)); - selB = find(design~=design(1)); - Nrepl = [length(selA), length(selB)]; - if Nrepl(1)~=Nrepl(2) - ft_error('number of replications per condition should be the same'); - end - - h = zeros(Nobs, 1); - p = zeros(Nobs, 1); - s = zeros(Nobs, 1); - ci = zeros(Nobs, 2); - df = zeros(Nobs, 1); - md = zeros(Nobs, 1); - sd = zeros(Nobs, 1); - cohens_d = zeros(Nobs, 1); - fprintf('number of observations %d\n', Nobs); - fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); - - ft_progress('init', cfg.feedback); - for chan = 1:Nobs - ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); - [h(chan), p(chan), ci(chan, :), stats] = ttest_wrapper(dat(chan, selA)-dat(chan, selB), 0, cfg.alpha, cfg.tail); - s(chan) = stats.tstat; - df(chan) = stats.df; - md(chan) = mean(dat(chan, selA)-dat(chan, selB)); - sd(chan) = stats.sd; - cohens_d(chan) = md(chan)/sd(chan); - end - ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case {'anova1'} - - if ~any(size(design)==1) - ft_error('design matrix should only contain one factor (i.e. one row)'); - end - Ncond = length(unique(design)); - Nobs = size(dat, 1); - Nrepl = size(dat, 2); % over all conditions - - h = zeros(Nobs, 1); - p = zeros(Nobs, 1); - fprintf('number of observations %d\n', Nobs); - fprintf('number of replications %d\n', Nrepl); - fprintf('number of levels %d\n', Ncond); - - ft_progress('init', cfg.feedback); - for chan = 1:Nobs - ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); - p(chan) = anova1(dat(chan, :), design(:), 'off'); - end - ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case {'kruskalwallis'} - - if ~any(size(design)==1) - ft_error('design matrix should only contain one factor (i.e. one row)'); - end - Ncond = length(unique(design)); - Nobs = size(dat, 1); - Nrepl = size(dat, 2); % over all conditions - - h = zeros(Nobs, 1); - p = zeros(Nobs, 1); - fprintf('number of observations %d\n', Nobs); - fprintf('number of replications %d\n', Nrepl); - fprintf('number of levels %d\n', Ncond); - - ft_progress('init', cfg.feedback); - for chan = 1:Nobs - ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); - p(chan) = kruskalwallis(dat(chan, :), design(:), 'off'); - end - ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% case {'anovan'} -% -% Nfact = size(design,1); -% Nobs = size(dat, 1); -% Nrepl = size(dat, 2); % over all conditions -% -% h = zeros(Nobs, 1); -% p = zeros(Nobs, 1); -% ci = zeros(Nobs, 2); -% fprintf('number of observations %d\n', Nobs); -% fprintf('number of replications %d\n', Nrepl); -% fprintf('number of factors %d\n', Nfact); -% -% % reformat the design matrix into the grouping variable cell-array -% for i=1:Nfact -% group{i} = design(i,:); -% end -% -% ft_progress('init', cfg.feedback); -% for chan = 1:Nobs -% ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); -% % FIXME, the probability is returned for each factor separately -% p = anovan(dat(chan, :), group, 'display', 'off'); -% end -% ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case 'ttest_window_avg_vs_const' - % this used to be a feature of the timelockanalysis as it was - % originally implemented by Jens Schwartzbach, but it has been - % superseded by the use of ft_selectdata for data selection - ft_error('%s is not supported any more, use cfg.avgovertime=''yes'' instead', cfg.statistic); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case {'signtest'} - % set the defaults - cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); - cfg.tail = ft_getopt(cfg, 'tail', 0); - - switch cfg.tail - case 0 - cfg.tail = 'both'; - case -1 - cfg.tail = 'left'; - case 1 - cfg.tail = 'right'; - end - if size(design,1)~=1 - ft_error('design matrix should only contain one factor (i.e. one row)'); - end - Ncond = length(unique(design)); - if Ncond~=2 - ft_error('method "%s" is only supported for two conditions', cfg.statistic); - end - Nobs = size(dat, 1); - selA = find(design==design(1)); - selB = find(design~=design(1)); - Nrepl = [length(selA), length(selB)]; - - h = zeros(Nobs, 1); - p = zeros(Nobs, 1); - s = zeros(Nobs, 1); - fprintf('number of observations %d\n', Nobs); - fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); - - ft_progress('init', cfg.feedback); - for chan = 1:Nobs - ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); - [p(chan), h(chan), stats] = signtest(dat(chan, selA), dat(chan, selB),'alpha', cfg.alpha,'tail', cfg.tail); - s(chan) = stats.sign; - end - ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -case {'signrank'} - % set the defaults - cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); - cfg.tail = ft_getopt(cfg, 'tail', 0); - - switch cfg.tail - case 0 - cfg.tail = 'both'; - case -1 - cfg.tail = 'left'; - case 1 - cfg.tail = 'right'; - end - - if size(design,1)~=1 - ft_error('design matrix should only contain one factor (i.e. one row)'); - end - Ncond = length(unique(design)); - if Ncond~=2 - ft_error('method ''%s'' is only supported for two conditions', cfg.statistic); - end - Nobs = size(dat, 1); - selA = find(design==design(1)); - selB = find(design~=design(1)); - Nrepl = [length(selA), length(selB)]; - - h = zeros(Nobs, 1); - p = zeros(Nobs, 1); - s = zeros(Nobs, 1); - fprintf('number of observations %d\n', Nobs); - fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); - - ft_progress('init', cfg.feedback); - for chan = 1:Nobs - ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); - [p(chan), h(chan), stats] = signrank(dat(chan, selA), dat(chan, selB),'alpha', cfg.alpha,'tail', cfg.tail); - s(chan) = stats.signedrank; - end - ft_progress('close'); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -otherwise - ft_error('method ''%s'' is not implemented', cfg.statistic); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'ttest', 'ttest_samples_vs_const'} + + % set the defaults + cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); + cfg.tail = ft_getopt(cfg, 'tail', 0); + cfg.constantvalue = ft_getopt(cfg, 'constantvalue', 0); + + if ~any(size(design)==1) + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + Ncond = length(unique(design)); + if Ncond>1 + ft_error('method "%s" is only supported for one condition', cfg.statistic); + end + Nobs = size(dat, 1); + Nrepl = size(dat, 2); % over all conditions + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + s = zeros(Nobs, 1); + ci = zeros(Nobs, 2); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d\n', Nrepl); + + ft_progress('init', cfg.feedback); + for chan = 1:Nobs + ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + [h(chan), p(chan), ci(chan, :), stats] = ttest_wrapper(dat(chan, :), cfg.constantvalue, cfg.alpha, cfg.tail); + s(chan) = stats.tstat; + end + ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'ttest2', 'ttest_2samples_by_timepoint'} + + % set the defaults + cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); + cfg.tail = ft_getopt(cfg, 'tail', 0); + + if size(design,1)~=1 + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + Ncond = length(unique(design)); + if Ncond~=2 + ft_error('%s method is only supported for two conditions', cfg.statistic); + end + Nobs = size(dat, 1); + selA = find(design==design(1)); + selB = find(design~=design(1)); + Nrepl = [length(selA), length(selB)]; + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + s = zeros(Nobs, 1); + ci = zeros(Nobs, 2); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); + + ft_progress('init', cfg.feedback); + for chan = 1:Nobs + ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + [h(chan), p(chan), ci(chan, :), stats] = ttest2_wrapper(dat(chan, selA), dat(chan, selB), cfg.alpha, cfg.tail); + s(chan) = stats.tstat; + end + ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'paired-ttest'} + + % set the defaults + cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); + cfg.tail = ft_getopt(cfg, 'tail', 0); + + if ~any(size(design)==1) + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + Ncond = length(unique(design)); + if Ncond~=2 + ft_error('method "%s" is only supported for two conditions', cfg.statistic); + end + Nobs = size(dat, 1); + selA = find(design==design(1)); + selB = find(design~=design(1)); + Nrepl = [length(selA), length(selB)]; + if Nrepl(1)~=Nrepl(2) + ft_error('number of replications per condition should be the same'); + end + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + s = zeros(Nobs, 1); + ci = zeros(Nobs, 2); + df = zeros(Nobs, 1); + md = zeros(Nobs, 1); + sd = zeros(Nobs, 1); + cohens_d = zeros(Nobs, 1); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); + + ft_progress('init', cfg.feedback); + for chan = 1:Nobs + ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + [h(chan), p(chan), ci(chan, :), stats] = ttest_wrapper(dat(chan, selA)-dat(chan, selB), 0, cfg.alpha, cfg.tail); + s(chan) = stats.tstat; + df(chan) = stats.df; + md(chan) = mean(dat(chan, selA)-dat(chan, selB)); + sd(chan) = stats.sd; + cohens_d(chan) = md(chan)/sd(chan); + end + ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'anova1'} + + if ~any(size(design)==1) + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + Ncond = length(unique(design)); + Nobs = size(dat, 1); + Nrepl = size(dat, 2); % over all conditions + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d\n', Nrepl); + fprintf('number of levels %d\n', Ncond); + + ft_progress('init', cfg.feedback); + for chan = 1:Nobs + ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + p(chan) = anova1(dat(chan, :), design(:), 'off'); + end + ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'kruskalwallis'} + + if ~any(size(design)==1) + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + Ncond = length(unique(design)); + Nobs = size(dat, 1); + Nrepl = size(dat, 2); % over all conditions + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d\n', Nrepl); + fprintf('number of levels %d\n', Ncond); + + ft_progress('init', cfg.feedback); + for chan = 1:Nobs + ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + p(chan) = kruskalwallis(dat(chan, :), design(:), 'off'); + end + ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % case {'anovan'} + % + % Nfact = size(design,1); + % Nobs = size(dat, 1); + % Nrepl = size(dat, 2); % over all conditions + % + % h = zeros(Nobs, 1); + % p = zeros(Nobs, 1); + % ci = zeros(Nobs, 2); + % fprintf('number of observations %d\n', Nobs); + % fprintf('number of replications %d\n', Nrepl); + % fprintf('number of factors %d\n', Nfact); + % + % % reformat the design matrix into the grouping variable cell-array + % for i=1:Nfact + % group{i} = design(i,:); + % end + % + % ft_progress('init', cfg.feedback); + % for chan = 1:Nobs + % ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + % % FIXME, the probability is returned for each factor separately + % p = anovan(dat(chan, :), group, 'display', 'off'); + % end + % ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case 'ttest_window_avg_vs_const' + % this used to be a feature of the timelockanalysis as it was + % originally implemented by Jens Schwartzbach, but it has been + % superseded by the use of ft_selectdata for data selection + ft_error('%s is not supported any more, use cfg.avgovertime=''yes'' instead', cfg.statistic); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'signtest'} + % set the defaults + cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); + cfg.tail = ft_getopt(cfg, 'tail', 0); + + switch cfg.tail + case 0 + cfg.tail = 'both'; + case -1 + cfg.tail = 'left'; + case 1 + cfg.tail = 'right'; + end + + if size(design,1)~=1 + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + Ncond = length(unique(design)); + if Ncond~=2 + ft_error('method "%s" is only supported for two conditions', cfg.statistic); + end + Nobs = size(dat, 1); + selA = find(design==design(1)); + selB = find(design~=design(1)); + Nrepl = [length(selA), length(selB)]; + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + s = zeros(Nobs, 1); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); + + ft_progress('init', cfg.feedback); + for chan = 1:Nobs + ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + [p(chan), h(chan), stats] = signtest(dat(chan, selA), dat(chan, selB),'alpha', cfg.alpha,'tail', cfg.tail); + s(chan) = stats.sign; + end + ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'signrank'} + % set the defaults + cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); + cfg.tail = ft_getopt(cfg, 'tail', 0); + + switch cfg.tail + case 0 + cfg.tail = 'both'; + case -1 + cfg.tail = 'left'; + case 1 + cfg.tail = 'right'; + end + + if size(design,1)~=1 + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + Ncond = length(unique(design)); + if Ncond~=2 + ft_error('method ''%s'' is only supported for two conditions', cfg.statistic); + end + Nobs = size(dat, 1); + selA = find(design==design(1)); + selB = find(design~=design(1)); + Nrepl = [length(selA), length(selB)]; + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + s = zeros(Nobs, 1); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d and %d\n', Nrepl(1), Nrepl(2)); + + ft_progress('init', cfg.feedback); + for chan = 1:Nobs + ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + [p(chan), h(chan), stats] = signrank(dat(chan, selA), dat(chan, selB),'alpha', cfg.alpha,'tail', cfg.tail); + s(chan) = stats.signedrank; + end + ft_progress('close'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case {'pearson', 'kendall', 'spearman'} + + % set the defaults + cfg.alpha = ft_getopt(cfg, 'alpha', 0.05); + cfg.tail = ft_getopt(cfg, 'tail', 0); + + switch cfg.tail + case 0 + cfg.tail = 'both'; + case -1 + cfg.tail = 'left'; + case 1 + cfg.tail = 'right'; + end + + % take the independent variable + design = design(cfg.ivar,:); + + if size(design,1)~=1 + ft_error('design matrix should only contain one factor (i.e. one row)'); + end + + if length(unique(design))<2 + ft_error('method ''%s'' is only supported for varying values', cfg.statistic); + end + + [Nobs, Nrepl] = size(dat); + + h = zeros(Nobs, 1); + p = zeros(Nobs, 1); + s = zeros(Nobs, 1); + fprintf('number of observations %d\n', Nobs); + fprintf('number of replications %d\n', Nrepl); + + % ft_progress('init', cfg.feedback); + % for chan = 1:Nobs + % ft_progress(chan/Nobs, 'Processing observation %d/%d\n', chan, Nobs); + % [s(chan), p(chan)] = corr(dat(chan, :)', design','type', cfg.statistic, 'tail', cfg.tail); + % h(chan) = p(chan) <= cfg.alpha; + % end + % ft_progress('close'); + + % it can be computed in one go + [s, p] = corr(dat', design','type', cfg.statistic, 'tail', cfg.tail); + h = p<=cfg.alpha; + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + otherwise + ft_error('method ''%s'' is not implemented', cfg.statistic); end % assign the output variable @@ -359,18 +414,18 @@ % - old Matlab, with syntax: ttest(x,y,alpha,tail,dim) % - new Matlab and GNU Octave, with syntax: ttest(x,y,'alpha',alpha,...) function [h,p,ci,stats]=ttest_wrapper(x,y,alpha,tail) - [h,p,ci,stats]=general_ttestX_wrapper(@ttest,x,y,alpha,tail); +[h,p,ci,stats]=general_ttestX_wrapper(@ttest,x,y,alpha,tail); function [h,p,ci,stats]=ttest2_wrapper(x,y,alpha,tail) - [h,p,ci,stats]=general_ttestX_wrapper(@ttest2,x,y,alpha,tail); +[h,p,ci,stats]=general_ttestX_wrapper(@ttest2,x,y,alpha,tail); function [h,p,ci,stats]=general_ttestX_wrapper(ttest_func,x,y,alpha,tail) - if nargin(ttest_func)>0 - % old Matlab - [h,p,ci,stats]=ttest_func(x,y,alpha,tail); - - else - % GNU Octave and new Matlab - [h,p,ci,stats]=ttest_func(x,y,'alpha',alpha,'tail',tail); - end +if nargin(ttest_func)>0 + % old Matlab + [h,p,ci,stats]=ttest_func(x,y,alpha,tail); + +else + % GNU Octave and new Matlab + [h,p,ci,stats]=ttest_func(x,y,'alpha',alpha,'tail',tail); +end diff --git a/external/fieldtrip/ft_steadystatesimulation.m b/external/fieldtrip/ft_steadystatesimulation.m index 4c52b3d3..c6251976 100644 --- a/external/fieldtrip/ft_steadystatesimulation.m +++ b/external/fieldtrip/ft_steadystatesimulation.m @@ -1,4 +1,4 @@ -function data = ft_steadystatesimulation(cfg) +function [data] = ft_steadystatesimulation(cfg) % FT_STEADYSTATESIMULATION creates a simulated EEG/MEG dataset. This function % allows to simulate the effect of several independent stimulus trains. These can diff --git a/external/fieldtrip/ft_stratify.m b/external/fieldtrip/ft_stratify.m index 74b9c294..ee2f5075 100644 --- a/external/fieldtrip/ft_stratify.m +++ b/external/fieldtrip/ft_stratify.m @@ -79,14 +79,14 @@ % stratification % set the defaults -cfg.method = ft_getopt('method', 'histogram'); -cfg.equalbinavg = ft_getopt('equalbinavg', 'yes'); -cfg.numbin = ft_getopt('numbin', 10); -cfg.numiter = ft_getopt('numiter', 2000); -cfg.binedges = ft_getopt('binedges', []); -cfg.pairtrials = ft_getopt('pairtrials', 'spikesort'); -cfg.channel = ft_getopt('channel', 'all'); -cfg.linkage = ft_getopt('linkage', 'complete'); % 'single', 'complete', 'average', 'weighted', 'centroid', 'median', 'ward' +cfg.method = ft_getopt(cfg, 'method', 'histogram'); +cfg.equalbinavg = ft_getopt(cfg, 'equalbinavg', 'yes'); +cfg.numbin = ft_getopt(cfg, 'numbin', 10); +cfg.numiter = ft_getopt(cfg, 'numiter', 2000); +cfg.binedges = ft_getopt(cfg, 'binedges', []); +cfg.pairtrials = ft_getopt(cfg, 'pairtrials', 'spikesort'); +cfg.channel = ft_getopt(cfg, 'channel', 'all'); +cfg.linkage = ft_getopt(cfg, 'linkage', 'complete'); % 'single', 'complete', 'average', 'weighted', 'centroid', 'median', 'ward' % the input data is a cell-array containing matrices for each condition input = varargin; diff --git a/external/fieldtrip/ft_timelockanalysis.m b/external/fieldtrip/ft_timelockanalysis.m index 58a7968c..f8047b56 100644 --- a/external/fieldtrip/ft_timelockanalysis.m +++ b/external/fieldtrip/ft_timelockanalysis.m @@ -267,6 +267,11 @@ endsampl = nearest(data.time{i}, latency(2)); numsamples(i) = endsampl-begsampl+1; dat = data.trial{i}(:, begsampl:endsampl); + if isfield(data, 'sampleinfo') + tmpsampl = data.sampleinfo(i,1):data.sampleinfo(i,2); + data.sampleinfo(i,:) = tmpsampl([begsampl endsampl]); + end + if (latency(1)1 - [stat, cfg] = statmethod(cfg, dat, design); - cfg = rollback_provenance(cfg); % ensure that changes to the cfg are passed back to the right level - else - [stat] = statmethod(cfg, dat, design); - end +if num>1 + [stat, cfg] = statmethod(cfg, dat, design); + cfg = rollback_provenance(cfg); % ensure that changes to the cfg are passed back to the right level else - if num>1 - [stat, cfg] = statmethod(cfg, dat, design); - cfg = rollback_provenance(cfg); % ensure that changes to the cfg are passed back to the right level - else - [stat] = statmethod(cfg, dat, design); - end + [stat] = statmethod(cfg, dat, design); end if ~isstruct(stat) @@ -214,13 +204,14 @@ stat.dimord = cfg.dimord; % copy the descripive fields into the output -stat = copyfields(varargin{1}, stat, {'time', 'label'}); +stat = copyfields(varargin{1}, stat, {'time', 'label', 'elec', 'grad', 'opto'}); % these were only present to inform the low-level functions cfg = removefields(cfg, {'dim', 'dimord'}); % do the general cleanup and bookkeeping at the end of the function ft_postamble debug +ft_postamble randomseed ft_postamble trackconfig ft_postamble previous varargin ft_postamble provenance stat diff --git a/external/fieldtrip/ft_topoplotIC.m b/external/fieldtrip/ft_topoplotIC.m index 3426c1de..afe1455e 100644 --- a/external/fieldtrip/ft_topoplotIC.m +++ b/external/fieldtrip/ft_topoplotIC.m @@ -142,6 +142,7 @@ clear tmpcomp; % don't show the callinfo for each separate component +tmpshowcallinfo = cfg.showcallinfo; cfg.showcallinfo = 'no'; % create temporary variable to prevent overwriting the selected components @@ -194,7 +195,7 @@ cfg = removefields(cfg, 'funcname'); % show the callinfo for all components together -cfg.showcallinfo = 'yes'; +cfg.showcallinfo = tmpshowcallinfo; % do the general cleanup and bookkeeping at the end of the function ft_postamble debug diff --git a/external/fieldtrip/ft_volumelookup.m b/external/fieldtrip/ft_volumelookup.m index dbbc5412..56d82341 100644 --- a/external/fieldtrip/ft_volumelookup.m +++ b/external/fieldtrip/ft_volumelookup.m @@ -1,31 +1,31 @@ function [output] = ft_volumelookup(cfg, volume) -% FT_VOLUMELOOKUP can be used in to combine an anatomical or functional atlas with -% the source reconstruction results. You can use it for forward and reverse lookup. +% FT_VOLUMELOOKUP can be used in to combine an anatomical or functional +% atlas with the source reconstruction results. You can use it for forward +% and reverse lookup. % -% Given the ROI as anatomical or functional label, it looks up the locations and -% creates a mask (as a binary volume) based on the label. Given the ROI as point in -% the brain, it creates a sphere or box around that point. In these two case the -% function is to be used as: +% Given the region of interest (ROI) as anatomical or functional label, it +% looks up the locations and creates a mask (as a binary volume) based on +% the label. Given the ROI as point in the brain, it creates a sphere or +% box around that point. In these two case the function is to be used as: % mask = ft_volumelookup(cfg, volume) % -% Given a binary volume that indicates a region of interest or a point of -% interest, it looks up the corresponding anatomical or functional labels -% from the atlas. In this case the function is to be used as: +% Given a binary volume that indicates a ROI or a point of interest (POI), +% it looks up the corresponding anatomical or functional labels from the +% atlas. In this case the function is to be used as: % labels = ft_volumelookup(cfg, volume) % % In both cases the input volume can be: -% mri is the output of FT_READ_MRI -% source is the output of FT_SOURCEANALYSIS +% mri is the output of FT_READ_MRI source is the output of FT_SOURCEANALYSIS % stat is the output of FT_SOURCESTATISTICS % % The configuration options for a mask according to an atlas: % cfg.inputcoord = 'mni' or 'tal', coordinate system of the mri/source/stat % cfg.atlas = string, filename of atlas to use, see FT_READ_ATLAS -% cfg.roi = string or cell-array of strings, region(s) of interest from anatomical atlas +% cfg.roi = string or cell-array of strings, ROI from anatomical atlas % -% The configuration options for a spherical/box mask around a point of interest: -% cfg.roi = Nx3 vector, coordinates of the points of interest +% The configuration options for a spherical/box mask around a POI: +% cfg.roi = Nx3 vector, coordinates of the POI % cfg.sphere = radius of each sphere in cm/mm dep on unit of input % cfg.box = Nx3 vector, size of each box in cm/mm dep on unit of input % cfg.round2nearestvoxel = 'yes' or 'no' (default = 'no'), voxel closest to point of interest is calculated @@ -35,17 +35,20 @@ % cfg.inputcoord = 'mni' or 'tal', coordinate system of the mri/source/stat % cfg.atlas = string, filename of atlas to use, see FT_READ_ATLAS % cfg.maskparameter = string, field in volume to be looked up, data in field should be logical -% cfg.maxqueryrange = number, should be odd (default = 1) +% cfg.minqueryrange = number, should be odd and <= to maxqueryrange (default = 1) +% cfg.maxqueryrange = number, should be odd and >= to minqueryrange (default = 1) % -% The configuration options for labels around a point of interest: -% cfg.output = 'label' -% cfg.roi = Nx3 vector, coordinates of the points of interest +% The configuration options for labels around POI: +% cfg.output = 'single' always outputs one label; if several POI are provided, they are considered together as describing a ROI (default) +% 'multiple' outputs one label per POI (e.g., choose to get labels for different electrodes) +% cfg.roi = Nx3 vector, coordinates of the POI % cfg.inputcoord = 'mni' or 'tal', coordinate system of the mri/source/stat % cfg.atlas = string, filename of atlas to use, see FT_READ_ATLAS -% cfg.maxqueryrange = number, should be 1, 3, 5 (default = 1) -% cfg.querymethod = 'sphere' searches voxels around the roi in a sphere (default) -% = 'cube' searches voxels around the roi in a sphere -% cfg.round2nearestvoxel = 'yes' or 'no', voxel closest to point of interest is calculated (default = 'yes') +% cfg.minqueryrange = number, should be odd and <= to maxqueryrange (default = 1) +% cfg.maxqueryrange = number, should be odd and >= to minqueryrange (default = 1) +% cfg.querymethod = 'sphere' searches voxels around the ROI in a sphere (default) +% = 'cube' searches voxels around the ROI in a cube +% cfg.round2nearestvoxel = 'yes' or 'no', voxel closest to POI is calculated (default = 'yes') % % The label output has a field "names", a field "count" and a field "usedqueryrange". % To get a list of areas of the given mask you can do for instance: @@ -108,13 +111,15 @@ % the handling of the default cfg options is done further down % the checking of the input data is done further down +cfg.minqueryrange = ft_getopt(cfg,'minqueryrange', 1); cfg.maxqueryrange = ft_getopt(cfg,'maxqueryrange', 1); cfg.output = ft_getopt(cfg,'output', []); % in future, cfg.output could be extended to support both 'label' and 'mask' +cfg = ft_checkconfig(cfg, 'renamedval', {'output', 'label', 'single'}); roi2mask = 0; mask2label = 0; roi2label = 0; -if isfield(cfg, 'roi') && strcmp(cfg.output, 'label') +if isfield(cfg, 'roi') && ~isempty(cfg.output) roi2label = 1; elseif isfield(cfg, 'roi') roi2mask = 1; @@ -150,8 +155,12 @@ volume = ft_checkdata(volume, 'datatype', 'source'); ft_checkconfig(cfg, 'required', {'atlas', 'inputcoord'}); - if isempty(intersect(cfg.maxqueryrange, 1:2:cfg.maxqueryrange)) - ft_error('incorrect query range, should be an odd number'); + if cfg.minqueryrange > cfg.maxqueryrange + ft_error('maxqueryrange should be superior or equal to minqueryrange'); + end + + if rem(cfg.minqueryrange, 2) == 0 || rem(cfg.maxqueryrange, 2) == 0 + ft_error('incorrect query range, should be odd numbers'); end cfg.round2nearestvoxel = ft_getopt(cfg, 'round2nearestvoxel', 'yes'); @@ -347,45 +356,57 @@ if istrue(cfg.round2nearestvoxel) % determine location of each anatomical voxel in head coordinates xyz = [volume.pos ones(size(volume.pos,1),1)]'; % note that this is 4xN - for i=1:size(cfg.roi,1) + nSel = size(cfg.roi, 1); + for i=1:nSel cfg.roi(i,:) = poi2voi(cfg.roi(i,:), xyz); end end % round2nearestvoxel - sel = find(ismember(volume.pos, cfg.roi, 'rows')==1); + sel = zeros(size(cfg.roi,1), 1); + for i = 1:size(cfg.roi,1) + sel(i) = find(volume.pos(:, 1) == cfg.roi(i, 1) & volume.pos(:, 2) == cfg.roi(i, 2) & volume.pos(:, 3) == cfg.roi(i, 3)); + end + end + if strcmp(cfg.output, 'multiple') + labels = repmat(labels, length(sel), 1); end for iVox = 1:length(sel) - label = {}; - for qr = 1:2:cfg.maxqueryrange + label = {}; + for qr = cfg.minqueryrange:2:cfg.maxqueryrange + if isempty(label) + label = atlas_lookup(atlas, volume.pos(sel(iVox), :), 'inputcoord', cfg.inputcoord, 'queryrange', qr, 'method', cfg.querymethod); + usedQR = qr; + end + end + if isempty(label) - label = atlas_lookup(atlas, [volume.pos(sel(iVox),1) volume.pos(sel(iVox),2) volume.pos(sel(iVox),3)], 'inputcoord', cfg.inputcoord, 'queryrange', qr, 'method', cfg.querymethod); - usedQR = qr; + label = {'no_label_found'}; + elseif length(label) == 1 + label = {label}; end - end - - if isempty(label) - label = {'no_label_found'}; - elseif length(label) == 1 - label = {label}; - end - - ind_lab = []; - for iLab = 1:length(label) - ind_lab = find(strcmp(label{iLab}, labels.name)); - labels.count(ind_lab) = labels.count(ind_lab)+1; % labels.count should give the number of times a label was found within a query range - end - -% labels.count(ind_lab) = labels.count(ind_lab) + (1/length(ind_lab)); -% ^this gives each label a weight depending on the number of -% labels found within the query range. Using this method, all labels -% that were found will have the same number listed for labels.count, -% which defeats the point of the labels.count field as I understand it - for iFoundLab = 1:length(ind_lab) - if isempty(labels.usedqueryrange{ind_lab(iFoundLab)}) - labels.usedqueryrange{ind_lab(iFoundLab)} = usedQR; + + if strcmp(cfg.output, 'multiple') + iLabOut = iVox; else - labels.usedqueryrange{ind_lab(iFoundLab)} = [labels.usedqueryrange{ind_lab(iFoundLab)} usedQR]; + iLabOut = 1; + end + ind_lab = []; + for iLab = 1:length(label) + ind_lab = find(strcmp(label{iLab}, labels(iLabOut).name)); + labels(iLabOut).count(ind_lab) = labels(iLabOut).count(ind_lab)+1; % labels.count should give the number of times a label was found within a query range + end + + % labels.count(ind_lab) = labels.count(ind_lab) + (1/length(ind_lab)); + % ^this gives each label a weight depending on the number of + % labels found within the query range. Using this method, all labels + % that were found will have the same number listed for labels.count, + % which defeats the point of the labels.count field as I understand it + for iFoundLab = 1:length(ind_lab) + if isempty(labels(iLabOut).usedqueryrange{ind_lab(iFoundLab)}) + labels(iLabOut).usedqueryrange{ind_lab(iFoundLab)} = usedQR; + else + labels(iLabOut).usedqueryrange{ind_lab(iFoundLab)} = [labels(iLabOut).usedqueryrange{ind_lab(iFoundLab)} usedQR]; + end end - end end %iVox output = labels; diff --git a/external/fieldtrip/ft_volumenormalise.m b/external/fieldtrip/ft_volumenormalise.m index 2ca4a728..06a26630 100644 --- a/external/fieldtrip/ft_volumenormalise.m +++ b/external/fieldtrip/ft_volumenormalise.m @@ -99,7 +99,7 @@ % set the defaults cfg.spmversion = ft_getopt(cfg, 'spmversion', 'spm8'); -cfg.spmmethod = ft_getopt(cfg, 'spmmethod', 'old'); % in case of spm12, use the old-style +cfg.spmmethod = ft_getopt(cfg, 'spmmethod', 'old'); % in case of spm12, use the old-style normalisation by default cfg.parameter = ft_getopt(cfg, 'parameter', 'all'); cfg.downsample = ft_getopt(cfg, 'downsample', 1); cfg.write = ft_getopt(cfg, 'write', 'no'); @@ -136,7 +136,7 @@ spmpath = spm('dir'); if strcmpi(cfg.spmversion, 'spm2'), cfg.template = fullfile(spmpath, filesep, 'templates', filesep, 'T1.mnc'); end if strcmpi(cfg.spmversion, 'spm8'), cfg.template = fullfile(spmpath, filesep, 'templates', filesep, 'T1.nii'); end - if strcmpi(cfg.spmversion, 'spm12'), cfg.template = fullfile(spmpath, filesep, 'toolbox', filesep, 'OldNorm', filesep, 'T1.nii'); end + if strcmpi(cfg.spmversion, 'spm12'), cfg.template = fullfile(spmpath, filesep, 'toolbox', filesep, 'OldNorm', filesep, 'T1.nii'); end end end @@ -182,8 +182,9 @@ cfg.parameter = cfg.parameter(fliplr(indx)); end -if cfg.downsample~=1 - % optionally downsample the anatomical and/or functional volumes +if cfg.downsample~=1 && ~(strcmp(cfg.spmversion, 'spm12')&&strcmp(cfg.spmmethod,'new')) + % optionally downsample the anatomical and/or functional volumes, this is + % not needed when using spm12 in combination with spmmethod='new' tmpcfg = keepfields(cfg, {'downsample', 'parameter', 'smooth', 'showcallinfo'}); mri = ft_volumedownsample(tmpcfg, mri); % restore the provenance information @@ -192,19 +193,28 @@ ws = ft_warning('off'); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% here the normalisation starts -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% use nii if possible +if strcmpi(cfg.spmversion, 'spm2') + ext = '.img'; +else + ext = '.nii'; +end % create an spm-compatible header for the anatomical volume data -VF = ft_write_mri([cfg.intermediatename '_anatomy.img'], mri.anatomy, 'transform', mri.transform, 'spmversion', cfg.spmversion); - +writeoptions = {'transform',mri.transform,'spmversion',cfg.spmversion}; +switch ext + case '.img' + % nothing to be done + case '.nii' + writeoptions(end+(1:2)) = {'dataformat', 'nifti_spm'}; +end +VF = ft_write_mri([cfg.intermediatename '_anatomy' ext], mri.anatomy, writeoptions{:}); + % create an spm-compatible file for each of the functional volumes -for parlop=2:length(cfg.parameter) % skip the anatomy - tmp = cfg.parameter{parlop}; - data = reshape(getsubfield(mri, tmp), mri.dim); - tmp(tmp=='.') = '_'; - ft_write_mri([cfg.intermediatename '_' tmp '.img'], data, 'transform', mri.transform, 'spmversion', cfg.spmversion); +for k = 2:length(cfg.parameter) % skip the anatomy + tmp = strrep(cfg.parameter{k}, '.', '_'); + data = reshape(getsubfield(mri, tmp), mri.dim); + VF(k) = ft_write_mri([cfg.intermediatename '_' tmp ext], data, writeoptions{:}); end % read the template anatomical volume @@ -217,59 +227,116 @@ ft_error('Unknown template'); end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% compute the normalisation parameters, if needed +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +oldparams = true; +newparams = false; fprintf('performing the normalisation\n'); -% do spatial normalisation according to these steps -% step 1: read header information for template and source image -% step 2: compute transformation parameters -% step 3: write the results to a file with prefix 'w' - -if ~isfield(cfg, 'spmparams') && strcmp(cfg.nonlinear, 'yes') - fprintf('warping the individual anatomy to the template anatomy\n'); - % compute the parameters by warping the individual anatomy - VF = spm_vol([cfg.intermediatename '_anatomy.img']); - params = spm_normalise(VG,VF); -elseif ~isfield(cfg, 'spmparams') && strcmp(cfg.nonlinear, 'no') - fprintf('warping the individual anatomy to the template anatomy, using only linear transformations\n'); - % compute the parameters by warping the individual anatomy - VF = spm_vol([cfg.intermediatename '_anatomy.img']); - flags.nits = 0; % put number of non-linear iterations to zero - params = spm_normalise(VG,VF,[],[],[],flags); +if ~isfield(cfg, 'spmparams') + if strcmp(cfg.nonlinear, 'yes') && ~(strcmp(cfg.spmversion, 'spm12') && strcmp(cfg.spmmethod, 'new')) + fprintf('warping the individual anatomy to the template anatomy\n'); + % compute the parameters by warping the individual anatomy + %VF = spm_vol([cfg.intermediatename '_anatomy' ext]); + params = spm_normalise(VG, VF(1)); + elseif strcmp(cfg.nonlinear, 'no') && ~(strcmp(cfg.spmversion, 'spm12') && strcmp(cfg.spmmethod, 'new')) + fprintf('warping the individual anatomy to the template anatomy, using only linear transformations\n'); + % compute the parameters by warping the individual anatomy + %VF = spm_vol([cfg.intermediatename '_anatomy' ext]); + flags.nits = 0; % put number of non-linear iterations to zero + params = spm_normalise(VG, VF(1), [], [], [], flags); + elseif strcmp(cfg.spmversion, 'spm12') && strcmp(cfg.spmmethod, 'new') + if ~isfield(cfg, 'tpm') || isempty(cfg.tpm) + cfg.tpm = fullfile(spm('dir'),'tpm','TPM.nii'); + end + + fprintf('warping the individual anatomy to the template anatomy, using the new-style segmentation\n'); + + % create the structure that is required for spm_preproc8 + opts = ft_getopt(cfg, 'opts'); + opts.image = VF(1); + opts.tpm = ft_getopt(opts, 'tpm', spm_load_priors8(cfg.tpm)); + opts.biasreg = ft_getopt(opts, 'biasreg', 0.0001); + opts.biasfwhm = ft_getopt(opts, 'biasfwhm', 60); + opts.lkp = ft_getopt(opts, 'lkp', [1 1 2 2 3 3 4 4 4 5 5 5 5 6 6 ]); + opts.reg = ft_getopt(opts, 'reg', [0 0.001 0.5 0.05 0.2]); + opts.samp = ft_getopt(opts, 'samp', 3); + opts.fwhm = ft_getopt(opts, 'fwhm', 1); + + Affine = spm_maff8(opts.image(1),3,32,opts.tpm,eye(4),'mni'); + Affine = spm_maff8(opts.image(1),3, 1,opts.tpm,Affine,'mni'); + opts.Affine = Affine; + + % run the segmentation + params = spm_preproc8(opts); + + % this writes the 'deformation field' + fprintf('writing the deformation field to file\n'); + bb = spm_get_bbox(opts.tpm.V(1)); + spm_preproc_write8(params, zeros(6,4), [0 0], [0 1], 1, 1, bb, cfg.downsample); + + oldparams = false; + newparams = true; + end + else fprintf('using the parameters specified in the configuration, skipping the parameter estimation\n'); % use the externally specified parameters - VF = spm_vol([cfg.intermediatename '_anatomy.img']); + %VF = spm_vol([cfg.intermediatename '_anatomy' ext]); params = cfg.spmparams; + + if ~isfield(params, 'Tr') + ft_error('Using precomputed parameters is not allowed with spmmethod=''new'', not sure whether this will work'); + oldparams = false; + newparams = true; + + % this writes the 'deformation field' + fprintf('writing the deformation field to file\n'); + bb = spm_get_bbox(params.tpm(1)); + spm_preproc_write8(params, zeros(6,4), [0 0], [0 1], 1, 1, bb, cfg.downsample); + end end -flags.vox = [cfg.downsample,cfg.downsample,cfg.downsample]; - -% determine the affine source->template coordinate transformation -final = VG.mat * inv(params.Affine) * inv(VF.mat) * initial; - -% apply the normalisation parameters to each of the volumes -files = cell(1,numel(cfg.parameter)); -wfiles = cell(1,numel(cfg.parameter)); -for parlop=1:length(cfg.parameter) - fprintf('creating normalised analyze-file for %s\n', cfg.parameter{parlop}); - tmp = cfg.parameter{parlop}; - tmp(tmp=='.') = '_'; - files{parlop} = sprintf('%s_%s.img', cfg.intermediatename, tmp); - [p, f, x] = fileparts(files{parlop}); - wfiles{parlop} = fullfile(p, ['w' f x]); -end -spm_write_sn(char(files),params,flags); % this creates the 'w' prefixed files - -% spm_figure('Create', 'Graphics'); -% spm_normalise_disp(params,VF); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% apply the normalisation parameters to the specified volumes +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% normalised = []; +fprintf('creating the normalized volumes\n'); +if oldparams + % apply the normalisation parameters to each of the volumes + flags.vox = cfg.downsample.*[1 1 1]; + spm_write_sn(char({VF.fname}), params, flags); % this creates the 'w' prefixed files + for k = 1:numel(VF) + [p, f, x] = fileparts(VF(k).fname); + Vout(k) = spm_vol(fullfile(p, ['w' f x])); + end +elseif newparams + [pth,fname,ext] = fileparts(params.image.fname); + + tmp = []; + tmp.fnames = {VF(:).fname}; + tmp.savedir.saveusr{1} = pth; + tmp.interp = 4; + tmp.mask = 0; + tmp.fwhm = [0 0 0]; + + job = []; + job.comp{1}.def = {fullfile(pth,['y_',fname,ext])}; + job.out{1}.pull = tmp; + out = spm_deformations(job); + Vout = spm_vol(char(out.warped)); +end + % read the normalised results from the 'w' prefixed files -V = spm_vol(char(wfiles)); -for vlop=1:length(V) - normalised = setsubfield(normalised, cfg.parameter{vlop}, spm_read_vols(V(vlop))); +for k=1:length(Vout) + normalised = setsubfield(normalised, cfg.parameter{k}, spm_read_vols(Vout(k))); end + +% determine the affine source->template coordinate transformation +final = VG.mat * inv(params.Affine) * inv(VF(1).mat) * initial; -normalised.transform = V(1).mat; +normalised.transform = Vout(1).mat; normalised.dim = size(normalised.anatomy); normalised.params = params; % this holds the normalization parameters normalised.initial = initial; % this holds the initial co-registration to approximately align with the template @@ -285,20 +352,19 @@ if strcmp(cfg.write, 'yes') % create an spm-compatible file for each of the normalised volumes - for parlop=1:length(cfg.parameter) % include the anatomy - tmp = cfg.parameter{parlop}; + for k = 1:length(cfg.parameter) % include the anatomy + tmp = strrep(cfg.parameter{k}, '.', '_'); data = reshape(getsubfield(normalised, tmp), normalised.dim); - tmp(tmp=='.') = '_'; - ft_write_mri([cfg.name '_' tmp '.img'], data, 'transform', normalised.transform, 'spmversion', cfg.spmversion); + ft_write_mri([cfg.name '_' tmp ext], data, writeoptions); end end if strcmp(cfg.keepintermediate, 'no') % remove the intermediate files - for flop=1:length(files) - [p, f, x] = fileparts(files{flop}); + for k = 1:length(Vout) + [p, f] = fileparts(VF(k).fname); delete(fullfile(p, [f, '.*'])); - [p, f, x] = fileparts(wfiles{flop}); + [p, f] = fileparts(Vout(k).fname); delete(fullfile(p, [f, '.*'])); end end diff --git a/external/fieldtrip/ft_volumerealign.m b/external/fieldtrip/ft_volumerealign.m index c7d90d0e..2bdaf07a 100644 --- a/external/fieldtrip/ft_volumerealign.m +++ b/external/fieldtrip/ft_volumerealign.m @@ -394,7 +394,7 @@ set(h, 'windowbuttondownfcn', @cb_buttonpress); set(h, 'windowbuttonupfcn', @cb_buttonrelease); set(h, 'windowkeypressfcn', @cb_keyboard); - set(h, 'CloseRequestFcn', @cb_cleanup); + set(h, 'CloseRequestFcn', @cb_quit); % axis handles will hold the anatomical functional if present, along with labels etc. h1 = axes('position', [0.06 0.06+0.06+h3size(2) h1size(1) h1size(2)]); @@ -576,7 +576,7 @@ set(h, 'visible', 'on'); % add callbacks set(h, 'windowkeypressfcn', @cb_keyboard_surface); - set(h, 'CloseRequestFcn', @cb_cleanup); + set(h, 'CloseRequestFcn', @cb_quit); % create figure handles h1 = axes; @@ -695,7 +695,7 @@ fprintf('doing interactive realignment with headshape\n'); tmpcfg = []; tmpcfg.template.headshape = shape; % this is the Polhemus recorded headshape - tmpcfg.template.headshapestyle = 'vertex'; + tmpcfg.template.headshapestyle = 'vertex'; tmpcfg.individual.headshape = scalp; % this is the headshape extracted from the anatomical MRI tmpcfg.individual.headshapestyle = 'surface'; tmpcfg = ft_interactiverealign(tmpcfg); @@ -982,7 +982,7 @@ transform = inv(spm_matrix(x(:)')); % from V1 to V2, to be multiplied still with the original transform (mri.transform), see below end - + if isfield(target, 'coordsys') coordsys = target.coordsys; else @@ -1098,16 +1098,16 @@ set(h, 'windowbuttondownfcn', @cb_buttonpress); set(h, 'windowbuttonupfcn', @cb_buttonrelease); set(h, 'windowkeypressfcn', @cb_keyboard); - set(h, 'CloseRequestFcn', @cb_cleanup); + set(h, 'CloseRequestFcn', @cb_quit); % axis handles will hold the anatomical functional if present, along with labels etc. h1 = axes('position', [0.06 0.06+0.06+h3size(2) h1size(1) h1size(2)]); h2 = axes('position', [0.06+0.06+h1size(1) 0.06+0.06+h3size(2) h2size(1) h2size(2)]); h3 = axes('position', [0.06 0.06 h3size(1) h3size(2)]); - set(h1, 'Tag', 'ik', 'Visible', 'off', 'XAxisLocation', 'top'); + set(h1, 'Tag', 'ij', 'Visible', 'off', 'XAxisLocation', 'top'); set(h2, 'Tag', 'jk', 'Visible', 'off', 'YAxisLocation', 'right'); % after rotating in ft_plot_ortho this becomes top - set(h3, 'Tag', 'ij', 'Visible', 'off'); + set(h3, 'Tag', 'ik', 'Visible', 'off'); set(h1, 'DataAspectRatio', 1./[voxlen1 voxlen2 voxlen3]); set(h2, 'DataAspectRatio', 1./[voxlen1 voxlen2 voxlen3]); @@ -1364,7 +1364,7 @@ function cb_keyboard_surface(h, eventdata) key = get(h, 'userdata'); else % determine the key that was pressed on the keyboard - key = parseKeyboardEvent(eventdata); + key = parsekeyboardevent(eventdata); end % get the most recent surface position that was clicked with the mouse @@ -1397,7 +1397,7 @@ function cb_keyboard_surface(h, eventdata) setappdata(h, 'opt', opt); if isequal(key, 'q') - cb_cleanup(h); + cb_quit(h); else cb_redraw_surface(h); end @@ -1690,14 +1690,17 @@ function cb_redraw(h, eventdata) % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function cb_keyboard(h, eventdata) +h = getparent(h); +opt = getappdata(h, 'opt'); if isempty(eventdata) % determine the key that corresponds to the uicontrol element that was activated key = get(h, 'userdata'); else % determine the key that was pressed on the keyboard - key = parseKeyboardEvent(eventdata); + key = parsekeyboardevent(eventdata); end + % get focus back to figure if ~strcmp(get(h, 'type'), 'figure') set(h, 'enable', 'off'); @@ -1705,9 +1708,6 @@ function cb_keyboard(h, eventdata) set(h, 'enable', 'on'); end -h = getparent(h); -opt = getappdata(h, 'opt'); - curr_ax = get(h, 'currentaxes'); tag = get(curr_ax, 'tag'); @@ -1716,7 +1716,7 @@ function cb_keyboard(h, eventdata) key = ''; end -% the following code is largely shared with FT_SOURCEPLOT +% the following code is largely shared by FT_SOURCEPLOT, FT_VOLUMEREALIGN, FT_INTERACTIVEREALIGN, FT_MESHREALIGN, FT_ELECTRODEPLACEMENT switch key case {'' 'shift+shift' 'alt-alt' 'control+control' 'command-0'} % do nothing @@ -1742,7 +1742,7 @@ function cb_keyboard(h, eventdata) case 'q' setappdata(h, 'opt', opt); - cb_cleanup(h); + cb_quit(h); case {'i' 'j' 'k' 'm' 28 29 30 31 'leftarrow' 'rightarrow' 'uparrow' 'downarrow'} % TODO FIXME use leftarrow rightarrow uparrow downarrow % update the view to a new position @@ -1765,8 +1765,8 @@ function cb_keyboard(h, eventdata) setappdata(h, 'opt', opt); cb_redraw(h); + case {43 'add' 'shift+equal'} % + or numpad + % contrast scaling - case {43 'shift+equal'} % numpad + % disable if viewresult if ~opt.viewresult if isempty(opt.clim) @@ -1780,7 +1780,8 @@ function cb_keyboard(h, eventdata) cb_redraw(h); end - case {45 'shift+hyphen'} % numpad - + case {45 'subtract' 'hyphen' 'shift+hyphen'} % - or numpad - + % contrast scaling % disable if viewresult if ~opt.viewresult if isempty(opt.clim) @@ -1929,7 +1930,7 @@ function cb_getposition(h, eventdata) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function cb_cleanup(h, eventdata) +function cb_quit(h, eventdata) opt = getappdata(h, 'opt'); if ~opt.viewresult @@ -1951,35 +1952,6 @@ function cb_cleanup(h, eventdata) p = get(h, 'parent'); end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function key = parseKeyboardEvent(eventdata) - -key = eventdata.Key; - -% handle possible numpad events (different for Windows and UNIX systems) -% NOTE: shift+numpad number does not work on UNIX, since the shift -% modifier is always sent for numpad events -if isunix() - shiftInd = match_str(eventdata.Modifier, 'shift'); - if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) - % now we now it was a numpad keystroke (numeric character sent AND - % shift modifier present) - key = eventdata.Character; - eventdata.Modifier(shiftInd) = []; % strip the shift modifier - end -elseif ispc() - if strfind(eventdata.Key, 'numpad') - key = eventdata.Character; - end -end - -if ~isempty(eventdata.Modifier) - key = [eventdata.Modifier{1} '+' key]; -end - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/ft_volumesegment.m b/external/fieldtrip/ft_volumesegment.m index a2f9ba4a..068b66c4 100644 --- a/external/fieldtrip/ft_volumesegment.m +++ b/external/fieldtrip/ft_volumesegment.m @@ -175,6 +175,8 @@ % as of march 2017 keepintermediate is deprecated, does not seem to be % used, nor sensible. If result files are to be kept, use cfg.write +cfg = ft_checkconfig(cfg, 'renamedval', {'output', 'skin', 'scalp'}); + % check if the input data is valid for this function mri = ft_checkdata(mri, 'datatype', 'volume', 'feedback', 'yes', 'hasunit', 'yes', 'hascoordsys', 'yes'); @@ -236,7 +238,7 @@ ft_error('you must specify the output filename in cfg.name'); end end -[pathstr, name, ~] = fileparts(cfg.name); +[pathstr, name] = fileparts(cfg.name); cfg.name = fullfile(pathstr, name); % remove any possible file extension, to be added later if ~iscell(cfg.output) @@ -360,8 +362,8 @@ VF = ft_write_mri([cfg.name, '.img'], mri.anatomy, 'transform', mri.transform, 'spmversion', cfg.spmversion, 'dataformat', 'nifti_spm'); fprintf('performing the segmentation on the specified volume\n'); - p = spm_preproc(VF, px); - [po, ~] = spm_prep2sn(p); + p = spm_preproc(VF, px); + [po, dum] = spm_prep2sn(p); % this writes a mat file, may be needed for Dartel, not sure yet save([cfg.name '_sn.mat'],'-struct','po'); @@ -378,7 +380,7 @@ spm_preproc_write(po, opts); % generate the list of filenames that contains the segmented volumes - [pathstr, name, ~] = fileparts(cfg.name); + [pathstr, name] = fileparts(cfg.name); filenames = {fullfile(pathstr,['c1', name, '.img']);... fullfile(pathstr,['c2', name, '.img']);... fullfile(pathstr,['c3', name, '.img'])}; @@ -398,8 +400,8 @@ VF = ft_write_mri([cfg.name, '.nii'], mri.anatomy, 'transform', mri.transform, 'spmversion', cfg.spmversion, 'dataformat', 'nifti_spm'); fprintf('performing the segmentation on the specified volume, using the old-style segmentation\n'); - p = spm_preproc(VF, px); - [po, ~] = spm_prep2sn(p); + p = spm_preproc(VF, px); + [po, dum] = spm_prep2sn(p); % this write a mat file, may be needed for Dartel, not sure yet save([cfg.name '_sn.mat'],'-struct','po'); @@ -416,13 +418,15 @@ spm_preproc_write(po, opts); % generate the list of filenames that contains the segmented volumes - [pathstr, name, ~] = fileparts(cfg.name); + [pathstr, name] = fileparts(cfg.name); filenames = {fullfile(pathstr,['c1', name, '.nii']);... fullfile(pathstr,['c2', name, '.nii']);... fullfile(pathstr,['c3', name, '.nii'])}; elseif strcmp(cfg.spmmethod, 'new') || strcmp(cfg.spmmethod, 'mars') + cfg.tpm = ft_getopt(cfg, 'tpm'); + cfg.tpm = char(cfg.tpm); if ~isfield(cfg, 'tpm') || isempty(cfg.tpm) cfg.tpm = fullfile(spm('dir'),'tpm','TPM.nii'); end @@ -465,7 +469,7 @@ save([cfg.name '_seg8.mat'],'-struct','p'); - [pathstr, name, ~] = fileparts(cfg.name); + [pathstr, name] = fileparts(cfg.name); filenames = {fullfile(pathstr,['c1', name, '.nii']);... fullfile(pathstr,['c2', name, '.nii']);... fullfile(pathstr,['c3', name, '.nii']);... @@ -490,7 +494,7 @@ end if strcmp(cfg.write, 'no') - [pathstr, name, ~] = fileparts(cfg.name); + [pathstr, name] = fileparts(cfg.name); prefix = {'c1';'c2';'c3';'c3';'c4';'c5';'c6';'m';'y_';''}; suffix = {'_seg1.hdr';'_seg2.hdr';'_seg3.hdr';'_seg1.img';'_seg2.img';'_seg3.img';'_seg1.mat';'_seg2.mat';'_seg3.mat';'.hdr';'.img';'.nii'}; for k = 1:numel(prefix) @@ -681,7 +685,7 @@ % output: gray, white, csf elseif any(strcmp(outp, 'gray')) || any(strcmp(outp, 'white')) || any(strcmp(outp, 'csf')) - [~, tissuetype] = max(cat(4, segmented.csf, segmented.gray, segmented.white), [], 4); + [dum, tissuetype] = max(cat(4, segmented.csf, segmented.gray, segmented.white), [], 4); clear dummy if any(strcmp(outp, 'white')) segmented.white = (tissuetype == 3) & brainmask; diff --git a/external/fieldtrip/imotions2fieldtrip.m b/external/fieldtrip/imotions2fieldtrip.m new file mode 100644 index 00000000..7d08449a --- /dev/null +++ b/external/fieldtrip/imotions2fieldtrip.m @@ -0,0 +1,323 @@ +function [raw, event] = imotions2fieldtrip(filename, varargin) + +% IMOTIONS2FIELDTRIP imports an iMotions *.txt file and represents it as a FieldTrip +% raw data structure. +% +% Use as +% data = imotions2fieldtrip(filename, ...) +% +% Additional options should be specified in key-value pairs and can be +% interpolate = 'no', 'time' or 'data' (default = 'no') +% isnumeric = cell-array with labels corresponding to numeric data (default = {}) +% isinteger = cell-array with labels corresponding to integer data that should be interpolated with nearest where applicable (default = {}) +% isnotnumeric = cell-array with labels not corresponding to numeric data (default = {}) +% isevent = cell-array with labels corresponding to events (default = {}) +% isnotevent = cell-array with labels not corresponding to events (default = {}) +% +% The options 'isnumeric' and 'isnotnumeric' are mutually exclusive. Idem for +% 'isevent' and 'isnotevent'. +% +% When using the interpolate='data' option, both the data and the time are interpolated +% to a regularly sampled representation, when using the interpolate='time' option, only +% the time axis is interpolated to a regularly sampled representation. This addresses +% the case that the data was actually acquired with a regular sampling rate, but the time +% stamps in the file are not correctly representing this (a known bug with some type of +% iMotions data). +% +% See also FT_DATATYPE_RAW, FT_PREPROCESSING, FT_HEARTRATE, FT_ELECTRODERMALACTIVITY + +% Copyright (C) 2017-2018, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +interpolate = ft_getopt(varargin, 'interpolate', 'no'); +isnotnumeric = ft_getopt(varargin, 'isnotnumeric', {}); +isnotevent = ft_getopt(varargin, 'isnotevent', {}); +isnumeric = ft_getopt(varargin, 'isnumeric', {}); +isinteger = ft_getopt(varargin, 'isinteger', {}); +isevent = ft_getopt(varargin, 'isevent', {}); + +% try to be kind to the users and provide backwarrd compatibility support +if ~isempty(ft_getopt(varargin, 'fixtime')) + ft_warning('the option ''fixtime'' is obsolete, please use ''interpolate'''); + switch ft_getopt(varargin, 'fixtime') + case {'interpolate_data' 'squash'} + interpolate = 'data'; + case {'interpolate_time' 'interpolate'} + interpolate = 'time'; + case 'no' + interpolate = 'no'; + otherwise + ft_error('invalid option for ''fixtime'''); + end +end + +% these options are mutually exclusive +if ~isempty(isnumeric) && ~isempty(isnotnumeric) + error('you should specify either ''numeric'' or ''isnotnumeric'''); +end +if ~isempty(isevent) && ~isempty(isnotevent) + error('you should specify either ''isevent'' or ''isnotevent'''); +end + +% read the whole ASCII file into memory +% this will include a MATLAB table with the actual data +dat = read_imotions_txt(filename); + +time = dat.TimestampInSec; +label = dat.table.Properties.VariableNames; +numericdat = zeros(0,numel(time)); +numericsel = false(size(label)); + +if ~isempty(isnumeric) + isnotnumeric = setdiff(label, isnumeric); +elseif ~isempty(isnotnumeric) + isnumeric = setdiff(label, isnotnumeric); +end + +if ~isempty(isevent) + isnotevent = setdiff(label, isevent); +elseif ~isempty(isnotnumeric) + isevent = setdiff(label, isnotevent); +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% check for each field/column whether it is numerical +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +for i=1:numel(label) + % skip if it is known to be not numeric + if ismember(label{i}, isnotnumeric) + continue + end + + % don't convert if all empty + str = dat.table.(label{i}); + if all(cellfun(@isempty, str)) + ft_info('column %15s does not contain numeric data', label{i}); + continue + end + + % try converting the first element + str = dat.table.(label{i})(1); + val = str2double(str); + if any(~cellfun(@isempty, str) & isnan(val)) + ft_info('column %15s does not contain numeric data', label{i}); + continue + end + + % try converting the first 20 elements + if numel(time)>20 + str = dat.table.(label{i})(1:20); + val = str2double(str); + if any(~cellfun(@isempty, str) & isnan(val)) + ft_info('column %15s does not contain numeric data', label{i}); + continue + end + end + + % try converting the whole column + str = dat.table.(label{i}); + val = str2double(str); + if all(cellfun(@isempty, str) | isnan(val)) + ft_info('column %15s does not contain numeric data', label{i}); + continue + end + + % if it gets here, it means that the whole column is numerical + numericsel(i) = true; + ft_info('column %15s will be represented as channel', label{i}); + numericdat = cat(1, numericdat, val'); +end +% remember the labels for the columns with numerical data +numericlabel = label(numericsel); + +% Note, isinteger should be subselection of isnumeric +if ~isempty(isinteger) + if sum(strcmp(numericlabel, isinteger))==0 + error('isinteger should be subset of numerical channels') + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% construct numerical channels for the columns that represent events +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +eventcode = zeros(0,numel(time)); +eventtype = {}; +eventvalue = {}; + +% determine which channels are to be considered for events +eventsel = ~numericsel; +eventsel(ismember(label, isnotevent)) = false; +eventsel(strcmp(label, 'Timestamp')) = false; +eventsel(strcmp(label, 'TimestampUTC')) = false; + +for i=find(eventsel) + str = dat.table.(label{i}); + if all(cellfun(@isempty, str)) + eventsel(i) = false; + continue + end + + % add one numerical channel per event type + eventcode(end+1,:) = 0; + eventtype{end+1} = label{i}; + eventvalue{end+1} = {}; + + this = 1; + code = 1; % this is the numerical code for the event values + while this<=numel(str) + + next = find(~strcmp(str(this:end), str{this}), 1, 'first') + this - 1; + if isempty(next) + next = numel(str)+1; + end + + % store the event as string and as numerical code + eventvalue{end}{end+1} = str{this}; + eventcode(end,this:next-1) = code; + + this = next; + code = code + 1; + end +end + +% give some feedback +for i=1:numel(eventtype) + n = numel(eventvalue{i}); + if n>20 + % only give the summary + ft_info('column %15s contains %d events, which are not shown in detail\n', eventtype{i}, n); + else + % give the full details + ft_info('column %15s contains the following %d events\n', eventtype{i}, n); + for j=1:numel(eventvalue{i}) + ft_info('%2d %15s\n', j, eventvalue{i}{j}); + end + end +end + +% construct a raw data structure +raw.time{1} = time(:)'; +raw.trial{1} = cat(1, numericdat, eventcode); +raw.label = cat(1, numericlabel(:), eventtype(:)); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% the next section deals with interpolation of data and or time axis +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +switch interpolate + case 'no' + % keep it as it is + + case 'data' + % make a local copy for convenience + time = raw.time{1}; + + dt = diff(time); + dt = median(dt); + begtime = min(time); + endtime = max(time); + + if any(diff(time)~=dt) + ft_notice('resampling data onto regularly spaced time axis\n'); + tmpcfg = []; + tmpcfg.time = {begtime:dt:endtime}; + + if ~isempty(isinteger) + % interpolating integer channels using nearest + cfgint = []; + cfgint.channel = isinteger; + raw_int = ft_selectdata(cfgint, raw); + [dum, raw_int] = rollback_provenance([], raw_int); + tmpcfg.method = 'nearest'; + raw_int = ft_resampledata(tmpcfg, raw_int); + [dum, raw_int] = rollback_provenance([], raw_int); + + % interpolating other channels using 'pchip', see INTERP1, shape-preserving piecewise cubic interpolation + cfgint = []; + cfgint.channel = setdiff(raw.label,isinteger); + raw_nonint = ft_selectdata(cfgint, raw); + [dum, raw_nonint] = rollback_provenance([], raw_nonint); + tmpcfg.method = 'pchip'; + raw_nonint = ft_resampledata(tmpcfg, raw_nonint); + [dum, raw_nonint] = rollback_provenance([], raw_nonint); + + % append + raw = ft_appenddata([],raw_int, raw_nonint); + [dum, raw] = rollback_provenance([], raw); + else + % interpolating all channels using 'pchip', see INTERP1, shape-preserving piecewise cubic interpolation + tmpcfg.method = 'pchip'; + raw = ft_resampledata(tmpcfg, raw); + [dum, raw] = rollback_provenance([], raw); + end + end + + % the channels with the event codes should remain integers + sel = match_str(raw.label, eventtype); + raw.trial{1}(sel,:) = floor(raw.trial{1}(sel,:)); + + case 'time' + ft_notice('creating regularly spaced time axis\n'); + y = raw.time{1}; + x = 1:numel(y); + % use a GLM to estimate y = b0 * x + b1 + p = polyfit(x, y, 1); + y = polyval(p, x); + % replace the time by the estimated linear interpolant + raw.time{1} = y; + + otherwise + error('unsupported option') +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% construct a structure with all events +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +event = []; +nsample = numel(raw.time{1}); +for i=1:numel(eventtype) + eventcode = raw.trial{1}(i+numel(numericlabel),:); + sel = [find(diff([0 eventcode])) nsample+1]; + for j=1:numel(sel)-1 + event(end+1).type = eventtype{i}; + event(end ).value = eventvalue{i}{j}; + event(end ).sample = sel(j); + event(end ).duration = sel(j+1)-sel(j); + event(end ).offset = 0; + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% wrap up +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +raw.fsample = 1/median(diff(raw.time{1})); + +% keep the details of the original tabular data +raw.hdr.orig = rmfield(dat, 'table'); + +% remove the channels with the integer representation of the events +raw.label = raw.label(1:numel(numericlabel)); +raw.trial{1} = raw.trial{1}(1:numel(numericlabel), :); diff --git a/external/fieldtrip/inverse/README b/external/fieldtrip/inverse/README index 4faeafca..13098789 100644 --- a/external/fieldtrip/inverse/README +++ b/external/fieldtrip/inverse/README @@ -1,16 +1,26 @@ This is the FieldTrip INVERSE module. It contains functions for inverse -modeling and source estimation of EEG and MEG data, based on a choise +modeling and source estimation of EEG and MEG data, based on a variety of volume conduction models, including spherical and realistic models. -For more information please visit http://www.ru.nl/neuroimaging/fieldtrip - -The FieldTrip software is free but copyrighted software, distributed -under the terms of the GNU General Public Licence as published by -the Free Software Foundation (either version 2, or at your option -any later version). See the file COPYING for more details. +For more information please visit http://www.fieldtriptoolbox.org/development/modules +------------------------------------------------------------------------------- Copyright (C) 2008-2009, Donders Institute for Brain, Cognition and Behaviour, The Netherlands (DCCN, DCC, DCN) Copyright (C) 2003-2008, F.C. Donders Centre for Cognitive Neuroimaging, Nijmegen, The Netherlands (FCDC) Copyright (C) 2003-2005, Center for Sensory-Motor Interaction, Aalborg University, Denmark (SMI) Copyright (C) 1999-2003, Department of Medical Physics, Radboud University Nijmegen, The Netherlands (MBFYS) +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +% +% $Id$ + + if ~ischar(what) error('first argument must be a string'); end @@ -48,6 +70,9 @@ switch what case 'matlabversion' tf = is_matlab() && matlabversion(varargin{:}); + + case 'octaveversion' + tf = is_octave() && octaveversion(varargin{:}); case 'exists-in-private-directory' tf = is_matlab(); @@ -206,11 +231,31 @@ end % function + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function [inInterval] = matlabversion(min, max) +function [inInterval] = octaveversion(min, max) +% the version does not change, making it persistent speeds up the subsequent calls +persistent curVer + +if nargin<2 + max = min; +end +if isempty(curVer) + curVer = OCTAVE_VERSION; +end + +% perform comparison with respect to version number +[major, minor] = parseMatlabVersion(curVer); +[minMajor, minMinor] = parseMatlabVersion(min); +[maxMajor, maxMinor] = parseMatlabVersion(max); + +inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); + +end % function + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MATLABVERSION checks if the current MATLAB version is within the interval % specified by min and max. % @@ -229,27 +274,8 @@ % etc. % % See also VERSION, VER, VERLESSTHAN - -% Copyright (C) 2006, Robert Oostenveld -% Copyright (C) 2010, Eelke Spaak -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [inInterval] = matlabversion(min, max) % the version does not change, making it persistent speeds up the subsequent calls persistent curVer @@ -311,8 +337,8 @@ minor = int8((ver - floor(ver)) * 10); else % ver is string (e.g. '7.10'), parse accordingly [major, rest] = strtok(ver, '.'); - major = str2num(major); - minor = str2num(strtok(rest, '.')); + major = str2double(major); + minor = str2double(strtok(rest, '.')); end end % function diff --git a/external/fieldtrip/inverse/private/ft_senstype.m b/external/fieldtrip/inverse/private/ft_senstype.m index b6753338..09712abe 100644 --- a/external/fieldtrip/inverse/private/ft_senstype.m +++ b/external/fieldtrip/inverse/private/ft_senstype.m @@ -156,7 +156,6 @@ isnirs = isa(input, 'struct') && isfield(input, 'label') && isfield(input, 'transceiver'); islabel = isa(input, 'cell') && ~isempty(input) && isa(input{1}, 'char'); haslabel = isa(input, 'struct') && isfield(input, 'label'); -haschantype = isa(input, 'struct') && isfield(input, 'chantype'); if ~(isdata || isheader || isgrad || iselec || isnirs || islabel || haslabel) && isfield(input, 'hdr') input = input.hdr; @@ -228,6 +227,7 @@ sens = []; end +haschantype = isfield(sens, 'chantype'); if isfield(sens, 'type') % preferably the structure specifies its own type diff --git a/external/fieldtrip/inverse/private/hasyokogawa.m b/external/fieldtrip/inverse/private/hasyokogawa.m index cf309aab..e013ca78 100644 --- a/external/fieldtrip/inverse/private/hasyokogawa.m +++ b/external/fieldtrip/inverse/private/hasyokogawa.m @@ -7,7 +7,7 @@ % Use as % string = hasyokogawa; % which returns a string describing the toolbox version, e.g. "12bitBeta3", -% "16bitBeta3", or "16bitBeta6" for preliminary versions, or '1.4' for the +% "16bitBeta3", or "16bitBeta6" for preliminary versions, or '1.5' for the % official Yokogawa MEG Reader Toolbox. An empty string is returned if the toolbox % is not installed. The string "unknown" is returned if it is installed but % the version is unknown. @@ -43,9 +43,9 @@ % there are a few versions of the old preliminary implementation, such as % 12bitBeta3, 16bitBeta3 and 16bitBeta6. In 2011 a completely new -% implementation was officially released, which contains functions with -% other names. At the time of writing this, the new implementation is -% version 1.4. +% implementation was officially released, which contains functions with other names. +% At the time of writing this [2018.06.08], +% the new implementation, Yokogawa MEG Reader, is version 1.5, in which EEG data are supported. if exist('getYkgwVersion', 'file') res = getYkgwVersion(); diff --git a/external/fieldtrip/inverse/private/mesh_spectrum.m b/external/fieldtrip/inverse/private/mesh_spectrum.m index 14c6fcc6..8d9d45ce 100644 --- a/external/fieldtrip/inverse/private/mesh_spectrum.m +++ b/external/fieldtrip/inverse/private/mesh_spectrum.m @@ -34,7 +34,7 @@ elseif length(pnt)==2&&j == 2 disp('Computing the spectrum of the the second hemisphere') end - [L{j},~] = mesh_laplacian(pnt{j},tri{j}); + L{j} = mesh_laplacian(pnt{j},tri{j}); L{j} = (L{j} + L{j}')/2; disp('Computing the spectrum of the negative Laplacian matrix') [H{j},D] = eigs(L{j},n,'sm'); diff --git a/external/fieldtrip/inverse/private/mkfilt_eloreta_v2.m b/external/fieldtrip/inverse/private/mkfilt_eloreta.m old mode 100755 new mode 100644 similarity index 83% rename from external/fieldtrip/inverse/private/mkfilt_eloreta_v2.m rename to external/fieldtrip/inverse/private/mkfilt_eloreta.m index 68f40316..6d01e24f --- a/external/fieldtrip/inverse/private/mkfilt_eloreta_v2.m +++ b/external/fieldtrip/inverse/private/mkfilt_eloreta.m @@ -1,6 +1,6 @@ -function A=mkfilt_eloreta_v2(L,regu) +function [A,Wout]=mkfilt_eloreta(L,regu,W); % makes spatial filter according to eLoreta -% usage A=mkfilt_eloreta_v2(L); or A=mkfilt_eloreta_v2(L,regu); +% usage A=mkfilt_eloreta(L); or A=mkfilt_eloreta(L,regu); % % input L: NxMxP leadfield tensor for N channels, M voxels, and % P dipole directions. Typically P=3. (If you do MEG for @@ -16,10 +16,10 @@ % % code implemented by Guido Nolte % please cite -% ?R.D. Pascual-Marqui: Discrete, 3D distributed, linear imaging methods of electric neuronal activity. Part 1: exact, zero -% error localization. arXiv:0710.3341 [math-ph], 2007-October-17, http://arxiv.org/pdf/0710.3341 ? +% “R.D. Pascual-Marqui: Discrete, 3D distributed, linear imaging methods of electric neuronal activity. Part 1: exact, zero +% error localization. arXiv:0710.3341 [math-ph], 2007-October-17, http://arxiv.org/pdf/0710.3341 ” -if nargin<2; regu=.05; end +if nargin<2;regu=.05;end [nchan ng ndum]=size(L); LL=zeros(nchan,ndum,ng); @@ -29,7 +29,10 @@ LL=reshape(LL,nchan,ndum*ng); u0=eye(nchan); +if nargin<3 W=reshape(repmat(eye(ndum),1,ng),ndum,ndum,ng); +end + Winv=zeros(ndum,ndum,ng); winvkt=zeros(ng*ndum,nchan); kont=0; @@ -53,15 +56,17 @@ [ux,sx,vx]=svd(kwinvkt); %disp(sx(1,1)/alpha) - + Wold=W; for i=1:ng; Lloc=squeeze(L(:,i,:)); - Wold=W; - W(:,:,i)=sqrtm(Lloc'*M*Lloc); + % Wold=W; + Mb=Lloc'*M*Lloc; + Mb=Mb+eye(ndum)*trace(Mb)/10^10; + W(:,:,i)=sqrtm(Mb); end reldef=(norm(reshape(W,[],1)-reshape(Wold,[],1))/norm(reshape(Wold,[],1))); disp(reldef) - if kk>20 || reldef< .000001 ; kont=1; end; + if kk>20 || reldef< .000001 ; kont=1;end; end %disp(kk) @@ -72,4 +77,5 @@ for i=1:ng; A(:,i,:)=(Winv(:,:,i)*ktm(ndum*(i-1)+1:ndum*i,:))'; end + Wout=W; return diff --git a/external/fieldtrip/inverse/private/quaternion.m b/external/fieldtrip/inverse/private/quaternion.m index bd34c4f6..d77b70d4 100644 --- a/external/fieldtrip/inverse/private/quaternion.m +++ b/external/fieldtrip/inverse/private/quaternion.m @@ -9,11 +9,12 @@ % Q [q0, q1, q2, q3, q4, q5, q6] vector with parameters % H corresponding homogenous transformation matrix % -% See Elekta/Neuromag MaxFilter manual version 2.2, section "D2 Coordinate Matching", -% page 77 for more details and +% If the input vector has length 6, it is assumed to represent a unit quaternion without scaling. +% +% See Elekta/Neuromag MaxFilter manual version 2.2, section "D2 Coordinate Matching", page 77 for more details and % https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Conversion_to_and_from_the_matrix_representation -% Copyright (C) 2016, Robert Oostenveld +% Copyright (C) 2016-2017, Robert Oostenveld % % This program is free software; you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by @@ -47,6 +48,13 @@ % % $Id$ +if numel(q)==6 + % this is used a lot by the Elekta software, where the first element is left out and a rigid body transformation wothout scaling is used. + % see also https://github.com/mne-tools/mne-python/blob/maint/0.15/mne/transforms.py#L1137 + q0 = sqrt(1 - q(1)^2 - q(2)^2 - q(3)^2); + q = [q0 q]; +end + if numel(q)~=7 ft_error('incorrect input vector'); end diff --git a/external/fieldtrip/inverse/private/solid_angle.mexw32 b/external/fieldtrip/inverse/private/solid_angle.mexw32 index 6ffabebf..c7231629 100755 Binary files a/external/fieldtrip/inverse/private/solid_angle.mexw32 and b/external/fieldtrip/inverse/private/solid_angle.mexw32 differ diff --git a/external/fieldtrip/inverse/private/solid_angle.mexw64 b/external/fieldtrip/inverse/private/solid_angle.mexw64 index 899195aa..54123674 100755 Binary files a/external/fieldtrip/inverse/private/solid_angle.mexw64 and b/external/fieldtrip/inverse/private/solid_angle.mexw64 differ diff --git a/external/fieldtrip/inverse/residualvariance.m b/external/fieldtrip/inverse/residualvariance.m index b681c3ed..0b333381 100644 --- a/external/fieldtrip/inverse/residualvariance.m +++ b/external/fieldtrip/inverse/residualvariance.m @@ -57,6 +57,10 @@ dip.subspace = dip.subspace(originside); end +if isfield(dip, 'leadfield') + dip.leadfield = dip.leadfield(originside); +end + if isfield(dip, 'subspace') % remember the original data prior to the voxel dependant subspace projection dat_pre_subspace = dat; diff --git a/external/fieldtrip/nutmeg2fieldtrip.m b/external/fieldtrip/nutmeg2fieldtrip.m index d5dcbe71..be1d0efe 100644 --- a/external/fieldtrip/nutmeg2fieldtrip.m +++ b/external/fieldtrip/nutmeg2fieldtrip.m @@ -1,4 +1,4 @@ -function [data, mri, grid] = nutmeg2fieldtrip(cfg,fileorstruct) +function [data, mri, grid] = nutmeg2fieldtrip(cfg, fileorstruct) % NUTMEG2FIELDTRIP converts from NUTMEG either a sensor data structure % ('nuts') to a valid FieldTrip 'raw' structure (plus 'grid' and 'mri' if diff --git a/external/fieldtrip/plotting/README b/external/fieldtrip/plotting/README index a68e9923..85465ac8 100644 --- a/external/fieldtrip/plotting/README +++ b/external/fieldtrip/plotting/README @@ -3,48 +3,23 @@ visualising data and interacting with it. These functions form the building blocks for the FieldTrip data plotting functions and graphical user interfaces. -For more information please visit http://www.ru.nl/neuroimaging/fieldtrip. - -The FieldTrip software is free but copyrighted software, distributed -under the terms of the GNU General Public Licence as published by -the Free Software Foundation (either version 2, or at your option -any later version). See the file COPYING for more details. +For more information please visit http://www.fieldtriptoolbox.org/module/plotting +------------------------------------------------------------------------------- Copyright (C) 2009, Donders Institute for Brain, Cognition and Behaviour, The Netherlands (DCCN) Copyright (C) 2009, Netherlands Institute for Neuroscience (NIN) +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. -Function overview -================= - -The following functions are used for displaying data in 2-D - ft_plot_box.m - ft_plot_lay.m - ft_plot_line.m - ft_plot_text.m - ft_plot_topo.m - ft_plot_vector.m - ft_plot_matrix.m - ft_plot_montage.m - ft_plot_ortho.m (can also be used for 3-D display) - ft_plot_slice.m (can also be used for 3-D display) - -The following functions are used for displaying data in 3-D - ft_plot_dipole.m - ft_plot_headshape.m - ft_plot_topo3d.m - ft_plot_vol.m - ft_plot_mesh.m - ft_plot_sens.m +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. -The following functions are used for graphical user interaction - ft_uilayout.m - ft_select_box.m - ft_select_channel.m - ft_select_circle.m - ft_select_contour.m - ft_select_point.m - ft_select_point3d.m - ft_select_range.m - ft_select_voxel.m +You should have received a copy of the GNU General Public License +along with this program. If not, see = 1, minimum radius of cloud if scalerad = 'yes' (default = 1) +% 'cloudtype' = 'point' plots a single 2D point at each sensor position (see plot3) +% 'cloud' (default) plots a group of spherically arranged points at each sensor position +% 'surf' plots a single spherical surface mesh at each sensor position % 'scalerad' = scale radius with val, can be 'yes' or 'no' (default = 'yes') -% 'colormap' = colormap for functional data, see COLORMAP -% 'colorgrad' = 'white' or a scalar (e.g. 1), degree to which color of points -% in cloud changes from its center +% 'radius' = scalar, maximum radius of cloud (default = 4) % 'clim' = 1x2 vector specifying the min and max for the colorscale % 'unit' = string, convert the sensor array to the specified geometrical units (default = []) % 'mesh' = string or Nx1 cell array, triangulated mesh(es), see FT_PREPARE_MESH % 'slice' = requires 'mesh' as input (default = 'none') % '2d', plots 2D slices through the cloud with an outline of the mesh % '3d', draws an outline around the mesh at a particular slice -% 'slicetype' = 'surf' plots the slices as a surface -% 'point' (default) plots the slices as points % -% The following inputs apply when 'slice' = 'none' or '3d', or '2dslicetype' = 'point' +% The following inputs apply when 'cloudtype' = 'cloud' +% 'rmin' = scalar >= 1, minimum radius of cloud if scalerad = 'yes' (default = 1) +% 'colormap' = colormap for functional data, see COLORMAP +% 'colorgrad' = 'white' or a scalar (e.g. 1), degree to which color of points +% in cloud changes from its center % 'ptsize' = scalar, size of points in cloud (default = 1) % 'ptdensity' = scalar, density of points in cloud (default = 20) % 'ptgradient' = scalar, degree to which density of points in cloud changes % from its center, default = .5 (uniform density) % +% The following options apply when 'cloudtype' = 'point' +% 'marker' = marker type representing the channels, see plot3 (default = '.') +% % The following inputs apply when 'slice' = '2d' or '3d' % 'ori' = 'x', 'y', or 'z', specifies the orthogonal plane which will be plotted (default = 'y') % 'slicepos' = 'auto' or Nx1 vector specifying the position of the @@ -45,7 +49,7 @@ function ft_plot_cloud(pos, val, varargin) % % See also FT_ELECTRODEPLACEMENT, FT_PLOT_TOPO, FT_PLOT_TOPO3D -% The following inputs apply when 'slicetype' = 'surf' +% The following inputs apply when 'cloudtype' = 'surf' and 'slice' = '2d' % 'ncirc' = scalar, number of concentric circles to plot for each % cloud slice (default = 15) make this hidden or scale % 'scalealpha' = 'yes' or 'no', scale the maximum alpha value of the center circle @@ -91,6 +95,7 @@ function ft_plot_cloud(pos, val, varargin) pos = pos * ft_scalingfactor(posunit, unit); % get the generic input arguments +cloudtype = ft_getopt(varargin, 'cloudtype', 'cloud'); radius = ft_getopt(varargin, 'radius', 4 * ft_scalingfactor('mm', unit)); rmin = ft_getopt(varargin, 'rmin', 1 * ft_scalingfactor('mm', unit)); scalerad = ft_getopt(varargin, 'scalerad', 'yes'); @@ -103,14 +108,16 @@ function ft_plot_cloud(pos, val, varargin) clim = ft_getopt(varargin, 'clim'); meshplot = ft_getopt(varargin, 'mesh'); -% point related inputs +% cloud related inputs ptsize = ft_getopt(varargin, 'ptsize', 1); ptdens = ft_getopt(varargin, 'ptdensity', 20); ptgrad = ft_getopt(varargin, 'ptgradient', .5); +% point related inputs +marker = ft_getopt(varargin, 'marker', '.'); + % slice related inputs sli = ft_getopt(varargin, 'slice', 'none'); -slicetype = ft_getopt(varargin, 'slicetype', 'point'); ori = ft_getopt(varargin, 'ori', 'y'); slicepos = ft_getopt(varargin, 'slicepos', 'auto'); nslices = ft_getopt(varargin, 'nslices', 1); @@ -126,6 +133,7 @@ function ft_plot_cloud(pos, val, varargin) edgecolor = ft_getopt(varargin, 'edgecolor', 'none'); facealpha = ft_getopt(varargin, 'facealpha', 1); edgealpha = ft_getopt(varargin, 'edgealpha', 0); +vertexcolor = ft_getopt(varargin, 'vertexcolor', 'curv'); if rmin < 1 * ft_scalingfactor('mm', unit) ft_error('cfg.rmin must be equal or larger than 1 mm'); @@ -158,7 +166,7 @@ function ft_plot_cloud(pos, val, varargin) end end - % facecolor and edgecolor should be cell array + % facecolor, edgecolor, and vertexcolor should be cell array if ~iscell(facecolor) tmp = facecolor; if ischar(tmp) @@ -185,6 +193,19 @@ function ft_plot_cloud(pos, val, varargin) edgecolor = {}; end end + if ~iscell(vertexcolor) + tmp = vertexcolor; + if ischar(tmp) + vertexcolor = {tmp}; + elseif ismatrix(tmp) && size(tmp, 2) == 3 + vertexcolor = cell(size(tmp,1), 1); + for i=1:size(tmp,1) + vertexcolor{i} = tmp(i, 1:3); + end + else + vertexcolor = {}; + end + end % make sure each mesh has plotting options specified if numel(meshplot) > 1 @@ -209,6 +230,11 @@ function ft_plot_cloud(pos, val, varargin) edgealpha(m) = edgealpha(1); end end + if numel(vertexcolor) < numel(meshplot) + for m = numel(vertexcolor)+1:nmesh + vertexcolor(m) = vertexcolor(1); + end + end end end @@ -389,7 +415,7 @@ function ft_plot_cloud(pos, val, varargin) if oriZ; distance = abs(indpos(3)-slicepos(s)); end if distance < rmax(c) - if strcmp(slicetype, 'surf') + if strcmp(cloudtype, 'surf') xmax = rmax(c)*x; ymax = rmax(c)*y; @@ -443,7 +469,7 @@ function ft_plot_cloud(pos, val, varargin) ycmax(c) = max(ye+indpos(2)); ycmin(c) = min(ye+indpos(2)); zcmax(c) = max(slicedime+slicepos(s)); zcmin(c) = min(slicedime+indpos(s)); end - elseif strcmp(slicetype, 'point') + elseif strcmp(cloudtype, 'cloud') rng(0, 'twister'); % random number generator npoints = round(ptdens*pi*rmax(c)^2); % number of points based on area of cloud cross section azimuth = 2*pi*rand(npoints,1); % azimuthal angle for each point @@ -490,7 +516,8 @@ function ft_plot_cloud(pos, val, varargin) ycmax(c) = max(y+indpos(2)); ycmin(c) = min(y+indpos(2)); zcmax(c) = max(z+slicepos(s)); zcmin(c) = min(z+slicepos(s)); end - + else + error('if slice = "2d" then cloudtype must be either "surf" or "cloud"'); end % cloudtype end % if distance < rmax(c) end % cloud loop @@ -607,40 +634,55 @@ function ft_plot_cloud(pos, val, varargin) % generate point cloud(s) hold on; for n = 1:size(pos,1) % cloud loop - % point cloud with radius scaling - rng(0, 'twister'); % random number generator - npoints = round(ptdens*(4/3)*pi*rmax(n)^3); % number of points based on cloud volume - elevation = asin(2*rand(npoints,1)-1); % elevation angle for each point - azimuth = 2*pi*rand(npoints,1); % azimuth angle for each point - radii = rmax(n)*(rand(npoints,1).^ptgrad); % radius value for each point - radii = sort(radii); % sort radii in ascending order so they are plotted from inside out - [x,y,z] = sph2cart(azimuth, elevation, radii); % convert to Carthesian - - % color axis with radius scaling - if strcmp(cgrad, 'white') % color runs up to white + if strcmp(cloudtype, 'cloud') + % point cloud with radius scaling + rng(0, 'twister'); % random number generator + npoints = round(ptdens*(4/3)*pi*rmax(n)^3); % number of points based on cloud volume + elevation = asin(2*rand(npoints,1)-1); % elevation angle for each point + azimuth = 2*pi*rand(npoints,1); % azimuth angle for each point + radii = rmax(n)*(rand(npoints,1).^ptgrad); % radius value for each point + radii = sort(radii); % sort radii in ascending order so they are plotted from inside out + [x,y,z] = sph2cart(azimuth, elevation, radii); % convert to Carthesian + + % color axis with radius scaling + if strcmp(cgrad, 'white') % color runs up to white + indx = ceil(cmid) + sign(colscf(n))*floor(abs(colscf(n)*cmid)); + indx = max(min(indx,size(cmapsc,1)),1); % index should fall within the colormap + fcol = cmapsc(indx,:); % color [Nx3] + ptcol = [linspace(fcol(1), 1, npoints)' linspace(fcol(2), 1, npoints)' linspace(fcol(3), 1, npoints)']; + elseif isscalar(cgrad) % color runs down towards colorbar middle + rnorm = radii/rmax(n); % normalized radius + if radscf(n)>=.5 % extreme values + ptcol = val(n) - (flip(1-rnorm).^inv(cgrad))*val(n); % scaled fun [Nx1] + elseif radscf(n)<.5 % values closest to zero + ptcol = val(n) + (flip(1-rnorm).^inv(cgrad))*abs(val(n)); % scaled fun [Nx1] + end + else + ft_error('color gradient should be either ''white'' or a scalar determining color falloff') + end + + % draw the points + scatter3(x+pos(n,1), y+pos(n,2), z+pos(n,3), ptsize, ptcol, '.'); + elseif strcmp(cloudtype, 'surf') indx = ceil(cmid) + sign(colscf(n))*floor(abs(colscf(n)*cmid)); indx = max(min(indx,size(cmapsc,1)),1); % index should fall within the colormap fcol = cmapsc(indx,:); % color [Nx3] - ptcol = [linspace(fcol(1), 1, npoints)' linspace(fcol(2), 1, npoints)' linspace(fcol(3), 1, npoints)']; - elseif isscalar(cgrad) % color runs down towards colorbar middle - rnorm = radii/rmax(n); % normalized radius - if radscf(n)>=.5 % extreme values - ptcol = val(n) - (flip(1-rnorm).^inv(cgrad))*val(n); % scaled fun [Nx1] - elseif radscf(n)<.5 % values closest to zero - ptcol = val(n) + (flip(1-rnorm).^inv(cgrad))*abs(val(n)); % scaled fun [Nx1] - end - else - ft_error('color gradient should be either ''white'' or a scalar determining color falloff') + [xsp, ysp, zsp] = sphere(100); + hs = surf(rmax(n)*xsp+pos(n,1), rmax(n)*ysp+pos(n,2), rmax(n)*zsp+pos(n,3)); + set(hs, 'EdgeColor', 'none', 'FaceColor', fcol, 'FaceAlpha', 1); + elseif strcmp(cloudtype, 'point') + indx = ceil(cmid) + sign(colscf(n))*floor(abs(colscf(n)*cmid)); + indx = max(min(indx,size(cmapsc,1)),1); % index should fall within the colormap + fcol = cmapsc(indx,:); % color [Nx3] + + hs = plot3(pos(n,1), pos(n,2), pos(n,3), 'Marker', marker, 'MarkerSize', rmax(n), 'Color', fcol, 'Linestyle', 'none'); end - - % draw the points - scatter3(x+pos(n,1), y+pos(n,2), z+pos(n,3), ptsize, ptcol, '.'); end % end cloud loop if ~isempty(meshplot) for k = 1:numel(meshplot) % mesh loop ft_plot_mesh(meshplot{k}, 'facecolor', facecolor{k}, 'EdgeColor', edgecolor{k}, ... - 'facealpha', facealpha(k), 'edgealpha', edgealpha(k)); + 'facealpha', facealpha(k), 'edgealpha', edgealpha(k), 'vertexcolor', vertexcolor{k}); material dull end % end mesh loop diff --git a/external/fieldtrip/plotting/ft_plot_crosshair.m b/external/fieldtrip/plotting/ft_plot_crosshair.m index 58c9235f..f41e86bb 100644 --- a/external/fieldtrip/plotting/ft_plot_crosshair.m +++ b/external/fieldtrip/plotting/ft_plot_crosshair.m @@ -20,7 +20,7 @@ % Example % ft_plot_crosshair([0.5 0.5], 'color', 'r') % -% See also TEXT, LINE +% See also FT_PLOT_BOX, FT_PLOT_LINE, TEXT, LINE % Copyright (C) 2003-2017, Robert Oostenveld % diff --git a/external/fieldtrip/plotting/ft_plot_dipole.m b/external/fieldtrip/plotting/ft_plot_dipole.m index 24ed8413..85576a24 100644 --- a/external/fieldtrip/plotting/ft_plot_dipole.m +++ b/external/fieldtrip/plotting/ft_plot_dipole.m @@ -5,20 +5,23 @@ % % Use as % ft_plot_dipole(pos, mom, ...) -% where pos and mom are the dipole mosition and moment. +% where pos and mom are the dipole mosition and moment. % % Optional input arguments should be specified in key-value pairs and can include -% 'diameter' = number indicating sphere diameter (default = 'auto') -% 'length' = number indicating length of the stick (default = 'auto') -% 'color' = [r g b] values or string, for example 'brain', 'cortex', 'skin', 'black', 'red', 'r' (default = 'r') -% 'unit' = 'm', 'cm' or 'mm', used for automatic scaling (default = 'cm') -% 'scale' = scale the dipole with the amplitude, can be 'none', 'both', 'diameter', 'length' (default = 'none') -% 'alpha' = alpha value of the plotted dipole +% 'diameter' = number indicating sphere diameter (default = 'auto') +% 'length' = number indicating length of the stick (default = 'auto') +% 'thickness' = number indicating thickness of the stick (default = 'auto') +% 'color' = [r g b] values or string, for example 'brain', 'cortex', 'skin', 'black', 'red', 'r' (default = 'r') +% 'unit' = 'm', 'cm' or 'mm', used for automatic scaling (default = 'cm') +% 'scale' = scale the dipole with the amplitude, can be 'none', 'both', 'diameter', 'length' (default = 'none') +% 'alpha' = alpha value of the plotted dipole % % Example % ft_plot_dipole([0 0 0], [1 2 3], 'color', 'r', 'alpha', 1) +% +% See also FT_PLOT_MESH, FT_PLOT_ORTHO -% Copyright (C) 2009, Robert Oostenveld +% Copyright (C) 2009-2018, Robert Oostenveld % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -41,12 +44,13 @@ ws = warning('on', 'MATLAB:divideByZero'); % get the optional input arguments -amplitudescale = ft_getopt(varargin, 'scale', 'none'); -color = ft_getopt(varargin, 'color', 'r'); % can also be a RGB triplet -diameter = ft_getopt(varargin, 'diameter', 'auto'); -length = ft_getopt(varargin, 'length', 'auto'); -unit = ft_getopt(varargin, 'unit', 'cm'); -alpha = ft_getopt(varargin, 'alpha', 1); +amplitudescale = ft_getopt(varargin, 'scale', 'none'); +color = ft_getopt(varargin, 'color', 'r'); % can also be a RGB triplet +diameter = ft_getopt(varargin, 'diameter', 'auto'); +length = ft_getopt(varargin, 'length', 'auto'); +thickness = ft_getopt(varargin, 'thickness', 'auto'); +unit = ft_getopt(varargin, 'unit', 'cm'); +alpha = ft_getopt(varargin, 'alpha', 1); % for backward compatibility, this can be changed into an error at the end of 2016 units = ft_getopt(varargin, 'units'); @@ -55,7 +59,7 @@ unit = units; clear units end - + if isequal(diameter, 'auto') % the default is a 5 mm sphere switch unit @@ -71,7 +75,7 @@ end if isequal(length, 'auto') - % the default is a 15 mm stick + % the default is a 15 mm long stick switch unit case 'm' length = 0.015; @@ -84,6 +88,11 @@ end end +if isequal(thickness, 'auto') + % the default is 1/3 of the sphere diameter + thickness = diameter/3; +end + % dipole position should be Nx3 if all(size(pos) == [3 1]) pos = pos'; @@ -112,22 +121,24 @@ % scale the dipole diameter and length with its amplitude if strcmp(amplitudescale, 'length') || strcmp(amplitudescale, 'both') - this_length = length*amplitude; + this_length = length*amplitude; else - this_length = length; + this_length = length; end if strcmp(amplitudescale, 'diameter') || strcmp(amplitudescale, 'both') - this_diameter = diameter*amplitude; + this_diameter = diameter*amplitude; + this_thickness = thickness*amplitude; else - this_diameter = diameter; + this_diameter = diameter; + this_thickness = thickness; end % start with a unit sphere and cylinder sphere = unitsphere; stick = unitcylinder; sphere.pos = ft_warp_apply(scale([0.5 0.5 0.5]), sphere.pos, 'homogeneous'); % the diameter should be 1 - stick.pos = ft_warp_apply(scale([0.5 0.5 0.5]), stick.pos, 'homogeneous'); % the length should be 1 - stick.pos = ft_warp_apply(translate([0 0 0.5]), stick.pos, 'homogeneous'); % it should start in the origin + stick.pos = ft_warp_apply(scale([0.5 0.5 0.5]), stick.pos, 'homogeneous'); % the length and thickness should be 1 + stick.pos = ft_warp_apply(translate([0 0 0.5]), stick.pos, 'homogeneous'); % it should start in the origin % scale the sphere sx = this_diameter; @@ -142,8 +153,8 @@ sphere.pos = ft_warp_apply(translate([tx ty tz]), sphere.pos, 'homogeneous'); % scale the stick - sx = this_diameter/3; - sy = this_diameter/3; + sx = this_thickness; + sy = this_thickness; sz = this_length; stick.pos = ft_warp_apply(scale([sx sy sz]), stick.pos, 'homogeneous'); diff --git a/external/fieldtrip/plotting/ft_plot_headshape.m b/external/fieldtrip/plotting/ft_plot_headshape.m index fca7110f..b4e9e07c 100644 --- a/external/fieldtrip/plotting/ft_plot_headshape.m +++ b/external/fieldtrip/plotting/ft_plot_headshape.m @@ -23,6 +23,8 @@ % Example % shape = ft_read_headshape(filename); % ft_plot_headshape(shape) +% +% See also FT_PLOT_MESH, FT_PLOT_ORTHO % Copyright (C) 2009, Cristiano Micheli % diff --git a/external/fieldtrip/plotting/ft_plot_lay.m b/external/fieldtrip/plotting/ft_plot_lay.m index 258ce7b4..657afb90 100644 --- a/external/fieldtrip/plotting/ft_plot_lay.m +++ b/external/fieldtrip/plotting/ft_plot_lay.m @@ -33,7 +33,7 @@ function ft_plot_lay(lay, varargin) % 'width' = width of the local axes % 'height' = height of the local axes % -% See also FT_PREPARE_LAYOUT +% See also FT_PREPARE_LAYOUT, FT_PLOT_TOPO % Copyright (C) 2009, Robert Oostenveld % diff --git a/external/fieldtrip/plotting/ft_plot_line.m b/external/fieldtrip/plotting/ft_plot_line.m index 75a93cc8..307db28e 100644 --- a/external/fieldtrip/plotting/ft_plot_line.m +++ b/external/fieldtrip/plotting/ft_plot_line.m @@ -20,7 +20,7 @@ % 'hlim' = horizontal scaling limits within the local axes % 'vlim' = vertical scaling limits within the local axes % -% See also FT_PLOT_BOX +% See also FT_PLOT_BOX, FT_PLOT_CROSSHAIR % Copyrights (C) 2009-2011, Robert Oostenveld % diff --git a/external/fieldtrip/plotting/ft_plot_matrix.m b/external/fieldtrip/plotting/ft_plot_matrix.m index 1ab5b717..6f711178 100644 --- a/external/fieldtrip/plotting/ft_plot_matrix.m +++ b/external/fieldtrip/plotting/ft_plot_matrix.m @@ -37,7 +37,7 @@ function ft_plot_matrix(varargin) % Example % ft_plot_matrix(randn(30,50), 'width', 1, 'height', 1, 'hpos', 0, 'vpos', 0) % -% See also FT_PLOT_VECTOR, IMAGESC +% See also FT_PLOT_VECTOR, IMAGESC, SURF % Copyrights (C) 2009-2011, Robert Oostenveld % diff --git a/external/fieldtrip/plotting/ft_plot_mesh.m b/external/fieldtrip/plotting/ft_plot_mesh.m index 265fc44e..cb357659 100644 --- a/external/fieldtrip/plotting/ft_plot_mesh.m +++ b/external/fieldtrip/plotting/ft_plot_mesh.m @@ -36,7 +36,7 @@ % ft_plot_mesh(mesh, 'facecolor', 'skin', 'edgecolor', 'none') % camlight % -% See also TRIMESH, PATCH +% See also FT_PLOT_HEADSHAPE, FT_PLOT_VOL, TRIMESH, PATCH % Copyright (C) 2009, Cristiano Micheli % Copyright (C) 2009-2015, Robert Oostenveld @@ -315,6 +315,7 @@ bgcolor = repmat(facecolor, [numel(vertexcolor) 1]); rgb = bg_rgba2rgb(bgcolor, vertexcolor, cmap, clim, facealpha, alphamapping, alphalim); set(hs, 'FaceVertexCData', rgb, 'facecolor', 'interp'); + if ~isempty(clim); caxis(clim); end % set colorbar scale to match [fcolmin fcolmax] end if faceindex diff --git a/external/fieldtrip/plotting/ft_plot_patch.m b/external/fieldtrip/plotting/ft_plot_patch.m index 40547ebc..9493ca93 100644 --- a/external/fieldtrip/plotting/ft_plot_patch.m +++ b/external/fieldtrip/plotting/ft_plot_patch.m @@ -37,7 +37,7 @@ % vdat = [vdat vdat(end:-1:1)+1]; % ft_plot_patch(hdat, vdat) % -% See also FT_PLOT_VECTOR +% See also FT_PLOT_VECTOR, PATCH, PLOT % Copyrights (C) 2015, Roemer van der Meij % diff --git a/external/fieldtrip/plotting/ft_plot_sens.m b/external/fieldtrip/plotting/ft_plot_sens.m index cab45bac..9f5f0a2f 100644 --- a/external/fieldtrip/plotting/ft_plot_sens.m +++ b/external/fieldtrip/plotting/ft_plot_sens.m @@ -24,10 +24,12 @@ % 'coilsize' = diameter or edge length of the coils (default is automatic) % The following options apply to EEG electrodes % 'elec' = true/false, plot each individual electrode (default = false) +% 'orientation' = true/false, plot a line for the orientation of each electrode (default = false) % 'elecshape' = 'point', 'circle', 'square', or 'sphere' (default is automatic) % 'elecsize' = diameter of the electrodes (default is automatic) % The following options apply to NIRS optodes % 'opto' = true/false, plot each individual optode (default = false) +% 'orientation' = true/false, plot a line for the orientation of each optode (default = false) % 'optoshape' = 'point', 'circle', 'square', or 'sphere' (default is automatic) % 'optosize' = diameter of the optodes (default is automatic) % @@ -47,7 +49,7 @@ % figure; ft_plot_sens(sens, 'coilshape', 'circle', 'coil', true, 'chantype', 'meggrad') % figure; ft_plot_sens(sens, 'coilshape', 'circle', 'coil', false, 'orientation', true) % -% See also FT_READ_SENS, FT_DATATYPE_SENS, FT_PLOT_HEADSHAPE, FT_PREPARE_VOL_SENS +% See also FT_READ_SENS, FT_PLOT_HEADSHAPE, FT_PLOT_VOL % Copyright (C) 2009-2016, Robert Oostenveld % @@ -254,47 +256,21 @@ hold on end -if istrue(orientation) - if istrue(individual) - if isfield(sens, 'coilori') - pos = sens.coilpos; - ori = sens.coilori; - elseif isfield(sens, 'elecori') - pos = sens.elecpos; - ori = sens.elecori; - else - pos = []; - ori = []; - end - else - if isfield(sens, 'chanori') - pos = sens.chanpos; - ori = sens.chanori; - else - pos = []; - ori = []; - end - end - scale = ft_scalingfactor('mm', sens.unit)*20; % draw a line segment of 20 mm - for i=1:size(pos,1) - x = [pos(i,1) pos(i,1)+ori(i,1)*scale]; - y = [pos(i,2) pos(i,2)+ori(i,2)*scale]; - z = [pos(i,3) pos(i,3)+ori(i,3)*scale]; - line(x, y, z) - end -end - if istrue(individual) - % simply get the position of all individual coils or electrodes + % get the position of all individual coils, electrodes or optodes if isfield(sens, 'coilpos') pos = sens.coilpos; elseif isfield(sens, 'elecpos') pos = sens.elecpos; + elseif isfield(sens, 'optopos') + pos = sens.optopos; end if isfield(sens, 'coilori') ori = sens.coilori; elseif isfield(sens, 'elecori') ori = sens.elecori; + elseif isfield(sens, 'optoori') + ori = sens.optoori; else ori = []; end @@ -316,6 +292,31 @@ end % if istrue(individual) +if isempty(ori) + % determine the orientation by fitting a sphere to the positions + % this should be reasonable for scalp electrodes or optodes with complete coverage + try + tmp = pos(~any(isnan(pos), 2),:); % remove rows that contain a nan + center = fitsphere(tmp); + catch + center = [nan nan nan]; + end + for i=1:size(pos,1) + ori(i,:) = pos(i,:) - center; + ori(i,:) = ori(i,:)/norm(ori(i,:)); + end +end + +if istrue(orientation) + scale = ft_scalingfactor('mm', sens.unit)*20; % draw a line segment of 20 mm + for i=1:size(pos,1) + x = [pos(i,1) pos(i,1)+ori(i,1)*scale]; + y = [pos(i,2) pos(i,2)+ori(i,2)*scale]; + z = [pos(i,3) pos(i,3)+ori(i,3)*scale]; + line(x, y, z) + end +end + switch sensshape case 'point' if ~isempty(style) @@ -335,7 +336,7 @@ end else % the style is not specified, use facecolor for the marker - hs = plot3(pos(:,1), pos(:,2), pos(:,3), 'Marker', marker, 'MarkerSize', sensize, 'Color', facecolor, 'Linestyle', 'none'); + hs = scatter3(pos(:,1), pos(:,2), pos(:,3), sensize.^2, facecolor, marker); end case 'circle' diff --git a/external/fieldtrip/plotting/ft_plot_topo.m b/external/fieldtrip/plotting/ft_plot_topo.m index 4db9974d..fcb40356 100644 --- a/external/fieldtrip/plotting/ft_plot_topo.m +++ b/external/fieldtrip/plotting/ft_plot_topo.m @@ -28,7 +28,7 @@ % 'hlim' = horizontal scaling limits within the local axes % 'vlim' = vertical scaling limits within the local axes % -% See also FT_PLOT_TOPO3D, FT_TOPOPLOTER, FT_TOPOPLOTTFR +% See also FT_PLOT_TOPO3D, FT_PLOT_LAY, FT_TOPOPLOTER, FT_TOPOPLOTTFR % Copyrights (C) 2009-2013, Giovanni Piantoni, Robert Oostenveld % @@ -202,7 +202,7 @@ end % take out NaN channels if interpmethod does not work with NaNs -if flagNaN && strcmp(interpmethod, default_interpmethod) +if flagNaN && strcmp(interpmethod, 'v4') dat(NaNind) = []; chanX(NaNind) = []; chanY(NaNind) = []; diff --git a/external/fieldtrip/plotting/ft_plot_topo3d.m b/external/fieldtrip/plotting/ft_plot_topo3d.m index da4f387e..edf9ecac 100644 --- a/external/fieldtrip/plotting/ft_plot_topo3d.m +++ b/external/fieldtrip/plotting/ft_plot_topo3d.m @@ -14,7 +14,7 @@ function ft_plot_topo3d(pos, val, varargin) % 'facealpha' = scalar, between 0 and 1 (default = 1) % 'refine' = scalar, number of refinement steps for the triangulation, to get a smoother interpolation (default = 0) % -% See also FT_PLOT_TOPO, FT_TOPOPLOTER, FT_TOPOPLOTTFR +% See also FT_PLOT_TOPO, FT_PLOT_SENS, FT_TOPOPLOTER, FT_TOPOPLOTTFR % Copyright (C) 2009-2015, Robert Oostenveld % diff --git a/external/fieldtrip/plotting/ft_plot_vector.m b/external/fieldtrip/plotting/ft_plot_vector.m index 6991b8a2..2dbca376 100644 --- a/external/fieldtrip/plotting/ft_plot_vector.m +++ b/external/fieldtrip/plotting/ft_plot_vector.m @@ -67,7 +67,7 @@ % rgb = interp1(1:64, rgb, linspace(1,64,100)); % ft_plot_vector(1:100, 'color', rgb); % -% See also FT_PLOT_MATRIX +% See also FT_PLOT_MATRIX, PLOT % Copyrights (C) 2009-2013, Robert Oostenveld % @@ -455,20 +455,33 @@ if xaxis % x-axis should touch 0,0 xrange = hlim; - if sign(xrange(1))==sign(xrange(2)) - [dum minind] = min(abs(hlim)); - xrange(minind) = 0; - end - ft_plot_line(xrange, [0 0],'hpos',hpos,'vpos',vpos,'hlim',hlim,'vlim',vlim,'width',width,'height',height); + + y_intercept = [0 0]; % If the y-axis crosses zero, the horizontal line should be at y = 0. + + % If the y-axis is all positive or negative, it should be as close to + % zero as possible. + if all(vlim > 0) + y_intercept = repmat(min(vlim), 1, 2); + elseif all(vlim < 0) + y_intercept = repmat(max(vlim), 1, 2); + end %if + + ft_plot_line(xrange, y_intercept,'hpos',hpos,'vpos',vpos,'hlim',hlim,'vlim',vlim,'width',width,'height',height); end if yaxis - % y-axis should touch 0,0 yrange = vlim; - if sign(yrange(1))==sign(yrange(2)) - [dum minind] = min(abs(vlim)); - yrange(minind) = 0; - end - ft_plot_line([0 0], yrange,'hpos',hpos,'vpos',vpos,'hlim',hlim,'vlim',vlim,'width',width,'height',height); + + x_intercept = [0 0]; % If the x-axis crosses zero, the vertical line should be at x = 0. + + % If the x-axis is all positive or negative, it should be as close to + % zero as possible. + if all(hlim > 0) + x_intercept = repmat(min(hlim), 1, 2); + elseif all(vlim < 0) + x_intercept = repmat(max(hlim), 1, 2); + end %if + + ft_plot_line(x_intercept, yrange,'hpos',hpos,'vpos',vpos,'hlim',hlim,'vlim',vlim,'width',width,'height',height); end end diff --git a/external/fieldtrip/plotting/ft_plot_vol.m b/external/fieldtrip/plotting/ft_plot_vol.m index de2e65a7..4a6f4d2e 100644 --- a/external/fieldtrip/plotting/ft_plot_vol.m +++ b/external/fieldtrip/plotting/ft_plot_vol.m @@ -16,6 +16,7 @@ function ft_plot_vol(headmodel, varargin) % 'edgealpha' = transparency, between 0 and 1 (default = 1) % 'surfaceonly' = true or false, plot only the outer surface of a hexahedral or tetrahedral mesh (default = false) % 'unit' = string, convert to the specified geometrical units (default = []) +% 'grad' = gradiometer array, used in combination with local spheres model % % Example % headmodel = []; @@ -23,7 +24,7 @@ function ft_plot_vol(headmodel, varargin) % headmodel.o = [0 0 40]; % figure, ft_plot_vol(headmodel) % -% See also FT_PREPARE_HEADMODEL FT_PLOT_MESH +% See also FT_PREPARE_HEADMODEL, FT_PLOT_MESH, FT_PLOT_SENS % Copyright (C) 2009, Cristiano Micheli % @@ -60,9 +61,13 @@ function ft_plot_vol(headmodel, varargin) facealpha = ft_getopt(varargin, 'facealpha', 1); surfaceonly = ft_getopt(varargin, 'surfaceonly'); unit = ft_getopt(varargin, 'unit'); +grad = ft_getopt(varargin, 'grad'); if ~isempty(unit) headmodel = ft_convert_units(headmodel, unit); + if ~isempty(grad) + grad = ft_convert_units(grad, unit); + end end faceindex = istrue(faceindex); % yes=view the face number @@ -87,12 +92,19 @@ function ft_plot_vol(headmodel, varargin) end case 'localspheres' - bnd = repmat(struct(), numel(headmodel.label)); - for i=1:numel(headmodel.label) - bnd(i).pos(:,1) = pos(:,1)*headmodel.r(i) + headmodel.o(i,1); - bnd(i).pos(:,2) = pos(:,2)*headmodel.r(i) + headmodel.o(i,2); - bnd(i).pos(:,3) = pos(:,3)*headmodel.r(i) + headmodel.o(i,3); - bnd(i).tri = tri; + if ~isempty(grad) + ft_notice('estimating point on head surface for each gradiometer'); + [headmodel, grad] = ft_prepare_vol_sens(headmodel, grad); + [bnd.pos, bnd.tri] = headsurface(headmodel, grad); + else + ft_notice('plotting sphere for each gradiometer'); + bnd = repmat(struct(), numel(headmodel.label)); + for i=1:numel(headmodel.label) + bnd(i).pos(:,1) = pos(:,1)*headmodel.r(i) + headmodel.o(i,1); + bnd(i).pos(:,2) = pos(:,2)*headmodel.r(i) + headmodel.o(i,2); + bnd(i).pos(:,3) = pos(:,3)*headmodel.r(i) + headmodel.o(i,3); + bnd(i).tri = tri; + end end if isempty(edgecolor) edgecolor = 'none'; diff --git a/external/fieldtrip/plotting/ft_select_point3d.m b/external/fieldtrip/plotting/ft_select_point3d.m index a0eab2ef..aee32029 100644 --- a/external/fieldtrip/plotting/ft_select_point3d.m +++ b/external/fieldtrip/plotting/ft_select_point3d.m @@ -22,7 +22,7 @@ % camlight % ... do something here % -% See also FT_SELECT_BOX, FT_SELECT_CHANNEL, FT_SELECT_POINT, FT_SELECT_RANGE, FT_SELECT_VOXEL +% See also FT_SELECT_BOX, FT_SELECT_CHANNEL, FT_SELECT_POINT, FT_SELECT_RANGE, FT_SELECT_VOXEL % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -99,51 +99,59 @@ az = 0; el = 0; view(az,el); + while ~done k = waitforbuttonpress; - [p v vi facev facei] = select3d(h); + [p, v, vi, facev, facei] = select3d(h); if k == 1 %checks if waitforbuttonpress was a key - key = get(gcf,'CurrentCharacter'); % which key was pressed (if any)? - if strcmp(key, 'q') - % finished selecting points - done = true; - elseif strcmp(key, 'r') - % remove last point - if ~isempty(selected) - if ~isempty(marker) - delete(findobj('marker', '*')); - hs = plot3(selected(1:end-1,1), selected(1:end-1,2), selected(1:end-1,3), [markercolor marker]); - set(hs, 'MarkerSize', markersize); + key = get(gcf,'CurrentCharacter'); % which key was pressed (if any)? + if strcmp(key, 'q') + % finished selecting points + done = true; + elseif strcmp(key, 'r') + % remove last point + if ~isempty(selected) + if ~isempty(marker) + delete(findobj('marker', '*')); + hs = plot3(selected(1:end-1,1), selected(1:end-1,2), selected(1:end-1,3), [markercolor marker]); + set(hs, 'MarkerSize', markersize); + end + fprintf('removed latest point at [%f %f %f]\n', selected(end,1), selected(end,2), selected(end,3)); + selected = selected(1:end-1,:); end - fprintf('removed point at [%f %f %f]\n', selected(end,1), selected(end,2), selected(end,3)); - selected = selected(1:end-1,:); + elseif strcmp(key,'+') + zoom(1.1) + elseif strcmp(key,'-') + zoom(0.9) + elseif strcmp(key,'w') + az = az+6; + view(az,el) + elseif strcmp(key,'a') + el = el+6; + view(az,el) + elseif strcmp(key,'s') + az = az-6; + view(az,el) + elseif strcmp(key,'d') + el = el-6; + view(az,el) end - elseif strcmp(key,'+') - zoom(1.1) - elseif strcmp(key,'-') - zoom(0.9) - elseif strcmp(key,'w') - az = az+6; - view(az,el) - elseif strcmp(key,'a') - el = el+6; - view(az,el) - elseif strcmp(key,'s') - az = az-6; - view(az,el) - elseif strcmp(key,'d') - el = el-6; - view(az,el) - end - - else + + elseif ~isempty(p) % a new point was selected if nearest selected(end+1,:) = v; else selected(end+1,:) = p; end % if nearest - fprintf('selected point at [%f %f %f]\n', selected(end,1), selected(end,2), selected(end,3)); + + if multiple + fprintf('selected points at\n'); + disp(selected); + else + fprintf('selected point at [%f %f %f]\n', selected(end,1), selected(end,2), selected(end,3)); + end + if ~isempty(marker) hs = plot3(selected(end,1), selected(end,2), selected(end,3), [markercolor marker]); set(hs, 'MarkerSize', markersize); diff --git a/external/fieldtrip/plotting/ft_select_range.m b/external/fieldtrip/plotting/ft_select_range.m index 78f3d253..c1b7b073 100644 --- a/external/fieldtrip/plotting/ft_select_range.m +++ b/external/fieldtrip/plotting/ft_select_range.m @@ -128,7 +128,7 @@ function ft_select_range(handle, eventdata, varargin) % setup contextmenu if ~isempty(contextmenu) if isempty(get(handle,'uicontextmenu')) - hcmenu = uicontextmenu; + hcmenu = uicontextmenu(handle); hcmenuopt = nan(1,numel(contextmenu)); for icmenu = 1:numel(contextmenu) hcmenuopt(icmenu) = uimenu(hcmenu, 'label', contextmenu{icmenu}, 'callback', {@evalcontextcallback, callback{:}, []}); % empty matrix is placeholder, will be updated to userdata.range diff --git a/external/fieldtrip/plotting/private/channelposition.m b/external/fieldtrip/plotting/private/channelposition.m index 1eecb56a..2952c3f2 100644 --- a/external/fieldtrip/plotting/private/channelposition.m +++ b/external/fieldtrip/plotting/private/channelposition.m @@ -5,11 +5,11 @@ % % Use as % [pos, ori, lab] = channelposition(sens) -% where sens is an electrode or gradiometer array. +% where sens is an electrode, gradiometer or optode array. % % See also FT_DATATYPE_SENS -% Copyright (C) 2009-2012, Robert Oostenveld & Vladimir Litvak +% Copyright (C) 2009-2018, Robert Oostenveld & Vladimir Litvak % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -49,6 +49,8 @@ sens.coilori = nan(size(sens.coilpos)); elseif ~isfield(sens, 'coilori') && isfield(sens, 'elecpos') sens.coilori = nan(size(sens.elecpos)); +elseif ~isfield(sens, 'coilori') && isfield(sens, 'optopos') + sens.coilori = nan(size(sens.optopos)); end switch ft_senstype(sens) @@ -261,6 +263,8 @@ nelec = size(sens.elecpos,1); % these are the electrodes elseif isfield(sens, 'coilpos') ncoil = size(sens.coilpos,1); % these are the coils + elseif isfield(sens, 'optopos') + nopto = size(sens.optopos,1); % these are the optodes end if ~isfield(sens, 'tra') && isfield(sens, 'elecpos') && nchan==nelec @@ -286,11 +290,18 @@ pnt = sens.coilpos; ori = sens.coilori; lab = sens.label; + + elseif ~isfield(sens, 'tra') && isfield(sens, 'optopos') && nchan==nopto + % there is one optode per channel, which means that the channel position is identical to the coil position + pnt = sens.optopos; + ori = nan(size(pnt)); + lab = sens.label; elseif isfield(sens, 'tra') % each channel depends on multiple sensors (electrodes or coils), compute a weighted position for the channel % for MEG gradiometer channels this means that the position is in between the two coils % for bipolar EEG channels this means that the position is in between the two electrodes + % for NIRS channels this means that the position is in between the transmit and receive optode pnt = nan(nchan,3); ori = nan(nchan,3); if isfield(sens, 'coilpos') @@ -306,14 +317,18 @@ weight = weight ./ sum(weight); pnt(i,:) = weight * sens.elecpos; end + elseif isfield(sens, 'optopos') + for i=1:nchan + weight = abs(sens.tra(i,:)); + weight = weight ./ sum(weight); + pnt(i,:) = weight * sens.optopos; + end end lab = sens.label; - end - end % switch senstype -n = size(lab,2); +n = size(lab,2); % this is to fix the planar layouts, which cannot be plotted anyway if n>1 && size(lab, 1)>1 % this is to prevent confusion when lab happens to be a row array pnt = repmat(pnt, n, 1); diff --git a/external/fieldtrip/plotting/private/defaultId.m b/external/fieldtrip/plotting/private/defaultId.m index f4e1ee99..e7569675 100644 --- a/external/fieldtrip/plotting/private/defaultId.m +++ b/external/fieldtrip/plotting/private/defaultId.m @@ -40,7 +40,9 @@ % remove the non-FieldTrip functions from the path, these should not be part of the default message identifier keep = true(size(stack)); -[v, p] = ft_version; +p = fileparts(mfilename('fullpath')); +% strip away '/utilities/private' where this function is located +p = p(1:end-18); for i=1:numel(stack) keep(i) = strncmp(p, stack(i).file, length(p)); end diff --git a/external/fieldtrip/plotting/private/find_mesh_edge.m b/external/fieldtrip/plotting/private/find_mesh_edge.m new file mode 100644 index 00000000..d72a474b --- /dev/null +++ b/external/fieldtrip/plotting/private/find_mesh_edge.m @@ -0,0 +1,47 @@ +function [pnt, line] = find_mesh_edge(pnt, tri) + +% FIND_MESH_EDGE returns the edge of a triangulated mesh +% +% [pnt, line] = find_mesh_edge(pnt, tri), where +% +% pnt contains the vertex locations and +% line contains the indices of the linepieces connecting the vertices + +% Copyright (C) 2003, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +npnt = size(pnt,1); +ntri = size(tri,1); + +line = zeros(0,2); +nb = find_triangle_neighbours(pnt, tri); +edge = isnan(nb); +indx = any(edge, 2); +for i=find(indx)' + if isnan(nb(i,1)) + line(end+1, :) = tri(i,[1 2]); + end + if isnan(nb(i,2)) + line(end+1, :) = tri(i,[2 3]); + end + if isnan(nb(i,3)) + line(end+1, :) = tri(i,[3 1]); + end +end diff --git a/external/fieldtrip/plotting/private/find_triangle_neighbours.m b/external/fieldtrip/plotting/private/find_triangle_neighbours.m new file mode 100644 index 00000000..402b47da --- /dev/null +++ b/external/fieldtrip/plotting/private/find_triangle_neighbours.m @@ -0,0 +1,78 @@ +function [nb] = find_triangle_neighbours(pnt, tri) + +% FIND_TRIANGLE_NEIGHBOURS determines the three neighbours for each triangle +% in a mesh. It returns NaN's if the triangle does not have a neighbour on +% that particular side. +% +% [nb] = find_triangle_neighbours(pnt, tri) + +% Copyright (C) 2003, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +npnt = size(pnt,1); +ntri = size(tri,1); + +% each triangle has maximally three neighbours, assuming that the +% surface mesh is not degenerate +nb = nan(size(tri)); + +% for i=1:ntri +% for j=setdiff(1:ntri, i) +% if length(intersect(tri(i,[1 2]), tri(j,:)))==2 +% nb(i,1) = j; +% continue; +% end +% if length(intersect(tri(i,[2 3]), tri(j,:)))==2 +% nb(i,2) = j; +% continue; +% end +% if length(intersect(tri(i,[3 1]), tri(j,:)))==2 +% nb(i,3) = j; +% continue; +% end +% end +% if all(~isnan(nb(i,:))) +% continue; +% end +% end + +for i=1:ntri + % find all neighbouring triangles + tmp1 = (tri==tri(i,1)); + tmp2 = (tri==tri(i,2)); + tmp3 = (tri==tri(i,3)); + tmp = (tmp1|tmp2|tmp3); + sel = find(sum(tmp,2)==2); + + % ensure that each neighbour is assigned to the proper edge + if length(sel)>3 + ft_error('more than three neighbours found for triangle %d', i); + else + for j=1:length(sel) + if isempty(setdiff(intersect(tri(i,:), tri(sel(j),:)), tri(i,[1 2]))) + nb(i,1) = sel(j); + elseif isempty(setdiff(intersect(tri(i,:), tri(sel(j),:)), tri(i,[2 3]))) + nb(i,2) = sel(j); + elseif isempty(setdiff(intersect(tri(i,:), tri(sel(j),:)), tri(i,[3 1]))) + nb(i,3) = sel(j); + end + end + end +end diff --git a/external/fieldtrip/plotting/private/fitsphere.m b/external/fieldtrip/plotting/private/fitsphere.m new file mode 100644 index 00000000..e0881de1 --- /dev/null +++ b/external/fieldtrip/plotting/private/fitsphere.m @@ -0,0 +1,93 @@ +function [C,R] = fitsphere(pnt) + +% FITSPHERE fits the centre and radius of a sphere to a set of points +% using Taubin's method. +% +% Use as +% [center,radius] = fitsphere(pnt) +% where +% pnt = Nx3 matrix with the Carthesian coordinates of the surface points +% and +% center = the center of the fitted sphere +% radius = the radius of the fitted sphere + +% Copyright (C) 2009, Jean Daunizeau (for SPM) +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +x = pnt(:,1); +y = pnt(:,2); +z = pnt(:,3); + +% Make sugary one and zero vectors +l = ones(length(x),1); +O = zeros(length(x),1); + +% Make design mx +D = [(x.*x + y.*y + z.*z) x y z l]; + +Dx = [2*x l O O O]; +Dy = [2*y O l O O]; +Dz = [2*z O O l O]; + +% Create scatter matrices +M = D'*D; +N = Dx'*Dx + Dy'*Dy + Dz'*Dz; + +% Extract eigensystem +[v, evalues] = eig(M); +evalues = diag(evalues); +Mrank = sum(evalues > eps*5*norm(M)); + +if (Mrank == 5) + % Full rank -- min ev corresponds to solution + % Minverse = v'*diag(1./evalues)*v; + [v,evalues] = eig(inv(M)*N); + [dmin,dminindex] = max(diag(evalues)); + pvec = v(:,dminindex(1))'; +else + % Rank deficient -- just extract nullspace of M + % pvec = null(M)'; % this does not work reliably because of inconsistent rank definition + pvec = v(:,evalues <= eps*5*norm(M))'; + [m,n] = size(pvec); + if m > 1 + pvec = pvec(1,:); + end +end + +if isempty(pvec) + ft_warning('was not able to fit a sphere to the surface points'); + C = [NaN NaN NaN]; + R = Inf; +else + % Convert to (R,C) + C = -0.5*pvec(2:4) / pvec(1); + R = sqrt(sum(C*C') - pvec(5)/pvec(1)); +end + + +% if nargout == 1, +% if pvec(1) < 0 +% pvec = -pvec; +% end +% C = pvec; +% else +% C = -0.5*pvec(2:4) / pvec(1); +% R = sqrt(sum(C*C') - pvec(5)/pvec(1)); +% end diff --git a/external/fieldtrip/plotting/private/ft_apply_montage.m b/external/fieldtrip/plotting/private/ft_apply_montage.m index 5262952b..63f9cab0 100644 --- a/external/fieldtrip/plotting/private/ft_apply_montage.m +++ b/external/fieldtrip/plotting/private/ft_apply_montage.m @@ -39,6 +39,7 @@ % 'balancename' string, name of the montage (default = '') % 'feedback' string, see FT_PROGRESS (default = 'text') % 'warning' boolean, whether to show warnings (default = true) +% 'showcallinfo' string, 'yes' or 'no' (default = 'no') % % If the first input is a montage, then the second input montage will be % applied to the first. In effect, the output montage will first do @@ -79,11 +80,12 @@ input = fixoldorg(input); % the input might be a montage or a sensor array % get optional input arguments -keepunused = ft_getopt(varargin, 'keepunused', 'no'); -inverse = ft_getopt(varargin, 'inverse', 'no'); -feedback = ft_getopt(varargin, 'feedback', 'text'); -showwarning = ft_getopt(varargin, 'warning', true); -bname = ft_getopt(varargin, 'balancename', ''); +keepunused = ft_getopt(varargin, 'keepunused', 'no'); +inverse = ft_getopt(varargin, 'inverse', 'no'); +feedback = ft_getopt(varargin, 'feedback', 'text'); +showwarning = ft_getopt(varargin, 'warning', true); +bname = ft_getopt(varargin, 'balancename', ''); +showcallinfo = ft_getopt(varargin, 'showcallinfo', 'no'); if istrue(showwarning) warningfun = @warning; @@ -254,6 +256,7 @@ if k > 0 && isfield(input, 'trial') % check for raw data now only cfg = []; cfg.channel = addlabel; + cfg.showcallinfo = showcallinfo; data_unused = ft_selectdata(cfg, input); % use an anonymous function to test for the presence of NaNs in the input data hasnan = @(x) any(isnan(x(:))); diff --git a/external/fieldtrip/plotting/private/ft_convert_units.m b/external/fieldtrip/plotting/private/ft_convert_units.m index 47279027..b1ac7b89 100644 --- a/external/fieldtrip/plotting/private/ft_convert_units.m +++ b/external/fieldtrip/plotting/private/ft_convert_units.m @@ -46,9 +46,6 @@ % 2) determine the requested scaling factor to obtain the output units % 3) try to apply the scaling to the known geometrical elements in the input object -% ensure the correct number of input and output arguments -% narginchk(2,inf); % see below -% nargoutchk(0,1); % the "target" input argument has been made required in Aug 2017 % prior to that it was also possible to use this function to estimate units diff --git a/external/fieldtrip/plotting/private/ft_datatype_sens.m b/external/fieldtrip/plotting/private/ft_datatype_sens.m index a3c1d2ec..b0c304be 100644 --- a/external/fieldtrip/plotting/private/ft_datatype_sens.m +++ b/external/fieldtrip/plotting/private/ft_datatype_sens.m @@ -161,7 +161,8 @@ sens = fixoldorg(sens, false); % ensure that all numbers are represented in double precision - sens = ft_struct2double(sens); + % this only affects the top-level fields and does not recurse + sens = ft_struct2double(sens, 1); % in version 2011v2 this was optional, now it is required if ~isfield(sens, 'chantype') || all(strcmp(sens.chantype, 'unknown')) diff --git a/external/fieldtrip/plotting/private/ft_datatype_volume.m b/external/fieldtrip/plotting/private/ft_datatype_volume.m index eb99e9ff..af2ac4a0 100644 --- a/external/fieldtrip/plotting/private/ft_datatype_volume.m +++ b/external/fieldtrip/plotting/private/ft_datatype_volume.m @@ -108,6 +108,16 @@ switch version case '2014' %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if isfield(volume, 'coordsys') + % ensure that it is in lower case + volume.coordsys = lower(volume.coordsys); + end + + if isfield(volume, 'unit') + % ensure that it is in lower case + volume.unit = lower(volume.unit); + end + if isfield(volume, 'dimord') volume = rmfield(volume, 'dimord'); end diff --git a/external/fieldtrip/plotting/private/ft_determine_units.m b/external/fieldtrip/plotting/private/ft_determine_units.m index 8c5864a3..37a49ff5 100644 --- a/external/fieldtrip/plotting/private/ft_determine_units.m +++ b/external/fieldtrip/plotting/private/ft_determine_units.m @@ -38,9 +38,6 @@ % % $Id$ -% ensure the correct number of input and output arguments -% narginchk(1,1); -% nargoutchk(0,1); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/plotting/private/ft_getopt.m b/external/fieldtrip/plotting/private/ft_getopt.m index 0d433115..5f2f387b 100644 --- a/external/fieldtrip/plotting/private/ft_getopt.m +++ b/external/fieldtrip/plotting/private/ft_getopt.m @@ -9,7 +9,7 @@ % s = structure or cell-array % key = string % default = any valid MATLAB data type (optional, default = []) -% emptymeaningful = boolean value (optional, default = 0) +% emptymeaningful = boolean value (optional, default = false) % % If the key is present as field in the structure, or as key-value pair in the % cell-array, the corresponding value will be returned. @@ -49,7 +49,7 @@ end if nargin < 4 - emptymeaningful = 0; + emptymeaningful = false; end if isa(opt, 'struct') || isa(opt, 'config') diff --git a/external/fieldtrip/plotting/private/ft_notification.m b/external/fieldtrip/plotting/private/ft_notification.m index dc9b7369..a74c4052 100644 --- a/external/fieldtrip/plotting/private/ft_notification.m +++ b/external/fieldtrip/plotting/private/ft_notification.m @@ -337,7 +337,7 @@ ft_default.notification.(level) = s; % the remainder is fully handled by the ERROR function if ~isempty(msgId) - error(msgId, varargin{:}); + error(state); else error(varargin{:}); end diff --git a/external/fieldtrip/plotting/private/ft_platform_supports.m b/external/fieldtrip/plotting/private/ft_platform_supports.m index c0676b62..34639722 100644 --- a/external/fieldtrip/plotting/private/ft_platform_supports.m +++ b/external/fieldtrip/plotting/private/ft_platform_supports.m @@ -41,6 +41,28 @@ % % See also FT_VERSION, VERSION, VER, VERLESSTHAN +% Copyright (C) 2006, Robert Oostenveld +% Copyright (C) 2010, Eelke Spaak +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + if ~ischar(what) error('first argument must be a string'); end @@ -48,6 +70,9 @@ switch what case 'matlabversion' tf = is_matlab() && matlabversion(varargin{:}); + + case 'octaveversion' + tf = is_octave() && octaveversion(varargin{:}); case 'exists-in-private-directory' tf = is_matlab(); @@ -206,11 +231,31 @@ end % function + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function [inInterval] = matlabversion(min, max) +function [inInterval] = octaveversion(min, max) +% the version does not change, making it persistent speeds up the subsequent calls +persistent curVer + +if nargin<2 + max = min; +end +if isempty(curVer) + curVer = OCTAVE_VERSION; +end + +% perform comparison with respect to version number +[major, minor] = parseMatlabVersion(curVer); +[minMajor, minMinor] = parseMatlabVersion(min); +[maxMajor, maxMinor] = parseMatlabVersion(max); + +inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); + +end % function + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MATLABVERSION checks if the current MATLAB version is within the interval % specified by min and max. % @@ -229,27 +274,8 @@ % etc. % % See also VERSION, VER, VERLESSTHAN - -% Copyright (C) 2006, Robert Oostenveld -% Copyright (C) 2010, Eelke Spaak -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [inInterval] = matlabversion(min, max) % the version does not change, making it persistent speeds up the subsequent calls persistent curVer @@ -311,8 +337,8 @@ minor = int8((ver - floor(ver)) * 10); else % ver is string (e.g. '7.10'), parse accordingly [major, rest] = strtok(ver, '.'); - major = str2num(major); - minor = str2num(strtok(rest, '.')); + major = str2double(major); + minor = str2double(strtok(rest, '.')); end end % function diff --git a/external/fieldtrip/plotting/private/ft_senstype.m b/external/fieldtrip/plotting/private/ft_senstype.m index b6753338..09712abe 100644 --- a/external/fieldtrip/plotting/private/ft_senstype.m +++ b/external/fieldtrip/plotting/private/ft_senstype.m @@ -156,7 +156,6 @@ isnirs = isa(input, 'struct') && isfield(input, 'label') && isfield(input, 'transceiver'); islabel = isa(input, 'cell') && ~isempty(input) && isa(input{1}, 'char'); haslabel = isa(input, 'struct') && isfield(input, 'label'); -haschantype = isa(input, 'struct') && isfield(input, 'chantype'); if ~(isdata || isheader || isgrad || iselec || isnirs || islabel || haslabel) && isfield(input, 'hdr') input = input.hdr; @@ -228,6 +227,7 @@ sens = []; end +haschantype = isfield(sens, 'chantype'); if isfield(sens, 'type') % preferably the structure specifies its own type diff --git a/external/fieldtrip/plotting/private/headsurface.m b/external/fieldtrip/plotting/private/headsurface.m new file mode 100644 index 00000000..1e5677d3 --- /dev/null +++ b/external/fieldtrip/plotting/private/headsurface.m @@ -0,0 +1,219 @@ +function [pos, tri] = headsurface(headmodel, sens, varargin) + +% HEADSURFACE constructs a triangulated description of the skin or brain +% surface from a volume conduction model, from a set of electrodes or +% gradiometers, or from a combination of the two. It returns a closed +% surface. +% +% Use as +% [pos, tri] = headsurface(headmodel, sens, ...) +% where +% headmodel = volume conduction model (structure) +% sens = electrode or gradiometer array (structure) +% +% Optional arguments should be specified in key-value pairs: +% surface = 'skin' or 'brain' (default = 'skin') +% npos = number of vertices (default is determined automatic) +% downwardshift = boolean, this will shift the lower rim of the helmet down with approximately 1/4th of its radius (default is 1) +% inwardshift = number (default = 0) +% headshape = string, file containing the head shape + +% Copyright (C) 2005-2006, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +if nargin<1 + headmodel = []; +end + +if nargin<2 + sens = []; +end + +if ~isempty(sens) + sens = ft_datatype_sens(sens); +end + +if nargin<3 + varargin = {}; +end + +% parse the optional input arguments +surface = ft_getopt(varargin, 'surface', 'skin'); % skin or brain +downwardshift = ft_getopt(varargin, 'downwardshift', true); % boolean +inwardshift = ft_getopt(varargin, 'inwardshift'); % number +headshape = ft_getopt(varargin, 'headshape'); % CTF *.shape file +npos = ft_getopt(varargin, 'npos'); % number of vertices + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if ~isempty(headshape) + % the headshape should be specified as a surface structure with pos and tri + pos = headshape.pos; + tri = headshape.tri; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif ~isempty(headmodel) && isfield(headmodel, 'r') && length(headmodel.r)<5 + if length(headmodel.r)==1 + % single sphere model, cannot distinguish between skin and/or brain + radius = headmodel.r; + if isfield(headmodel, 'o') + origin = headmodel.o; + else + origin = [0 0 0]; + end + elseif length(headmodel.r)<5 + % multiple concentric sphere model + switch surface + case 'skin' + % using outermost sphere + radius = max(headmodel.r); + case 'brain' + % using innermost sphere + radius = min(headmodel.r); + otherwise + ft_error('other surfaces cannot be constructed this way'); + end + if isfield(headmodel, 'o') + origin = headmodel.o; + else + origin = [0 0 0]; + end + end + % this requires a specification of the number of vertices + if isempty(npos) + npos = 642; + end + % construct an evenly tesselated unit sphere + switch npos + case 2562 + [pos, tri] = icosahedron2562; + case 642 + [pos, tri] = icosahedron642; + case 162 + [pos, tri] = icosahedron162; + case 42 + [pos, tri] = icosahedron42; + case 12 + [pos, tri] = icosahedron; + otherwise + [pos, tri] = ksphere(npos); + end + % scale and translate the vertices + pos = pos*radius; + pos(:,1) = pos(:,1) + origin(1); + pos(:,2) = pos(:,2) + origin(2); + pos(:,3) = pos(:,3) + origin(3); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif ft_voltype(headmodel, 'localspheres') + % local spheres MEG model, this also requires a gradiometer structure + grad = sens; + if ~isfield(grad, 'tra') || ~isfield(grad, 'coilpos') + ft_error('incorrect specification for the gradiometer array'); + end + Nchans = size(grad.tra, 1); + Ncoils = size(grad.tra, 2); + Nspheres = size(headmodel.o, 1); + if Nspheres~=Ncoils + ft_error('there should be just as many spheres as coils'); + end + % for each coil, determine a surface point using the corresponding sphere + vec = grad.coilpos - headmodel.o; + nrm = sqrt(sum(vec.^2,2)); + vec = vec ./ [nrm nrm nrm]; + pos = headmodel.o + vec .* [headmodel.r headmodel.r headmodel.r]; + pos = unique(pos, 'rows'); + % make a triangularization that is needed to find the rim of the helmet + prj = elproj(pos); + tri = delaunay(prj(:,1),prj(:,2)); + % find the lower rim of the helmet shape + [pos, line] = find_mesh_edge(pos, tri); + edgeind = unique(line(:)); + % shift the lower rim of the helmet shape down with approximately 1/4th of its radius + if downwardshift + % determine the extent of the volume conduction model + dist = mean(sqrt(sum((pos - repmat(mean(pos,1), size(pos,1), 1)).^2, 2))); + dist = dist/4; + pos(edgeind,3) = pos(edgeind,3) - dist; + end + % construct the triangulation of the surface + tri = projecttri(pos); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +elseif ft_voltype(headmodel, 'bem') || ft_voltype(headmodel, 'singleshell') + % volume conduction model with triangulated boundaries + switch surface + case 'skin' + if ~isfield(headmodel, 'skin') + headmodel.skin = find_outermost_boundary(headmodel.bnd); + end + pos = headmodel.bnd(headmodel.skin).pos; + tri = headmodel.bnd(headmodel.skin).tri; + case 'brain' + if ~isfield(headmodel, 'source') + headmodel.source = find_innermost_boundary(headmodel.bnd); + end + pos = headmodel.bnd(headmodel.source).pos; + tri = headmodel.bnd(headmodel.source).tri; + otherwise + ft_error('other surfaces cannot be constructed this way'); + end +end + +% retriangulate the skin/brain/cortex surface to the desired number of vertices +if ~isempty(npos) && size(pos,1)~=npos + switch npos + case 2562 + [pnt2, tri2] = icosahedron2562; + case 642 + [pnt2, tri2] = icosahedron642; + case 162 + [pnt2, tri2] = icosahedron162; + case 42 + [pnt2, tri2] = icosahedron42; + case 12 + [pnt2, tri2] = icosahedron; + otherwise + [pnt2, tri2] = ksphere(npos); + end + [pos, tri] = retriangulate(pos, tri, pnt2, tri2, 2); +end + +% shift the surface inward with a certain amount +if ~isempty(inwardshift) && inwardshift~=0 + ori = normals(pos, tri, 'vertex'); + % FIXME in case of a icosahedron projected onto a localspheres model, the + % surfaceorientation for the lower rim points fails, causing problems + % with the inward shift + tmp = surfaceorientation(pos, tri, ori); + % the orientation of the normals should be pointing to the outside of the surface + if tmp==1 + % the normals are outward oriented + % nothing to do + elseif tmp==-1 + % the normals are inward oriented + tri = fliplr(tri); + ori = -ori; + else + ft_warning('cannot determine the orientation of the vertex normals'); + % nothing to do + end + % the orientation is outward, hence shift with a negative amount + pos = pos - inwardshift * ori; +end diff --git a/external/fieldtrip/plotting/private/icosahedron.m b/external/fieldtrip/plotting/private/icosahedron.m index b6d65c34..663cb7d5 100644 --- a/external/fieldtrip/plotting/private/icosahedron.m +++ b/external/fieldtrip/plotting/private/icosahedron.m @@ -1,11 +1,11 @@ -function [pos, tri] = icosahedron() +function [pos, tri] = icosahedron(varargin) -% ICOSAHEDRON creates an icosahedron +% ICOSAHEDRON creates an icosahedron with 12 vertices and 20 triangles % -% [pos, tri] = icosahedron -% creates an icosahedron with 12 vertices and 20 triangles -% -% See also OCTAHEDRON, ICOSAHEDRON42, ICOSAHEDRON162, ICOSAHEDRON642, ICOSAHEDRON2562 +% Use as +% [pos, tri] = icosahedron +% +% See also TETRAHEDRON, OCTAHEDRON, ICOSAHEDRON42, ICOSAHEDRON162, ICOSAHEDRON642, ICOSAHEDRON2562 % Copyright (C) 2002, Robert Oostenveld % @@ -27,27 +27,27 @@ % tri = [ - 1 2 3 - 1 3 4 - 1 4 5 - 1 5 6 - 1 6 2 - 2 8 3 - 3 9 4 - 4 10 5 - 5 11 6 - 6 7 2 - 7 8 2 - 8 9 3 - 9 10 4 - 10 11 5 - 11 7 6 + 1 2 3 + 1 3 4 + 1 4 5 + 1 5 6 + 1 6 2 + 2 8 3 + 3 9 4 + 4 10 5 + 5 11 6 + 6 7 2 + 7 8 2 + 8 9 3 + 9 10 4 + 10 11 5 + 11 7 6 12 8 7 12 9 8 12 10 9 12 11 10 12 7 11 -]; + ]; pos = zeros(12, 3); @@ -66,3 +66,12 @@ pos(12, :) = [0 0 -1]; % bottom point +if nargin>0 + n = varargin{1}; + % perform an n-fold refinement + for i=1:n + [pos, tri] = refine(pos, tri); + % scale all vertices to the unit sphere + pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); + end +end diff --git a/external/fieldtrip/plotting/private/icosahedron162.m b/external/fieldtrip/plotting/private/icosahedron162.m index ae3bfe69..457969da 100644 --- a/external/fieldtrip/plotting/private/icosahedron162.m +++ b/external/fieldtrip/plotting/private/icosahedron162.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron162() -% ICOSAHEDRON162 creates a 2-fold refined icosahedron +% ICOSAHEDRON162 creates a 2-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron162; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,9 +26,6 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); - -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +[pos, tri] = icosahedron(2); diff --git a/external/fieldtrip/plotting/private/icosahedron2562.m b/external/fieldtrip/plotting/private/icosahedron2562.m index 11752a0c..c9164f12 100644 --- a/external/fieldtrip/plotting/private/icosahedron2562.m +++ b/external/fieldtrip/plotting/private/icosahedron2562.m @@ -1,6 +1,12 @@ -function [pnt, tri] = icosahedron2562() +function [pos, tri] = icosahedron2562() -% ICOSAHEDRON2562 creates a 4-fold refined icosahedron +% ICOSAHEDRON2562 creates a 4-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron2562; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -22,10 +28,4 @@ % % $Id$ -[pnt, tri] = icosahedron; -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); - -pnt = pnt ./ repmat(sqrt(sum(pnt.^2,2)), 1,3); +[pos, tri] = icosahedron(4); diff --git a/external/fieldtrip/plotting/private/icosahedron42.m b/external/fieldtrip/plotting/private/icosahedron42.m index 833c11eb..3f8ccae0 100644 --- a/external/fieldtrip/plotting/private/icosahedron42.m +++ b/external/fieldtrip/plotting/private/icosahedron42.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron42() -% ICOSAHEDRON42 creates a 1-fold refined icosahedron +% ICOSAHEDRON42 creates a 1-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron42; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,8 +26,6 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); - -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +[pos, tri] = icosahedron(1); diff --git a/external/fieldtrip/plotting/private/icosahedron642.m b/external/fieldtrip/plotting/private/icosahedron642.m index 4d932e5c..76e0d149 100644 --- a/external/fieldtrip/plotting/private/icosahedron642.m +++ b/external/fieldtrip/plotting/private/icosahedron642.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron642() -% ICOSAHEDRON642 creates a 3-fold refined icosahedron +% ICOSAHEDRON642 creates a 3-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron642; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,10 +26,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); +[pos, tri] = icosahedron(3); -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); diff --git a/external/fieldtrip/plotting/private/octahedron.m b/external/fieldtrip/plotting/private/octahedron.m new file mode 100644 index 00000000..2e9eb8f8 --- /dev/null +++ b/external/fieldtrip/plotting/private/octahedron.m @@ -0,0 +1,39 @@ +function [pos, tri] = octahedron(varargin) + +% OCTAHEDRON +% +% Use as +% [pos tri] = octahedron; +% +% See also TETRAHEDRON ICOSAHEDRON + + +pos = [ + 0 0 +1 + +1 0 0 + 0 +1 0 + -1 0 0 + 0 -1 0 + 0 0 -1 + ]; + +tri = [ + 1 2 3 + 1 3 4 + 1 4 5 + 1 5 2 + 6 3 2 + 6 4 3 + 6 5 4 + 6 2 5 + ]; + +if nargin>0 + n = varargin{1}; + % perform an n-fold refinement + for i=1:n + [pos, tri] = refine(pos, tri, 'banks'); + end + % scale all vertices to the unit sphere + pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +end \ No newline at end of file diff --git a/external/fieldtrip/plotting/private/quaternion.m b/external/fieldtrip/plotting/private/quaternion.m index bd34c4f6..d77b70d4 100644 --- a/external/fieldtrip/plotting/private/quaternion.m +++ b/external/fieldtrip/plotting/private/quaternion.m @@ -9,11 +9,12 @@ % Q [q0, q1, q2, q3, q4, q5, q6] vector with parameters % H corresponding homogenous transformation matrix % -% See Elekta/Neuromag MaxFilter manual version 2.2, section "D2 Coordinate Matching", -% page 77 for more details and +% If the input vector has length 6, it is assumed to represent a unit quaternion without scaling. +% +% See Elekta/Neuromag MaxFilter manual version 2.2, section "D2 Coordinate Matching", page 77 for more details and % https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Conversion_to_and_from_the_matrix_representation -% Copyright (C) 2016, Robert Oostenveld +% Copyright (C) 2016-2017, Robert Oostenveld % % This program is free software; you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by @@ -47,6 +48,13 @@ % % $Id$ +if numel(q)==6 + % this is used a lot by the Elekta software, where the first element is left out and a rigid body transformation wothout scaling is used. + % see also https://github.com/mne-tools/mne-python/blob/maint/0.15/mne/transforms.py#L1137 + q0 = sqrt(1 - q(1)^2 - q(2)^2 - q(3)^2); + q = [q0 q]; +end + if numel(q)~=7 ft_error('incorrect input vector'); end diff --git a/external/fieldtrip/plotting/private/refine.m b/external/fieldtrip/plotting/private/refine.m index 9dc242d4..075e6c05 100644 --- a/external/fieldtrip/plotting/private/refine.m +++ b/external/fieldtrip/plotting/private/refine.m @@ -1,12 +1,12 @@ -function [pntr, trir, texr] = refine(pnt, tri, method, varargin) +function [posr, trir, texr] = refine(pos, tri, method, varargin) % REFINE a 3D surface that is described by a triangulation % % Use as -% [pnt, tri] = refine(pnt, tri) -% [pnt, tri] = refine(pnt, tri, 'banks') -% [pnt, tri, texture] = refine(pnt, tri, 'banks', texture) -% [pnt, tri] = refine(pnt, tri, 'updown', numtri) +% [pos, tri] = refine(pos, tri) +% [pos, tri] = refine(pos, tri, 'banks') +% [pos, tri, texture] = refine(pos, tri, 'banks', texture) +% [pos, tri] = refine(pos, tri, 'updown', numtri) % % If no method is specified, the default is to refine the mesh globally by bisecting % each edge according to the algorithm described in Banks, 1983. @@ -65,7 +65,7 @@ switch lower(method) case 'banks' if ~isempty(texture) - npnt = size(pnt,1); + npnt = size(pos,1); ntri = size(tri,1); ntex = size(texture,1); @@ -73,9 +73,9 @@ insert = spalloc(3*npnt,3*npnt,3*ntri); trir = zeros(4*ntri,3); % allocate memory for the new triangles - pntr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices + posr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices texr = zeros(ntex+3*ntri,2); - pntr(1:npnt,:) = pnt; % insert the original vertices + posr(1:npnt,:) = pos; % insert the original vertices texr(1:ntex,:) = texture; current = npnt; @@ -83,7 +83,7 @@ if ~insert(tri(i,1),tri(i,2)) current = current + 1; - pntr(current,:) = (pnt(tri(i,1),:) + pnt(tri(i,2),:))/2; + posr(current,:) = (pos(tri(i,1),:) + pos(tri(i,2),:))/2; texr(current,:) = (texture(tri(i,1),:) + texture(tri(i,2),:))/2; insert(tri(i,1),tri(i,2)) = current; insert(tri(i,2),tri(i,1)) = current; @@ -94,7 +94,7 @@ if ~insert(tri(i,2),tri(i,3)) current = current + 1; - pntr(current,:) = (pnt(tri(i,2),:) + pnt(tri(i,3),:))/2; + posr(current,:) = (pos(tri(i,2),:) + pos(tri(i,3),:))/2; texr(current,:) = (texture(tri(i,2),:) + texture(tri(i,3),:))/2; insert(tri(i,2),tri(i,3)) = current; insert(tri(i,3),tri(i,2)) = current; @@ -105,7 +105,7 @@ if ~insert(tri(i,3),tri(i,1)) current = current + 1; - pntr(current,:) = (pnt(tri(i,3),:) + pnt(tri(i,1),:))/2; + posr(current,:) = (pos(tri(i,3),:) + pos(tri(i,1),:))/2; texr(current,:) = (texture(tri(i,3),:) + texture(tri(i,1),:))/2; insert(tri(i,3),tri(i,1)) = current; insert(tri(i,1),tri(i,3)) = current; @@ -121,26 +121,26 @@ trir(4*(i-1)+4, :) = [v12 v23 v31]; end - pntr = pntr(1:current, :); + posr = posr(1:current, :); texr = texr(1:current, :); else % there is no texture - npnt = size(pnt,1); + npnt = size(pos,1); ntri = size(tri,1); insert = spalloc(3*npnt,3*npnt,3*ntri); trir = zeros(4*ntri,3); % allocate memory for the new triangles - pntr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices - pntr(1:npnt,:) = pnt; % insert the original vertices + posr = zeros(npnt+3*ntri,3); % allocate memory for the maximum number of new vertices + posr(1:npnt,:) = pos; % insert the original vertices current = npnt; for i=1:ntri if ~insert(tri(i,1),tri(i,2)) current = current + 1; - pntr(current,:) = (pnt(tri(i,1),:) + pnt(tri(i,2),:))/2; + posr(current,:) = (pos(tri(i,1),:) + pos(tri(i,2),:))/2; insert(tri(i,1),tri(i,2)) = current; insert(tri(i,2),tri(i,1)) = current; v12 = current; @@ -150,7 +150,7 @@ if ~insert(tri(i,2),tri(i,3)) current = current + 1; - pntr(current,:) = (pnt(tri(i,2),:) + pnt(tri(i,3),:))/2; + posr(current,:) = (pos(tri(i,2),:) + pos(tri(i,3),:))/2; insert(tri(i,2),tri(i,3)) = current; insert(tri(i,3),tri(i,2)) = current; v23 = current; @@ -160,7 +160,7 @@ if ~insert(tri(i,3),tri(i,1)) current = current + 1; - pntr(current,:) = (pnt(tri(i,3),:) + pnt(tri(i,1),:))/2; + posr(current,:) = (pos(tri(i,3),:) + pos(tri(i,1),:))/2; insert(tri(i,3),tri(i,1)) = current; insert(tri(i,1),tri(i,3)) = current; v31 = current; @@ -175,18 +175,18 @@ trir(4*(i-1)+4, :) = [v12 v23 v31]; end % remove the space for the vertices that was not used - pntr = pntr(1:current, :); + posr = posr(1:current, :); end case 'updown' ntri = size(tri,1); while ntri. + + +v1 = [ 0, 0, 1 ]; +v2 = [ sqrt(8/9), 0, -1/3 ]; +v3 = [ -sqrt(2/9), sqrt(2/3), -1/3 ]; +v4 = [ -sqrt(2/9), -sqrt(2/3), -1/3 ]; + +pos = [ + v1 + v2 + v3 + v4 + ]; + +tri = [ + 1 2 3 + 1 3 4 + 1 4 2 + 2 4 3 + ]; + +if nargin>0 + n = varargin{1}; + % perform an n-fold refinement + for i=1:n + [pos, tri] = refine(pos, tri, 'banks'); + end + % scale all vertices to the unit sphere + pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +end + + diff --git a/external/fieldtrip/preproc/README b/external/fieldtrip/preproc/README index 143b486f..1523be28 100644 --- a/external/fieldtrip/preproc/README +++ b/external/fieldtrip/preproc/README @@ -1,16 +1,26 @@ This is the FieldTrip PREPROC module. It contains functions for the -preprocessing of EEG and MEG data, such as filtering, baseline -correction and re-referencing. +preprocessing of MEG, EEG and other time-series data, such as filtering, +baseline correction and re-referencing. -For more information please visit http://www.ru.nl/fcdonders/fieldtrip. - -The FieldTrip software is free but copyrighted software, distributed -under the terms of the GNU General Public Licence as published by -the Free Software Foundation (either version 2, or at your option -any later version). See the file COPYING for more details. +For more information please visit http://www.fieldtriptoolbox.org/modules/preproc +------------------------------------------------------------------------------- Copyright (C) 2008-2009, Donders Institute for Brain, Cognition and Behaviour, The Netherlands (DCCN) Copyright (C) 2003-2008, F.C. Donders Centre for Cognitive Neuroimaging, Nijmegen, The Netherlands (FCDC) Copyright (C) 2003-2005, Center for Sensory-Motor Interaction, Aalborg University, Denmark (SMI) Copyright (C) 1999-2003, Department of Medical Physics, Radboud University Nijmegen, The Netherlands (MBFYS) +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see begsample % this will be a linear piecewise function consisting of two pieces taking turns - endsample = begsample + min(prepadlength-endsample, nsamples-1); - padbeg(end-endsample+2:end-begsample+1) = fliplr(mod(0:(endsample-begsample-1), nsamples)+2); - begsample = endsample-1; - - if prepadlength > begsample - endsample = begsample + min(prepadlength-endsample+1, nsamples-1); - padbeg(end-endsample+1:end-begsample+1) = mod(0:(endsample-begsample), nsamples)+nsamples-endsample+begsample; - begsample = endsample+1; - end + % create an indexvector to index the data with + index = (1:(prepadlength+nsamples+postpadlength))-prepadlength; + while any(index<1|index>nsamples) + index(index<1) = -index(index<1) + 1; + index(index>nsamples) = 2.*nsamples - index(index>nsamples) + 1; end - - % postdata padding - begsample = 1; - endsample = 0; - while postpadlength > begsample % this will be a linear piecewise function consisting of two pieces taking turns - endsample = begsample + min(postpadlength-endsample, nsamples-1); - padend(begsample:endsample-1) = fliplr(mod(0:(endsample-begsample-1), nsamples)+nsamples-endsample+begsample); - begsample = endsample-1; - - if postpadlength > begsample - endsample = begsample + min(postpadlength-endsample+1, nsamples-1); - padend(begsample:endsample) = mod(0:(endsample-begsample), nsamples)+1; - begsample = endsample+1; - end - end - - dat = [dat(:, padbeg) dat dat(:, padend)]; + dat = dat(:, index); case 'edge' dat = [dat(:,1)*ones(1,prepadlength) dat dat(:,end)*ones(1,postpadlength)]; diff --git a/external/fieldtrip/preproc/ft_preproc_polyremoval.m b/external/fieldtrip/preproc/ft_preproc_polyremoval.m index b16bb60e..d0811f1b 100644 --- a/external/fieldtrip/preproc/ft_preproc_polyremoval.m +++ b/external/fieldtrip/preproc/ft_preproc_polyremoval.m @@ -114,7 +114,7 @@ % estimate the contribution of the basis functions % beta = dat(:,begsample:endsample)/x(:,begsample:endsample); <-this leads to numerical issues, even in simple examples - invxcov = inv(x(:,usesamples)*x(:,usesamples)'); + invxcov = pinv(x(:,usesamples)*x(:,usesamples)'); beta = dat(:,usesamples)*x(:,usesamples)'*invxcov; end diff --git a/external/fieldtrip/preproc/private/defaultId.m b/external/fieldtrip/preproc/private/defaultId.m index f4e1ee99..e7569675 100644 --- a/external/fieldtrip/preproc/private/defaultId.m +++ b/external/fieldtrip/preproc/private/defaultId.m @@ -40,7 +40,9 @@ % remove the non-FieldTrip functions from the path, these should not be part of the default message identifier keep = true(size(stack)); -[v, p] = ft_version; +p = fileparts(mfilename('fullpath')); +% strip away '/utilities/private' where this function is located +p = p(1:end-18); for i=1:numel(stack) keep(i) = strncmp(p, stack(i).file, length(p)); end diff --git a/external/fieldtrip/preproc/private/ft_notification.m b/external/fieldtrip/preproc/private/ft_notification.m index dc9b7369..a74c4052 100644 --- a/external/fieldtrip/preproc/private/ft_notification.m +++ b/external/fieldtrip/preproc/private/ft_notification.m @@ -337,7 +337,7 @@ ft_default.notification.(level) = s; % the remainder is fully handled by the ERROR function if ~isempty(msgId) - error(msgId, varargin{:}); + error(state); else error(varargin{:}); end diff --git a/external/fieldtrip/preproc/private/ft_platform_supports.m b/external/fieldtrip/preproc/private/ft_platform_supports.m index c0676b62..34639722 100644 --- a/external/fieldtrip/preproc/private/ft_platform_supports.m +++ b/external/fieldtrip/preproc/private/ft_platform_supports.m @@ -41,6 +41,28 @@ % % See also FT_VERSION, VERSION, VER, VERLESSTHAN +% Copyright (C) 2006, Robert Oostenveld +% Copyright (C) 2010, Eelke Spaak +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + + if ~ischar(what) error('first argument must be a string'); end @@ -48,6 +70,9 @@ switch what case 'matlabversion' tf = is_matlab() && matlabversion(varargin{:}); + + case 'octaveversion' + tf = is_octave() && octaveversion(varargin{:}); case 'exists-in-private-directory' tf = is_matlab(); @@ -206,11 +231,31 @@ end % function + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% SUBFUNCTION -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function [inInterval] = matlabversion(min, max) +function [inInterval] = octaveversion(min, max) +% the version does not change, making it persistent speeds up the subsequent calls +persistent curVer + +if nargin<2 + max = min; +end +if isempty(curVer) + curVer = OCTAVE_VERSION; +end + +% perform comparison with respect to version number +[major, minor] = parseMatlabVersion(curVer); +[minMajor, minMinor] = parseMatlabVersion(min); +[maxMajor, maxMinor] = parseMatlabVersion(max); + +inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); + +end % function + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % MATLABVERSION checks if the current MATLAB version is within the interval % specified by min and max. % @@ -229,27 +274,8 @@ % etc. % % See also VERSION, VER, VERLESSTHAN - -% Copyright (C) 2006, Robert Oostenveld -% Copyright (C) 2010, Eelke Spaak -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [inInterval] = matlabversion(min, max) % the version does not change, making it persistent speeds up the subsequent calls persistent curVer @@ -311,8 +337,8 @@ minor = int8((ver - floor(ver)) * 10); else % ver is string (e.g. '7.10'), parse accordingly [major, rest] = strtok(ver, '.'); - major = str2num(major); - minor = str2num(strtok(rest, '.')); + major = str2double(major); + minor = str2double(strtok(rest, '.')); end end % function diff --git a/external/fieldtrip/private/append_common.m b/external/fieldtrip/private/append_common.m index 5405e26f..db2b6034 100644 --- a/external/fieldtrip/private/append_common.m +++ b/external/fieldtrip/private/append_common.m @@ -5,7 +5,7 @@ % The general bookkeeping and the correct specification of the cfg % should be taken care of by the calling function. % -% See FT_APPENDDATA, T_APPENDTIMELOCK, FT_APPENDFREQ +% See FT_APPENDDATA, FT_APPENDTIMELOCK, FT_APPENDFREQ % Copyright (C) 2017, Robert Oostenveld % @@ -145,6 +145,13 @@ data.(cfg.parameter{i})(:,chansel,:) = varargin{j}.(cfg.parameter{i})(:,chansel,:); end + case {'rpt_chan_freq_time' 'rpttap_chan_freq_time' 'subj_chan_freq_time'} + data.(cfg.parameter{i}) = nan(dimsiz); + for j=1:numel(varargin) + chansel = match_str(varargin{j}.label, oldlabel{j}); + data.(cfg.parameter{i})(:,chansel,:,:) = varargin{j}.(cfg.parameter{i})(:,chansel,:,:); + end + otherwise % do not concatenate this field end % switch @@ -168,7 +175,7 @@ end % determine the union of all input data - tmpcfg = keepfields(cfg, {'tolerance', 'channel'}); + tmpcfg = keepfields(cfg, {'tolerance', 'channel', 'showcallinfo'}); tmpcfg.select = 'union'; [varargin{:}] = ft_selectdata(tmpcfg, varargin{:}); for i=1:numel(varargin) @@ -251,7 +258,7 @@ case 'rpt' % determine the intersection of all input data - tmpcfg = keepfields(cfg, {'tolerance', 'channel'}); + tmpcfg = keepfields(cfg, {'tolerance', 'channel', 'showcallinfo'}); tmpcfg.select = 'intersect'; [varargin{:}] = ft_selectdata(tmpcfg, varargin{:}); for i=1:numel(varargin) diff --git a/external/fieldtrip/private/artifact_viewer.m b/external/fieldtrip/private/artifact_viewer.m deleted file mode 100644 index 85719070..00000000 --- a/external/fieldtrip/private/artifact_viewer.m +++ /dev/null @@ -1,243 +0,0 @@ -function artifact_viewer(cfg, artcfg, zval, artval, zindx, inputdata) - -% ARTIFACT_VIEWER is a subfunction that reads a segment of data -% (one channel only) and displays it together with the cumulated -% z-value - -% Copyright (C) 2004-2006, Jan-Mathijs Schoffelen & Robert Oostenveld -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ - -if ishandle(cfg) - % get the input variables from the handle's guidata - hx = cfg; clear cfg; - tmp = guidata(hx); - cfg = tmp.cfg; - cfg.trl = tmp.trl; - artcfg = tmp.artcfg; - zval = tmp.zval; - artval = tmp.artval; - zindx = tmp.zindx; - if ~isempty(tmp.data) - inputdata = tmp.data; - hdr = ft_fetch_header(inputdata); - else - hdr = ft_read_header(cfg.headerfile); - end - % make a new figure - figure; -else - if nargin == 5 - hdr = ft_read_header(cfg.headerfile); - elseif nargin == 6 - hdr = ft_fetch_header(inputdata); % used name inputdata iso data, because data is already used later in this function - end -end - -dat.cfg = cfg; -dat.artcfg = artcfg; -if exist('inputdata', 'var') dat.inputdata = inputdata; end -dat.trlop = 1; -dat.zval = zval; -dat.artval = artval; -dat.zindx = zindx; -dat.stop = 0; -dat.numtrl = size(cfg.trl,1); -dat.trialok = zeros(1,dat.numtrl); -dat.hdr = hdr; -for trlop=1:dat.numtrl - dat.trialok(trlop) = ~any(artval{trlop}); -end - -h = gcf; -guidata(h,dat); -uicontrol(gcf,'units','pixels','position',[5 5 40 18],'String','stop','Callback',@stop); -uicontrol(gcf,'units','pixels','position',[50 5 25 18],'String','<','Callback',@prevtrial); -uicontrol(gcf,'units','pixels','position',[75 5 25 18],'String','>','Callback',@nexttrial); -uicontrol(gcf,'units','pixels','position',[105 5 25 18],'String','<<','Callback',@prev10trial); -uicontrol(gcf,'units','pixels','position',[130 5 25 18],'String','>>','Callback',@next10trial); -uicontrol(gcf,'units','pixels','position',[160 5 50 18],'String','','Callback',@nextartfct); - -while ishandle(h), - dat = guidata(h); - if dat.stop == 0, - read_and_plot(h); - uiwait; - else - break - end -end -if ishandle(h) - close(h); -end - -%------------ -%subfunctions -%------------ - -function read_and_plot(h) -vlinecolor = [0 0 0]; -dat = guidata(h); -% make a local copy of the relevant variables -trlop = dat.trlop; -zval = dat.zval{trlop}; -artval = dat.artval{trlop}; -zindx = dat.zindx{trlop}; -cfg = dat.cfg; -artcfg = dat.artcfg; -hdr = dat.hdr; -trl = dat.artcfg.trl; -trlpadsmp = round(artcfg.trlpadding*hdr.Fs); -% determine the channel with the highest z-value -[dum, indx] = max(zval); -sgnind = zindx(indx); -iscontinuous = 1; -if isfield(dat, 'inputdata') - data = ft_fetch_data(dat.inputdata, 'header', hdr, 'begsample', trl(trlop,1), 'endsample', trl(trlop,2), 'chanindx', sgnind, 'checkboundary', strcmp(cfg.continuous,'no')); -else - data = ft_read_data(cfg.datafile, 'header', hdr, 'begsample', trl(trlop,1), 'endsample', trl(trlop,2), 'chanindx', sgnind, 'checkboundary', strcmp(cfg.continuous,'no')); -end -% data = preproc(data, channel, hdr.Fs, artfctdef, [], fltpadding, fltpadding); -str = sprintf('trial %3d, channel %s', dat.trlop, hdr.label{sgnind}); -fprintf('showing %s\n', str); -% plot z-values in lower subplot -subplot(2,1,2); -cla -hold on -xval = trl(trlop,1):trl(trlop,2); -if trlpadsmp - sel = 1:trlpadsmp; - h = plot(xval(sel), zval(sel)); set(h, 'color', [0.5 0.5 1]); % plot the trialpadding in another color - sel = trlpadsmp:(length(data)-trlpadsmp); - plot(xval(sel), zval(sel), 'b'); - sel = (length(data)-trlpadsmp):length(data); - h = plot(xval(sel), zval(sel)); set(h, 'color', [0.5 0.5 1]); % plot the trialpadding in another color - vline(xval( 1)+trlpadsmp, 'color', vlinecolor); - vline(xval(end)-trlpadsmp, 'color', vlinecolor); -else - plot(xval, zval, 'b'); -end -% draw a line at the threshold level -hline(artcfg.cutoff, 'color', 'r', 'linestyle', ':'); -% make the artefact part red -zval(~artval) = nan; -plot(xval, zval, 'r-'); -hold off -xlabel('samples'); -ylabel('zscore'); - -% plot data of most aberrant channel in upper subplot -subplot(2,1,1); -cla -hold on -if trlpadsmp - sel = 1:trlpadsmp; - h = plot(xval(sel), data(sel)); set(h, 'color', [0.5 0.5 1]); % plot the trialpadding in another color - sel = trlpadsmp:(length(data)-trlpadsmp); - plot(xval(sel), data(sel), 'b'); - sel = (length(data)-trlpadsmp):length(data); - h = plot(xval(sel), data(sel)); set(h, 'color', [0.5 0.5 1]); % plot the trialpadding in another color - vline(xval( 1)+trlpadsmp, 'color', vlinecolor); - vline(xval(end)-trlpadsmp, 'color', vlinecolor); -else - plot(xval, data, 'b'); -end -data(~artval) = nan; -plot(xval, data, 'r-'); -hold off -xlabel('samples'); -ylabel('uV or Tesla'); -title(str); - -function varargout = nexttrial(h, eventdata, handles, varargin) -dat = guidata(h); -if dat.trlop < dat.numtrl, - dat.trlop = dat.trlop + 1; -end; -guidata(h,dat); -uiresume; - -function varargout = next10trial(h, eventdata, handles, varargin) -dat = guidata(h); -if dat.trlop < dat.numtrl - 10, - dat.trlop = dat.trlop + 10; -else dat.trlop = dat.numtrl; -end; -guidata(h,dat); -uiresume; - -function varargout = prevtrial(h, eventdata, handles, varargin) -dat = guidata(h); -if dat.trlop > 1, - dat.trlop = dat.trlop - 1; -else dat.trlop = 1; -end; -guidata(h,dat); -uiresume; - -function varargout = prev10trial(h, eventdata, handles, varargin) -dat = guidata(h); -if dat.trlop > 10, - dat.trlop = dat.trlop - 10; -else dat.trlop = 1; -end; -guidata(h,dat); -uiresume; - -function varargout = nextartfct(h, eventdata, handles, varargin) -dat = guidata(h); -artfctindx = find(dat.trialok == 0); -sel = find(artfctindx > dat.trlop); -if ~isempty(sel) - dat.trlop = artfctindx(sel(1)); -else - dat.trlop = dat.trlop; -end -guidata(h,dat); -uiresume; - -function varargout = prevartfct(h, eventdata, handles, varargin) -dat = guidata(h); -artfctindx = find(dat.trialok == 0); -sel = find(artfctindx < dat.trlop); -if ~isempty(sel) - dat.trlop = artfctindx(sel(end)); -else - dat.trlop = dat.trlop; -end -guidata(h,dat); -uiresume; - -function varargout = stop(h, eventdata, handles, varargin) -dat = guidata(h); -dat.stop = 1; -guidata(h,dat); -uiresume; - -function vsquare(x1, x2, c) -abc = axis; -y1 = abc(3); -y2 = abc(4); -x = [x1 x2 x2 x1 x1]; -y = [y1 y1 y2 y2 y1]; -z = [-1 -1 -1 -1 -1]; -h = patch(x, y, z, c); -set(h, 'edgecolor', c) - diff --git a/external/fieldtrip/private/bg_rgba2rgb.m b/external/fieldtrip/private/bg_rgba2rgb.m new file mode 100644 index 00000000..8624e8dc --- /dev/null +++ b/external/fieldtrip/private/bg_rgba2rgb.m @@ -0,0 +1,162 @@ +function [rgb] = bg_rgba2rgb(bg, rgba, varargin) + +% BG_RGBA2RGB overlays a transparency masked colored image on a colored background, +% and represents the result as an RGB matrix. +% +% Use as: +% rgb = bg_rgba2rgb(bg, rgba) +% +% or +% rgb = bg_rgba2rgb(bg, rgba, cmap, clim, alpha, amap, alim); +% +% When 2 input arguments are supplied: +% bg = Nx3 matrix of background rgb-coded color-values, or MxNx3 +% rgba = Nx4 matrix of rgb + alpha values, or MxNx4 +% +% When 7 input arguments are supplied: +% bg = Nx3 matrix, Nx1 vector, 1x3 vector, MxN, or MxNx3. +% rgba = Nx1 vector with 'functional values', or MxN. +% cmap = Mx3 colormap, or MATLAB-supported name of colormap +% clim = 1x2 vector denoting the color limits +% alpha = Nx1 vector with 'alpha values', or MxN +% amap = Mx1 alphamap, or MATLAB -supported name of alphamap ('rampup/down', 'vup/down') +% alim = 1x2 vector denoting the opacity limits + +if numel(varargin)==0 + siz1 = size(bg); + siz2 = size(rgba); + assert(isequal(siz1(1:end-1),siz2(1:end-1)), 'number of data points should be the same'); + assert(siz1(end)==3 && siz2(end)==4, 'inconsistent input size'); + + bg = reshape(bg, [], 3); + rgba = reshape(rgba, [], 4); + rgb = do_conversion(bg, rgba); + rgb = reshape(rgb, siz1); +else + % this requires the data to be converted into rgb values irst, and + % needs a cmap + clim, and an alpha, alphamap and alim + if numel(varargin)~=5 + error('if a vector of color data is supplied, more input arguments are required'); + end + + % colormap handling + cmap = varargin{1}; + if ischar(cmap) + cmap = colormap(cmap); + elseif size(cmap,2)~=3 + error('invalid specification of colormap, should be nx3'); + end + clim = varargin{2}; if isempty(clim), clim(1) = min(rgba(:)); clim(2) = max(rgba(:)); end + alpha = varargin{3}; + amap = varargin{4}; + alim = varargin{5}; if isempty(alim), alim(1) = min(alpha(:)); alim(2) = max(alpha(:)); end + + % deal with the color data + dat = rgba; + siz = size(dat); + dat = reshape(dat, [], 1); + alpha = reshape(alpha, [], 1); % assume to be same siza as input data! + finvals = isfinite(dat); + + rgba = zeros(size(dat,1),4); + rgba(finvals,1:3) = dat2rgb(dat(finvals), cmap, clim); + + finvals = isfinite(alpha); + rgba(finvals,4) = alpha2a(alpha(finvals), amap, alim); + rgba(~finvals,4) = 0; + + % deal with the background: allow it to be 1x3, i.e. a single color per + % pixel. + if isequal(size(bg),[1 3]) + bg = permute(repmat(bg(:), [1 siz]), [1+(1:numel(siz)) 1]); + else + siz_bg = size(bg); + if isequal(siz_bg, siz) + % make bg rgb + if all(bg(:)<=1) && all(bg(:)>=0) + % don't scale + else + bg_min = min(bg(:)); + bg_max = max(bg(:)); + bg = (bg-bg_min)./(bg_max-bg_min); + end + bg = repmat(bg, [ones(1,ndims(bg)) 3]); + else + %FIXME + end + end + bg = reshape(bg, [], 3); + + if numel(siz)==2 && siz(2)==1, siz = siz(1); end + rgb = do_conversion(bg, rgba); + rgb = reshape(rgb, [siz 3]); +end + +function rgb = do_conversion(bg, rgba) + +rgb = zeros(size(rgba,1),3); +a_ = 1-rgba(:,4); +a = rgba(:,4); + +for k = 1:3 + rgb(:,k) = bg(:,k).*a_ + rgba(:,k).*a; +end + + +function rgb = dat2rgb(dat, cmap, clim) + +dat(end+1) = clim(1); +dat(end+1) = clim(2); % add the extremes to be sure that they are included + +dat(datclim(2)) = clim(2); + + +% scale between 0 and 1 +dat = dat-min(dat); +dat = dat/max(dat); + +ind = round(dat.*(size(cmap,1)-1))+1; +rgb = cmap(ind(1:end-2),:); + +function a = alpha2a(alpha, amap, alim) + +alpha(end+1) = alim(1); +alpha(end+1) = alim(2); + +alpha(alphaalim(2)) = alim(2); + +if ischar(amap) + switch amap + case 'rampup' + a = alpha - min(alpha); + a = a./max(a); + case 'rampdown' + a = alpha - min(alpha); + a = a./max(a); + a = 1-a; + case 'vup' + a = alpha - min(alpha); + a = a./max(a); + a = 1 - 2.*abs(a - 0.5); + case 'vdown' + a = alpha - min(alpha); + a = a./max(a); + a = 2.*abs(a - 0.5); + otherwise + error('unknown alphamap specified'); + end +else + amap = amap(:); + + % assume it to be a vector that has M elements, scaled between 0 and 1 + a = alpha-min(alpha); + a = a/max(a); + ind = round(a.*(size(amap,1)-1))+1; + a = amap(ind(1:end),:); +end +a = a(1:end-2); + + + diff --git a/external/fieldtrip/private/bivariate_common.m b/external/fieldtrip/private/bivariate_common.m index a2da0095..cc139652 100644 --- a/external/fieldtrip/private/bivariate_common.m +++ b/external/fieldtrip/private/bivariate_common.m @@ -60,10 +60,10 @@ function make_figure(cfg, varargin) cfg = rmfield(cfg, 'inputfile'); end -dimord = getdimord(varargin{1}, cfg.parameter); -dimtok = tokenize(dimord, '_'); - for i=1:numel(varargin) + dimord = getdimord(varargin{i}, cfg.parameter); + dimtok = tokenize(dimord, '_'); + if isequal(dimtok{1}, 'chancmb') % Convert 2-dimensional channel matrix to a single dimension if isempty(cfg.directionality) diff --git a/external/fieldtrip/private/blockindx2cmbindx.m b/external/fieldtrip/private/blockindx2cmbindx.m index 2160c91b..14c1f4b4 100644 --- a/external/fieldtrip/private/blockindx2cmbindx.m +++ b/external/fieldtrip/private/blockindx2cmbindx.m @@ -1,53 +1,123 @@ function [cmbindx, n, blocklabel] = blockindx2cmbindx(labelcmb, blockindx, block) -% this is a helper function which JM at some point wrote, but it is not -% clear to him anymore, what the function actually does. as soon as he -% finds out, the documentation will be updated. +% This is a helper function that is needed for the bookkeeping of the data, +% when requesting (conditional)-blockwise granger causality estimates. Its +% single use is in ft_connectivityanalysis, but in order to keep that code +% clean, it was decided to put this function as a private function. +% +% Use as +% [cmbindx, n, blocklabel] = blockindx2cmbindx(labelcmb, blockindx, +% block) +% +% The purpose is to generate a cell-array (Nx2, same size as input array +% block) of numeric indices, which index into the rows of the Mx2 labelcmb +% array, and which can subsequently be used by lower-level functionality +% (i.e. blockwise_conditionalgranger) to compute the connectivity metric of +% interest. Blockindx is a 1x2 cell-array, which maps the individual +% channels in blockindx{1} to an indexed block in blockindx{2}. Block +% specifies in each row of cells two ordered lists of blocks that are +% needed to compute a conditioned Granger spectrum. +% Copyright (C) 2010-2017 Jan-Mathijs Schoffelen +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +% The variable labelcmb contains the specification of how each +% row of the data has been computed, from chanX to chanY given a multivariate +% decomposition of [chanA,...,chanZ], first generate a cell-array y and z +% that splits the channel pair from the multivariate block y = cell(size(labelcmb)); -z = cell(size(labelcmb)); -okorig = zeros(size(labelcmb)); +z = cell(size(labelcmb,1),1); cmbindx = cell(size(block)); n = cell(size(block)); -for k = 1:numel(y) - x1 = strfind(labelcmb{k},'['); - x2 = strfind(labelcmb{k},']'); - y{k} = labelcmb{k}(1:x1-1); - z{k} = labelcmb{k}(x1+1:x2-1); - okorig(k) = numel(z{k}); +for k = 1:size(y,1) + x1 = strfind(labelcmb{k,1},'['); + x2 = strfind(labelcmb{k,1},']'); + y{k,1} = labelcmb{k,1}(1:x1-1); % string denoting single channel + z{k,1} = labelcmb{k,1}(x1+1:x2-1); % concatenated string of all channels in the current blockwise decomp + x1 = strfind(labelcmb{k,2},'['); + y{k,2} = labelcmb{k,2}(1:x1-1); end +z(:,2) = z(:,1); -for k = 1:numel(block) - tmpsel = []; - tmp = {}; - for j = 1:numel(block{k}) - dummy = find(ismember(blockindx{2},block{k}(j))); - tmpsel = [tmpsel;dummy]; - n{k}(j,1) = numel(dummy); - end - tmp = blockindx{1}(tmpsel); - blocklabel{k} = cat(2,y{tmpsel}); - - ok = okorig; - for j = 1:numel(labelcmb) - for jj = 1:numel(tmp) - if isempty(strfind(z{j}, tmp{jj})), - ok(j) = nan; - else - ok(j) = ok(j) - numel(tmp{jj}); - end - end +uz = unique(z(:,1)); % this is the number of unique blocks +% the unique blocks can be of different size, but should be present in full +% in the labelcmb, i.e. correspond to n^2 rows in the labelcmb + +z_idx = zeros(size(z,1),1); +z_block = cell(numel(uz),1); +z_label = cell(numel(uz),1); +for k = 1:numel(uz) + tmp = strcmp(z(:,1),uz{k}); + tok = tokenize(uz{k}, ','); + + z_label{k} = tok(:); % the label of the channels that participate in a block + + [dum,i2x] = match_str(y(tmp,1), z_label{k}); + [dum,i2y] = match_str(y(tmp,2), z_label{k}); + + if sqrt(sum(tmp))~=numel(tok) + error('incomplete data'); end + z_idx(tmp) = k; - list = []; - if sum(ok(:,1)==0)==numel(tmp).^2, - list = zeros(numel(tmp)*[1 1]); - ok = find(ok(:,1)==0); - for j = 1:numel(ok) - x1 = match_str(tmp, y(ok(j),1)); - x2 = match_str(tmp, y(ok(j),2)); - list(x1,x2) = ok(j); + tmp = find(tmp); + list = zeros(numel(tok)); + for kk = 1:numel(i2x) + list(i2x(kk),i2y(kk)) = tmp(kk); + end + z_block{k} = list; % the ordered indices of the combinations in a block, as per the order in z_label +end +z_label_sorted = z_label; +for k = 1:numel(z_label) + z_label_sorted{k} = sort(z_label{k}); +end + +% now expand the block vectors to channel labels (ordered) +b_label = cell(size(block)); +b_n = cell(size(block)); +for k = 1:numel(block) + tmplist = cell(0,1); + tmpn = zeros(0,1); + for m = 1:numel(block{k}) + tmplist = cat(1,tmplist,blockindx{1}(blockindx{2}==block{k}(m))); + tmpn = cat(1,tmpn,sum(blockindx{2}==block{k}(m))); + end + b_label{k} = tmplist; + b_n{k} = tmpn; +end + +% now identify which of the z_blocks corresponds to the requested blocks, +% and reorder if needed +for k = 1:numel(block) + indx = []; + for m = 1:numel(z_label_sorted) + if ~isequal(sort(b_label{k}), z_label_sorted{m}) + continue; + else + indx = m; + break; end end - cmbindx{k} = list(:); + [dum, i2] = match_str(b_label{k}, z_label{indx}); + cmbindx{k} = reshape(z_block{indx}(i2,i2),[],1); + n{k} = b_n{k};%(i2); end + +blocklabel = []; % don't know why this is needed, keep for now. diff --git a/external/fieldtrip/private/cancorr.m b/external/fieldtrip/private/cancorr.m deleted file mode 100644 index 06bc17b3..00000000 --- a/external/fieldtrip/private/cancorr.m +++ /dev/null @@ -1,126 +0,0 @@ -function [px, py, wx, wy, powx, powy] = cancorr(C,x,y,powflag,realflag,trunc) - -% CANCORR computes the canonical correlation between multiple variables -% -% Canonical correlation analysis (CCA) is a way of measuring the linear -% relationship between two multidimensional variables. It finds two bases, -% one for each variable, that are optimal with respect to correlations and, -% at the same time, it finds the corresponding correlations. -% -% Use as -% [px, py, wx, wy] = cancorr(C,x,y) - -% Copyright (C) 2005, Robert Oostenveld -% -% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id$ - -if nargin<3, - ft_error('to compute canonical coherence, you need to specify the indices to the cross-spectral density making up the dependent and independent variable'); -elseif nargin==3, - powflag = 0; - realflag = 0; - trunc = 0; -elseif nargin==4, - realflag = 0; - trunc = 0; -elseif nargin==5, - trunc = 0; -end - -siz = size(C,1); -ind = find(sum(isfinite(C))>0); - -xorig = x; -yorig = y; -x = intersect(ind, x); -y = intersect(ind, y); - -%use the approach as specified by Borga et al 1992: a unified approach to PCA, PLS, MLR and CCA -%that is: solve the generalized eigenvalue problem eig(inv(B)*A) with -%A = [O Cxy; Cyx O], and B = [Cxx O; O Cyy]; -indx = zeros(length(ind), 1); -indy = zeros(length(ind), 1); -indx(1:length(x)) = 1; -indy(length(x)+1:end) = 1; - -Aorig = [indx*indy' + indy*indx'].*C([x y],[x y]); -Borig = [indx*indx' + indy*indy'].*C([x y],[x y]); - -if realflag, - A = real(Aorig); - B = real(Borig); -else - A = Aorig; - B = Borig; -end -%if A and B are rank deficient this could lead to non-finite eigenvalues -%[w, p] = eig(A,B); -[ua,sa,va] = svds(C(x,x), rank(C(x,x))); -[ub,sb,vb] = svds(C(y,y), rank(C(y,y))); -U = [ua' zeros(size(ua,2),size(ub,1)); zeros(size(ub,2),size(ua,1)) ub']; -[w, p] = eig(U*A*U', U*B*U'); - -[srt, ind] = sort(diag(abs(p)), 'descend'); -w = w(:, ind); -p = p(ind, ind); - -%eigenvalues come in pairs, with 180-degree ambiguity -nump = ceil(size(p,1)/2); -%wx = zeros(length(xorig)); wx(find(ismember(xorig,x)), 1:nump) = w(1:length(x), 1:2:end); -%wy = zeros(length(yorig)); wy(find(ismember(yorig,y)), 1:nump) = w(length(x)+1:end, 1:2:end); -wx = zeros(length(xorig)); -wy = zeros(length(yorig)); -px = abs(p(1:2:end, 1:2:end)); -py = abs(p(1:2:end, 1:2:end)); - -if powflag, - powx = wx'*B(x,x)*wx; - powy = wy'*B(y,y)*wy; -end - -if 0, -Cxx = C(x,x); -Cyy = C(y,y); -Cxy = C(x,y); -Cyx = C(y,x); - -[wx, px] = eig(inv(Cxx)*Cxy*inv(Cyy)*Cyx); -[wy, py] = eig(inv(Cyy)*Cyx*inv(Cxx)*Cxy); - -[srtx,indx] = sort(diag(px), 'descend'); -[srty,indy] = sort(diag(py), 'descend'); - -px = px(indx,indx); -wx = wx(:, indx); -py = py(indy,indy); -wy = wy(:, indy); - -if powflag, - powx = wx'*Cxx*wx; - powy = wy'*Cyy*wy; -end -end - -if trunc>0 && trunc<1, - px = px.*double(px>trunc); - py = py.*double(py>trunc); -elseif trunc>=1, - px(trunc+1:end,trunc+1:end) = 0; - py(trunc+1:end,trunc+1:end) = 0; -end diff --git a/external/fieldtrip/private/channelposition.m b/external/fieldtrip/private/channelposition.m index 1eecb56a..2952c3f2 100644 --- a/external/fieldtrip/private/channelposition.m +++ b/external/fieldtrip/private/channelposition.m @@ -5,11 +5,11 @@ % % Use as % [pos, ori, lab] = channelposition(sens) -% where sens is an electrode or gradiometer array. +% where sens is an electrode, gradiometer or optode array. % % See also FT_DATATYPE_SENS -% Copyright (C) 2009-2012, Robert Oostenveld & Vladimir Litvak +% Copyright (C) 2009-2018, Robert Oostenveld & Vladimir Litvak % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. @@ -49,6 +49,8 @@ sens.coilori = nan(size(sens.coilpos)); elseif ~isfield(sens, 'coilori') && isfield(sens, 'elecpos') sens.coilori = nan(size(sens.elecpos)); +elseif ~isfield(sens, 'coilori') && isfield(sens, 'optopos') + sens.coilori = nan(size(sens.optopos)); end switch ft_senstype(sens) @@ -261,6 +263,8 @@ nelec = size(sens.elecpos,1); % these are the electrodes elseif isfield(sens, 'coilpos') ncoil = size(sens.coilpos,1); % these are the coils + elseif isfield(sens, 'optopos') + nopto = size(sens.optopos,1); % these are the optodes end if ~isfield(sens, 'tra') && isfield(sens, 'elecpos') && nchan==nelec @@ -286,11 +290,18 @@ pnt = sens.coilpos; ori = sens.coilori; lab = sens.label; + + elseif ~isfield(sens, 'tra') && isfield(sens, 'optopos') && nchan==nopto + % there is one optode per channel, which means that the channel position is identical to the coil position + pnt = sens.optopos; + ori = nan(size(pnt)); + lab = sens.label; elseif isfield(sens, 'tra') % each channel depends on multiple sensors (electrodes or coils), compute a weighted position for the channel % for MEG gradiometer channels this means that the position is in between the two coils % for bipolar EEG channels this means that the position is in between the two electrodes + % for NIRS channels this means that the position is in between the transmit and receive optode pnt = nan(nchan,3); ori = nan(nchan,3); if isfield(sens, 'coilpos') @@ -306,14 +317,18 @@ weight = weight ./ sum(weight); pnt(i,:) = weight * sens.elecpos; end + elseif isfield(sens, 'optopos') + for i=1:nchan + weight = abs(sens.tra(i,:)); + weight = weight ./ sum(weight); + pnt(i,:) = weight * sens.optopos; + end end lab = sens.label; - end - end % switch senstype -n = size(lab,2); +n = size(lab,2); % this is to fix the planar layouts, which cannot be plotted anyway if n>1 && size(lab, 1)>1 % this is to prevent confusion when lab happens to be a row array pnt = repmat(pnt, n, 1); diff --git a/external/fieldtrip/private/chanscale_common.m b/external/fieldtrip/private/chanscale_common.m index 1d98a271..32b51af9 100644 --- a/external/fieldtrip/private/chanscale_common.m +++ b/external/fieldtrip/private/chanscale_common.m @@ -13,8 +13,8 @@ % cfg.ecgscale = number, scaling to apply to the ECG channels prior to display % cfg.emgscale = number, scaling to apply to the EMG channels prior to display % cfg.megscale = number, scaling to apply to the MEG channels prior to display -% cfg.gradscale = number, scaling to apply to the MEG gradiometer channels prior to display (in addition to the cfg.megscale factor) % cfg.magscale = number, scaling to apply to the MEG magnetometer channels prior to display (in addition to the cfg.megscale factor) +% cfg.gradscale = number, scaling to apply to the MEG gradiometer channels prior to display (in addition to the cfg.megscale factor) % % For individual control off the scaling for all channels you can use % cfg.chanscale = Nx1 vector with scaling factors, one per channel specified in cfg.channel @@ -23,6 +23,26 @@ % cfg.mychanscale = number, scaling to apply to the channels specified in cfg.mychan % cfg.mychan = Nx1 cell-array with selection of channels +% Copyright (C) 2017, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + cfg.eegscale = ft_getopt(cfg, 'eegscale'); cfg.eogscale = ft_getopt(cfg, 'eogscale'); cfg.ecgscale = ft_getopt(cfg, 'ecgscale'); @@ -34,6 +54,10 @@ cfg.mychanscale = ft_getopt(cfg, 'mychanscale'); cfg.mychan = ft_getopt(cfg, 'mychan'); +% these should be column vectors +cfg.chanscale = cfg.chanscale(:); +cfg.mychanscale = cfg.mychanscale(:); + dimord = getdimord(data, cfg.parameter); switch dimord @@ -76,11 +100,11 @@ end if ~isempty(cfg.chanscale) chansel = match_str(data.label, ft_channelselection(cfg.channel, data.label)); - dat(chansel,:,:) = dat(chansel,:,:) .* repmat(cfg.chanscale(:),1,size(dat,2),size(dat,3)); + dat(chansel,:,:) = dat(chansel,:,:) .* repmat(cfg.chanscale,1,size(dat,2),size(dat,3)); end if ~isempty(cfg.mychanscale) - chansel = match_str(data.label, ft_channelselection(cfg.mychan, data.label)); - dat(chansel,:,:) = dat(chansel,:,:) .* repmat(cfg.mychanscale(:),1,size(dat,2),size(dat,3)); + [chansel, scalesel] = match_str(data.label, cfg.mychan); + dat(chansel,:,:) = dat(chansel,:,:) .* repmat(cfg.mychanscale(scalesel),1,size(dat,2),size(dat,3)); end % put the data back into the structure @@ -126,11 +150,11 @@ end if ~isempty(cfg.chanscale) chansel = match_str(data.label, ft_channelselection(cfg.channel, data.label)); - dat(chansel,:) = dat(chansel,:) .* repmat(cfg.chanscale(:),1,size(dat,2)); + dat(chansel,:) = dat(chansel,:) .* repmat(cfg.chanscale,1,size(dat,2)); end if ~isempty(cfg.mychanscale) - chansel = match_str(data.label, ft_channelselection(cfg.mychan, data.label)); - dat(chansel,:) = dat(chansel,:) .* repmat(cfg.mychanscale(:),1,size(dat,2)); + [chansel, scalesel] = match_str(data.label, cfg.mychan); + dat(chansel,:) = dat(chansel,:) .* repmat(cfg.mychanscale(scalesel),1,size(dat,2)); end % put the data back into the structure diff --git a/external/fieldtrip/private/defaultId.m b/external/fieldtrip/private/defaultId.m index f4e1ee99..e7569675 100644 --- a/external/fieldtrip/private/defaultId.m +++ b/external/fieldtrip/private/defaultId.m @@ -40,7 +40,9 @@ % remove the non-FieldTrip functions from the path, these should not be part of the default message identifier keep = true(size(stack)); -[v, p] = ft_version; +p = fileparts(mfilename('fullpath')); +% strip away '/utilities/private' where this function is located +p = p(1:end-18); for i=1:numel(stack) keep(i) = strncmp(p, stack(i).file, length(p)); end diff --git a/external/fieldtrip/private/determine_griddim.m b/external/fieldtrip/private/determine_griddim.m new file mode 100644 index 00000000..b6b3bfb9 --- /dev/null +++ b/external/fieldtrip/private/determine_griddim.m @@ -0,0 +1,144 @@ +function GridDim = determine_griddim(elec) + +% DETERMINE_GRIDDIM uses the labels and positions of electrodes in elec to +% determine the dimensions of each set of electrodes (i.e., electrodes with +% the same string, but different numbers) +% +% use as: +% GridDim = determine_griddim(elec) +% where elec is a structure that contains an elecpos field and a label field +% and GridDim(1) = number of rows and GridDim(2) = number of columns +% +% +% See also FT_ELECTRODEREALIGN + +% Copyright (C) 2017, Arjen Stolk, Sandon Griffin +% +% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id$ + +digits = regexp(elec.label, '\d+', 'match'); +maxdigit = 1; +for l=1:numel(digits) + ElecStrs{l,1} = regexprep(elec.label{l}, '\d+(?:_(?=\d))?', ''); % without electrode numbers + labels{l,1} = digits{l}{1}; % use first found digit + if str2num(digits{l}{1}) > maxdigit + maxdigit = str2num(digits{l}{1}); + end +end +ElecStr = cell2mat(unique(ElecStrs)); + +% determine if any electrodes appear to be cut out of this grid based on +% the labels +cutout = []; % index of electrodes that appear to be cut out +dowarn = 0; +for e = 1:maxdigit; + if isempty(match_str(labels, num2str(e))) + cutout(end+1) = e; + dowarn = 1; + end +end +if dowarn + ft_warning('%s appears to be missing electrodes %s or have electrodes that are labeled using an unconventional numbering system', ElecStr, num2str(cutout)); +end + +% determine if the labels are out of order and if they are, then re-order +% electrode positions based on numbers in the label and replace +% missing numbers with electrodes at position [NaN NaN NaN]; +labels_out_of_order = 0; +for n = 1:numel(labels)-1 + if str2num(cell2mat(labels(n+1))) ~= str2num(cell2mat(labels(n)))+1 + labels_out_of_order = 1; + end +end + +if labels_out_of_order + pos_ordered = NaN(maxdigit,3); + for e = 1:maxdigit; + if ~isempty(match_str(labels, num2str(e))) + pos_ordered(e, :) = elec.elecpos(match_str(labels, num2str(e)),:); + end + end + elec.elecpos = pos_ordered; +end + +d_bwelec = []; +for e = 1:size(elec.elecpos,1)-1; + d_bwelec(e) = sqrt(sum((elec.elecpos(e+1,:)-elec.elecpos(e,:)).^2)); +end + +% Find the electrode spacing +d_bwelec(find(d_bwelec == 0)) = NaN; +elec_space = nanmedian(d_bwelec); + + +% For each electrode, find the electrodes that are within 1.3*elec_space, +% which should be the adjacent neighbors. Without considering the +% electrodes in the same row (e.g., those that are numbered either +1 or -1 +% to the given electrode), find the numbers of the electrodes in the +% same column. Then, find the difference between the numbers of electrodes +% in the same column. The most common difference should be the row length. +vertical_skip = []; % n x 2 matrix where column 1 refers to a given electrode and column 1 refers to the difference in this electrode's number and the electrodes in the same column +for n = 1:size(elec.elecpos,1) + adj_elec_nums = []; % numbers of the electrodes in the same row or column + for e = 1:size(elec.elecpos,1) + if n~=e && n~=e-1 && n~=e+1 && n2 % if this is a two dimensional set of electrodes + % try to determine if this is a square set of electrodes or L-shaped + uniq_skips = unique(vertical_skip(:,2)); + count = zeros(numel(uniq_skips),1); + for k = 1:numel(uniq_skips) + count(k) = sum(vertical_skip(:,2)==uniq_skips(k)); + end + mode1 = mode(vertical_skip(:,2)); + n_mode = count(find(uniq_skips == mode1)); % the count of the mode + modes = uniq_skips(find(count > n_mode*0.75)); + GridDim(2) = mode(vertical_skip(:,2)); + GridDim(1) = ceil(maxdigit/GridDim(2)); + + if numel(modes) == 2 && diff(modes) ~= 1 % this set of electrodes is derived from an L-shaped grid + LGridDim = NaN(2,2); + + mode2 = modes; + mode2(find(modes == mode(vertical_skip(:,2)))) = []; + e_kink = vertical_skip(find(vertical_skip(:,2) == mode2, 1),1) + mode(vertical_skip(:,2)) -1; % number of electrode on the interior where the L-shape starts + LGridDim(1, 2) = mode(vertical_skip(:,2)); + LGridDim(1, 1) = ceil(e_kink/LGridDim(1, 2)); + + LGridDim(2, 2) = mode2; + LGridDim(2, 1) = ceil((maxdigit-e_kink)/LGridDim(2,2)); + ft_warning('%s appears to be a grid that is derived from an L-shaped structure and is best described as a %d x %d grid adjacent to a %d x %d grid', ElecStr, LGridDim(1,1), LGridDim(1,2), LGridDim(2,1), LGridDim(2,2)); + isLshaped = 1; + end + +else + GridDim(2) = maxdigit; + GridDim(1) = 1; +end + +% Give feedback about the grid dimensions +fprintf('assuming %s has %d x %d grid dimensions\n', ElecStr, GridDim(1), GridDim(2)); diff --git a/external/fieldtrip/private/elec1020_locate.m b/external/fieldtrip/private/elec1020_locate.m index 6c335a79..551cfa29 100644 --- a/external/fieldtrip/private/elec1020_locate.m +++ b/external/fieldtrip/private/elec1020_locate.m @@ -35,16 +35,18 @@ if feedback figure ft_plot_mesh(struct('pos', pnt, 'tri', dhk), 'edgecolor', 'none', 'facecolor', 'skin') - alpha 0.5 + lighting gouraud + material dull + lightangle(0, 90); + alpha 0.9 ft_plot_mesh(nas, 'vertexsize', 30) ft_plot_mesh(lpa, 'vertexsize', 30) ft_plot_mesh(ini, 'vertexsize', 30) ft_plot_mesh(rpa, 'vertexsize', 30) ft_plot_mesh(ver, 'vertexsize', 30) - axis equal - axis vis3d grid on hold on + view([1 1 0.5]) end @@ -874,5 +876,8 @@ lab = lab(sel); if feedback - ft_plot_mesh(elc, 'vertexsize', 10, 'vertexcolor', 'b') + elec = []; + elec.elecpos = elc; + elec.label = lab; + ft_plot_sens(elec) end diff --git a/external/fieldtrip/private/estimate_fwhm1.m b/external/fieldtrip/private/estimate_fwhm1.m index d52aed0f..799d2add 100644 --- a/external/fieldtrip/private/estimate_fwhm1.m +++ b/external/fieldtrip/private/estimate_fwhm1.m @@ -16,6 +16,9 @@ source.dim = pos2dim(source.pos); end inside = source.inside; +if islogical(inside) + inside = find(inside); +end ninside = length(inside); if ~isfield(source.avg, 'filter') @@ -164,8 +167,8 @@ source.fwhm = fwhm(:); source.indx = indx(:); source.insideold = source.inside; -source.inside = find(isfinite(fwhm)); -source.outside = setdiff((1:size(source.pos,1))', source.inside); +source.inside = isfinite(fwhm); +%source.outside = setdiff((1:size(source.pos,1))', source.inside); %in the extreme cases a is either: [2 1 1;1 2 1;1 1 2]; det(a) = 4; no correlation = small FWHM %or a is : [0 0 0;0 0 0;0 0 0]; det(a) = 0; perfect correlation = large FWHM diff --git a/external/fieldtrip/private/fixsampleinfo.m b/external/fieldtrip/private/fixsampleinfo.m index cc29030d..9bed0c8a 100644 --- a/external/fieldtrip/private/fixsampleinfo.m +++ b/external/fieldtrip/private/fixsampleinfo.m @@ -23,24 +23,31 @@ % check whether we're dealing with a timelock structure that has trials istimelock = hastime && hastrial && ~iscell(data.trial) && ~iscell(data.time); +israw = hastime && hastrial && iscell(data.trial) && iscell(data.time); + +% if the data does not have repetitions (i.e. trials) then it does not make sense to keep the sampleinfo +if ~hastrial && isfield(data, 'sampleinfo') + data = rmfield(data, 'sampleinfo'); + return +end if ~hasfsample && hastime - if istimelock - data.fsample = median(1./diff(data.time)); - else + if israw data.fsample = median(1./diff(data.time{1})); + elseif hastime + data.fsample = median(1./diff(data.time)); end end if hastrial - if istimelock - ntrial = size(data.trial,1); - else + if israw ntrial = numel(data.trial); + elseif istimelock + ntrial = size(data.trial,1); end else ntrial = dimlength(data, 'rpt'); - if ~isfinite(ntrial) && strcmp(data.dimord(1:6), 'rpttap') && isfield(data, 'cumtapcnt'), + if ~isfinite(ntrial) && strcmp(data.dimord(1:6), 'rpttap') && isfield(data, 'cumtapcnt') ntrial = numel(data.cumtapcnt); elseif ~isfinite(ntrial) ntrial = 1; @@ -49,17 +56,24 @@ trl = ft_findcfg(data.cfg, 'trl'); -if istimelock - nsmp = ones(ntrial,1) .* size(data.trial,3); -else +if istable(trl) + % the subsequent code requires this to be an array + trl = table2array(trl(:,1:3)); +end + +if israw nsmp = zeros(ntrial,1); - if hastrial + if israw for i=1:ntrial nsmp(i) = size(data.trial{i}, 2); end elseif ~isempty(trl) nsmp = trl(:,2) - trl(:,1) + 1; end +elseif istimelock + nsmp = ones(ntrial,1) .* size(data.trial,3); +elseif hastime + nsmp = ones(ntrial,1) .* length(data.time); end if isempty(trl) @@ -77,24 +91,25 @@ if isempty(trl) || ~all(nsmp==trl(:,2)-trl(:,1)+1) ft_warning('reconstructing sampleinfo by assuming that the trials are consecutive segments of a continuous recording'); + dbstack % construct a trial definition on the fly, assume that the trials are % consecutive segments of a continuous recording - if ntrial==1, + if ntrial==1 begsample = 1; else begsample = cat(1, 0, cumsum(nsmp(1:end-1))) + 1; end endsample = begsample + nsmp - 1; - - if istimelock - offset = ones(ntrial,1) .* time2offset(data.time, data.fsample); - else - offset = zeros(ntrial,1); - if hastime, + + if israw + offset = zeros(ntrial,1); + if hastime for i=1:ntrial offset(i) = time2offset(data.time{i}, data.fsample); end end + elseif hastime + offset = ones(ntrial,1) .* time2offset(data.time, data.fsample); end trl = [begsample endsample offset]; end @@ -106,11 +121,6 @@ ft_warning('failed to create sampleinfo field'); end -if (~isfield(data, 'trialinfo') || isempty(data.trialinfo)) && ~isempty(trl) && size(trl, 2) > 3, +if (~isfield(data, 'trialinfo') || isempty(data.trialinfo)) && ~isempty(trl) && size(trl, 2) > 3 data.trialinfo = trl(:, 4:end); end - -% if data does not have repetitions (i.e. trials) then it does not make sense to keep the sampleinfo -if ~hastrial && isfield(data, 'sampleinfo') - data = rmfield(data, 'sampleinfo'); -end diff --git a/external/fieldtrip/private/ft_fetch_sens.m b/external/fieldtrip/private/ft_fetch_sens.m index 410ff3f6..7e744927 100644 --- a/external/fieldtrip/private/ft_fetch_sens.m +++ b/external/fieldtrip/private/ft_fetch_sens.m @@ -149,17 +149,17 @@ display('reading electrodes from file ''%s''\n', cfg.elecfile); sens = ft_read_sens(cfg.elecfile); % only keep positions and labels in case of EEG electrodes - sens = keepfields(sens, {'elecpos', 'chanpos', 'unit', 'coordsys', 'label'}); + sens = keepfields(sens, {'elecpos', 'chanpos', 'unit', 'coordsys', 'label','tra'}); elseif hascfgelec display('using electrodes specified in the configuration\n'); sens = cfg.elec; % only keep positions and labels in case of EEG electrodes - sens = keepfields(sens, {'elecpos', 'chanpos', 'unit', 'coordsys', 'label'}); + sens = keepfields(sens, {'elecpos', 'chanpos', 'unit', 'coordsys', 'label','tra'}); elseif hasdataelec display('using electrodes specified in the data\n'); sens = data.elec; % only keep positions and labels in case of EEG electrodes - sens = keepfields(sens, {'elecpos', 'chanpos', 'unit', 'coordsys', 'label'}); + sens = keepfields(sens, {'elecpos', 'chanpos', 'unit', 'coordsys', 'label','tra'}); elseif hasoptofile display('reading optodes from file ''%s''\n', cfg.optofile); diff --git a/external/fieldtrip/private/icosahedron.m b/external/fieldtrip/private/icosahedron.m index b6d65c34..663cb7d5 100644 --- a/external/fieldtrip/private/icosahedron.m +++ b/external/fieldtrip/private/icosahedron.m @@ -1,11 +1,11 @@ -function [pos, tri] = icosahedron() +function [pos, tri] = icosahedron(varargin) -% ICOSAHEDRON creates an icosahedron +% ICOSAHEDRON creates an icosahedron with 12 vertices and 20 triangles % -% [pos, tri] = icosahedron -% creates an icosahedron with 12 vertices and 20 triangles -% -% See also OCTAHEDRON, ICOSAHEDRON42, ICOSAHEDRON162, ICOSAHEDRON642, ICOSAHEDRON2562 +% Use as +% [pos, tri] = icosahedron +% +% See also TETRAHEDRON, OCTAHEDRON, ICOSAHEDRON42, ICOSAHEDRON162, ICOSAHEDRON642, ICOSAHEDRON2562 % Copyright (C) 2002, Robert Oostenveld % @@ -27,27 +27,27 @@ % tri = [ - 1 2 3 - 1 3 4 - 1 4 5 - 1 5 6 - 1 6 2 - 2 8 3 - 3 9 4 - 4 10 5 - 5 11 6 - 6 7 2 - 7 8 2 - 8 9 3 - 9 10 4 - 10 11 5 - 11 7 6 + 1 2 3 + 1 3 4 + 1 4 5 + 1 5 6 + 1 6 2 + 2 8 3 + 3 9 4 + 4 10 5 + 5 11 6 + 6 7 2 + 7 8 2 + 8 9 3 + 9 10 4 + 10 11 5 + 11 7 6 12 8 7 12 9 8 12 10 9 12 11 10 12 7 11 -]; + ]; pos = zeros(12, 3); @@ -66,3 +66,12 @@ pos(12, :) = [0 0 -1]; % bottom point +if nargin>0 + n = varargin{1}; + % perform an n-fold refinement + for i=1:n + [pos, tri] = refine(pos, tri); + % scale all vertices to the unit sphere + pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); + end +end diff --git a/external/fieldtrip/private/icosahedron162.m b/external/fieldtrip/private/icosahedron162.m index ae3bfe69..457969da 100644 --- a/external/fieldtrip/private/icosahedron162.m +++ b/external/fieldtrip/private/icosahedron162.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron162() -% ICOSAHEDRON162 creates a 2-fold refined icosahedron +% ICOSAHEDRON162 creates a 2-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron162; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,9 +26,6 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); - -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +[pos, tri] = icosahedron(2); diff --git a/external/fieldtrip/private/icosahedron2562.m b/external/fieldtrip/private/icosahedron2562.m index 11752a0c..c9164f12 100644 --- a/external/fieldtrip/private/icosahedron2562.m +++ b/external/fieldtrip/private/icosahedron2562.m @@ -1,6 +1,12 @@ -function [pnt, tri] = icosahedron2562() +function [pos, tri] = icosahedron2562() -% ICOSAHEDRON2562 creates a 4-fold refined icosahedron +% ICOSAHEDRON2562 creates a 4-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron2562; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -22,10 +28,4 @@ % % $Id$ -[pnt, tri] = icosahedron; -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); -[pnt, tri] = refine(pnt, tri); - -pnt = pnt ./ repmat(sqrt(sum(pnt.^2,2)), 1,3); +[pos, tri] = icosahedron(4); diff --git a/external/fieldtrip/private/icosahedron42.m b/external/fieldtrip/private/icosahedron42.m index 833c11eb..3f8ccae0 100644 --- a/external/fieldtrip/private/icosahedron42.m +++ b/external/fieldtrip/private/icosahedron42.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron42() -% ICOSAHEDRON42 creates a 1-fold refined icosahedron +% ICOSAHEDRON42 creates a 1-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron42; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,8 +26,6 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); - -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); +[pos, tri] = icosahedron(1); diff --git a/external/fieldtrip/private/icosahedron642.m b/external/fieldtrip/private/icosahedron642.m index 4d932e5c..76e0d149 100644 --- a/external/fieldtrip/private/icosahedron642.m +++ b/external/fieldtrip/private/icosahedron642.m @@ -1,6 +1,12 @@ function [pos, tri] = icosahedron642() -% ICOSAHEDRON642 creates a 3-fold refined icosahedron +% ICOSAHEDRON642 creates a 3-fold refined icosahedron with the vertices on a unit +% sphere +% +% Use as +% [pos, tri] = icosahedron642; +% +% See also ICOSAHEDRON % Copyright (C) 2003, Robert Oostenveld % @@ -20,10 +26,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % +% $Id$ -[pos, tri] = icosahedron; -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); -[pos, tri] = refine(pos, tri); +[pos, tri] = icosahedron(3); -pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); diff --git a/external/fieldtrip/private/ignorefields.m b/external/fieldtrip/private/ignorefields.m index 5cf9f24b..be46a041 100644 --- a/external/fieldtrip/private/ignorefields.m +++ b/external/fieldtrip/private/ignorefields.m @@ -15,6 +15,7 @@ 'grad' 'elec' 'opto' + 'fsample' 'trialinfo' % this is dealt with explicitly 'sampleinfo' % this is dealt with explicitly }; @@ -63,7 +64,6 @@ case 'allowed' ignore = { % some fields that are always allowed to be present in the configuration - 'postamble' 'trackconfig' 'checkconfig' 'checksize' @@ -82,25 +82,39 @@ 'toolbox' }; + case {'rollback'} + ignore = { + % these should not be updated in rollback_provenance + 'callinfo' + 'checkconfig' + 'checksize' + 'debug' + 'notification' + 'previous' + 'showcallinfo' + 'trackcallinfo' + 'trackconfig' + 'trackdatainfo' + 'trackusage' + 'version' + 'warning' + }; case {'provenance', 'history'} ignore = { % these should not be included in the provenance or history - 'postamble' 'checkconfig' 'checksize' + 'debug' + 'notification' + 'showcallinfo' + 'trackcallinfo' 'trackconfig' - 'trackusage' 'trackdatainfo' - 'trackcallinfo' - 'showcallinfo' + 'trackusage' 'warning' - 'notification' - 'debug' - 'progress' }; - case 'trackconfig' ignore = { % these fields from the user should be ignored @@ -111,7 +125,6 @@ 'artifact' 'artfctdef' % these fields are for internal usage only - 'postamble' 'checkconfig' 'checksize' 'trackconfig' @@ -125,6 +138,7 @@ 'notification' 'debug' 'previous' + 'hastoolbox' }; case 'checksize' @@ -137,6 +151,24 @@ 'artifact' 'artfctdef' 'previous' + 'hastoolbox' + }; + + case 'makessense' + ignore = { + % these fields should not be used to check whether the trialinfo and sampleinfo make sense + 'label' + 'time' + 'freq' + 'hdr' + 'fsample' + 'dimord' + 'trialinfo' + 'sampleinfo' + 'grad' + 'elec' + 'opto' + 'cfg' }; otherwise diff --git a/external/fieldtrip/private/isalmostequal.m b/external/fieldtrip/private/isalmostequal.m new file mode 100644 index 00000000..cfb1a36c --- /dev/null +++ b/external/fieldtrip/private/isalmostequal.m @@ -0,0 +1,200 @@ +function [ok, message] = isalmostequal(a, b, varargin) + +% ISALMOSTEQUAL compares two input variables and returns true/false +% and a message containing the details on the observed difference. +% +% Use as +% [ok, message] = isalmostequal(a, b) +% [ok, message] = isalmostequal(a, b, ...) +% +% This works for all possible input variables a and b, like +% numerical arrays, string arrays, cell arrays, structures +% and nested data types. +% +% Optional input arguments come in key-value pairs, supported are +% 'depth' number, for nested structures +% 'abstol' number, absolute tolerance for numerical comparison +% 'reltol' number, relative tolerance for numerical comparison +% 'diffabs' boolean, check difference between absolute values for numericals (useful for e.g. mixing matrices which have arbitrary signs) +% +% See also ISEQUAL, ISEQUALNAN + +% Copyright (C) 2004-2012, Robert Oostenveld & Markus Siegel +% +% $Id$ + +if nargin==3 + % for backward compatibility + depth = varargin{1}; +else + depth = keyval('depth', varargin); + if isempty(depth) + % set the default + depth = inf; + end +end + +message = {}; +location = ''; + +[message] = do_work(a, b, depth, location, message, varargin{:}); +message = message(:); +ok = isempty(message); + +if ~nargout + disp(message); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [message] = do_work(a, b, depth, location, message, varargin) + +knowntypes = { + 'double' % Double precision floating point numeric array + 'logical' % Logical array + 'char' % Character array + 'cell' % Cell array + 'struct' % Structure array + 'numeric' % Integer or floating-point array + 'single' % Single precision floating-point numeric array + 'int8' % 8-bit signed integer array + 'uint8' % 8-bit unsigned integer array + 'int16' % 16-bit signed integer array + 'uint16' % 16-bit unsigned integer array + 'int32' % 32-bit signed integer array + 'uint32' % 32-bit unsigned integer array + }; + +for type=knowntypes(:)' + if isa(a, type{:}) && ~isa(b, type{:}) + message{end+1} = sprintf('different data type in %s', location); + return + end +end + +if isempty(location) + location = 'array'; +end + +if isa(a, 'numeric') || isa(a, 'char') || isa(a, 'logical') + % perform numerical comparison + if length(size(a))~=length(size(b)) + message{end+1} = sprintf('different number of dimensions in %s', location); + return; + end + if any(size(a)~=size(b)) + message{end+1} = sprintf('different size in %s', location); + return; + end + if ~all(isnan(a(:)) == isnan(b(:))) + message{end+1} = sprintf('different occurence of NaNs in %s', location); + return; + end + % replace the NaNs, since we cannot compare them numerically + a = a(~isnan(a(:))); + b = b(~isnan(b(:))); + % continue with numerical comparison + if ischar(a) && any(a~=b) + message{end+1} = sprintf('different string in %s: %s ~= %s', location, a, b); + else + % use the desired tolerance + reltol = keyval('reltol', varargin{:}); % any value, relative to the mean + abstol = keyval('abstol', varargin{:}); % any value + relnormtol = keyval('relnormtol', varargin{:}); % the matrix norm, relative to the mean norm + absnormtol = keyval('absnormtol', varargin{:}); % the matrix norm + diffabs = keyval('diffabs', varargin{:}); + + if ~isempty(diffabs) && diffabs + a = abs(a); + b = abs(b); + end + + if ~isempty(abstol) && any(abs(a-b)>abstol) + message{end+1} = sprintf('different values in %s', location); + elseif ~isempty(reltol) && any((abs(a-b)./(0.5*(a+b)))>reltol) + message{end+1} = sprintf('different values in %s', location); + elseif isempty(abstol) && isempty(reltol) && any(a~=b) + message{end+1} = sprintf('different values in %s', location); + elseif ~isempty(relnormtol) && (norm(a-b)/(0.5*(norm(a)+norm(b)))>relnormtol) + message{end+1} = sprintf('different values in %s', location); + elseif ~isempty(absnormtol) && norm(a-b)>absnormtol + message{end+1} = sprintf('different values in %s', location); + end + end + +elseif isa(a, 'struct') && all(size(a)==1) + % perform recursive comparison of all fields of the structure + fna = fieldnames(a); + fnb = fieldnames(b); + if ~all(ismember(fna, fnb)) + tmp = fna(~ismember(fna, fnb)); + for i=1:length(tmp) + message{end+1} = sprintf('field missing in the 2nd argument in %s: {%s}', location, tmp{i}); + end + end + if ~all(ismember(fnb, fna)) + tmp = fnb(~ismember(fnb, fna)); + for i=1:length(tmp) + message{end+1} = sprintf('field missing in the 1st argument in %s: {%s}', location, tmp{i}); + end + end + fna = intersect(fna, fnb); + if depth>0 + % warning, this is a recursive call to transverse nested structures + for i=1:length(fna) + fn = fna{i}; + ra = getfield(a, fn); + rb = getfield(b, fn); + [message] = do_work(ra, rb, depth-1, [location '.' fn], message, varargin{:}); + end + end + +elseif isa(a, 'struct') && ~all(size(a)==1) + % perform recursive comparison of all array elements + if any(size(a)~=size(b)) + message{end+1} = sprintf('different size of struct-array in %s', location); + return; + end + siz = size(a); + dim = ndims(a); + a = a(:); + b = b(:); + for i=1:length(a) + ra = a(i); + rb = b(i); + tmp = sprintf('%s(%s)', location, my_ind2sub(siz, i)); + [message] = do_work(ra, rb, depth-1, tmp, message, varargin{:}); + end + +elseif isa(a, 'cell') + % perform recursive comparison of all array elements + if any(size(a)~=size(b)) + message{end+1} = sprintf('different size of cell-array in %s', location); + return; + end + siz = size(a); + dim = ndims(a); + a = a(:); + b = b(:); + for i=1:length(a) + ra = a{i}; + rb = b{i}; + tmp = sprintf('%s{%s}', location, my_ind2sub(siz, i)); + [message] = do_work(ra, rb, depth-1, tmp, message, varargin{:}); + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% return a string with the formatted subscript +function [str] = my_ind2sub(siz,ndx) +n = length(siz); +k = [1 cumprod(siz(1:end-1))]; +ndx = ndx - 1; +for i = n:-1:1, + tmp(i) = floor(ndx/k(i))+1; + ndx = rem(ndx,k(i)); +end +str = ''; +for i=1:n + str = [str ',' num2str(tmp(i))]; +end +str = str(2:end); diff --git a/external/fieldtrip/private/ismatch.m b/external/fieldtrip/private/ismatch.m new file mode 100644 index 00000000..7c44a52d --- /dev/null +++ b/external/fieldtrip/private/ismatch.m @@ -0,0 +1,35 @@ +function s = ismatch(x, y) + +% ISMATCH returns true if x is a member of array y, regardless of the class +% of x and y, if y is a string, or a cell-array of strings, it can contain +% the wildcard '*' + +if isempty(x) || isempty(y) + s = false; +elseif ischar(x) && ischar(y) + y = sprintf('%s%s%s', '^', regexptranslate('wildcard',y), '$'); + s = ~isempty(regexp(x, y, 'once')); +elseif isnumeric(x) && isnumeric(y) + s = ismember(x, y); +elseif ischar(x) && iscell(y) + y = y(strcmp(class(x), cellfun(@class, y, 'UniformOutput', false))); + s = ismember(x, y); + + % one or more of the elements in y can contain a wildcard, only proceed if + % s=false + if ~s && any(contains(y, '*')) + y = y(contains(y, '*')); + for i = 1:numel(y) + tmpy = sprintf('%s%s%s', '^', regexptranslate('wildcard',y{i}), '$'); + s = ~isempty(regexp(x, tmpy, 'once')); + if s, return; end + end + end +elseif isnumeric(x) && iscell(y) && all(cellfun(@isnumeric, y)) + s = false; + for i=1:numel(y) + s = s || ismember(x, y{i}); + end +else + s = false; +end diff --git a/external/fieldtrip/private/labelcmb2indx.m b/external/fieldtrip/private/labelcmb2indx.m index 8fc4dc2e..fbf3e285 100644 --- a/external/fieldtrip/private/labelcmb2indx.m +++ b/external/fieldtrip/private/labelcmb2indx.m @@ -65,9 +65,6 @@ [indx, label] = labelcmb2indx(tmplabelcmb, unique(tmplabelcmb(:))); [blockindx, blocklabel] = labelcmb2indx(tmpblock, blocklabel); blockindx = blockindx(:,1); -% for k = 1:numel(blocklabel) -% nperblock(k,1) = sum(blockindx==k); -% end return; end @@ -82,18 +79,13 @@ nchan = numel(label); for k = 1:nchan - %sel1 = strcmp(label{k}, labelcmb(:,1)); - %sel2 = strcmp(label{k}, labelcmb(:,2)); sel = strcmp(label{k}, labelcmb); if nargin==1, - %autoindx = find(sel1 & sel2); autoindx = find(sel(:,1) & sel(:,2), 1, 'first'); if isempty(autoindx), ft_error('the required autocombination is not found in the input'); end else autoindx = k; end - %indx(sel1,1) = autoindx; - %indx(sel2,2) = autoindx; indx(sel(:,1),1) = autoindx; indx(sel(:,2),2) = autoindx; end diff --git a/external/fieldtrip/private/lmoutr.mexw32 b/external/fieldtrip/private/lmoutr.mexw32 index d367b100..f2f1837a 100755 Binary files a/external/fieldtrip/private/lmoutr.mexw32 and b/external/fieldtrip/private/lmoutr.mexw32 differ diff --git a/external/fieldtrip/private/lmoutr.mexw64 b/external/fieldtrip/private/lmoutr.mexw64 index ab5adac3..01167e39 100755 Binary files a/external/fieldtrip/private/lmoutr.mexw64 and b/external/fieldtrip/private/lmoutr.mexw64 differ diff --git a/external/fieldtrip/private/mergeconfig.m b/external/fieldtrip/private/mergeconfig.m index bc0a6ef1..4adba379 100644 --- a/external/fieldtrip/private/mergeconfig.m +++ b/external/fieldtrip/private/mergeconfig.m @@ -1,18 +1,23 @@ -function input = mergeconfig(input, default) +function original = mergeconfig(original, default, emptymeaningful) -% MERGECONFIG merges the fields of a default structure into an input -% configuration structure +% MERGECONFIG merges the fields of a structure with defaults into an original +% configuration structure. The default is only copied in case the field is absent in +% the original. % % Use as -% output = mergeconfig(input, default) +% output = mergeconfig(original, default) % Copyright (C) 2009-2012, Robert Oostenveld % % $Id$ -if isempty(input) && ~isstruct(input) +if nargin<3 + emptymeaningful = true; +end + +if isempty(original) && ~isstruct(original) % ensure that it is an empty struct, not an empty double - input = struct; + original = struct; end if isempty(default) && ~isstruct(default) @@ -20,30 +25,35 @@ default = struct; end -% merge the input with the fields from default +if ~emptymeaningful + original = remove_empty(original); + default = remove_empty(default); +end + +% merge the original with the fields from default defaultfields = fieldnames(default); -inputfields = fieldnames(input); +inputfields = fieldnames(original); allfields = union(defaultfields, inputfields); -if numel(default)>1 && numel(default)==numel(input) +if numel(default)>1 && numel(default)==numel(original) % create an empty structure that has all the fields - tmp = emptystruct(allfields); - for i=1:numel(input) + tmp = empty_struct(allfields); + for i=1:numel(original) for j=1:numel(allfields) - if isfield(input(i), allfields{j}) - tmp(i).(allfields{j}) = input(i).(allfields{j}); + if isfield(original(i), allfields{j}) + tmp(i).(allfields{j}) = original(i).(allfields{j}); else tmp(i).(allfields{j}) = default(i).(allfields{j}); end end end - input = tmp; + original = tmp; -elseif numel(default)~=numel(input) +elseif numel(default)~=numel(original) for j=1:numel(allfields) - % ensure that the input has all fields - if ~isfield(input, allfields{j}) - input(1).(allfields{j}) = []; + % ensure that the original has all fields + if ~isfield(original, allfields{j}) + original(1).(allfields{j}) = []; end % ensure that the default has all fields if ~isfield(default, allfields{j}) @@ -51,31 +61,43 @@ end end % simply concatenate them - input = cat(1, input(:), default(:)); + original = cat(1, original(:), default(:)); else for i=1:length(defaultfields) fn = defaultfields{i}; - if ~isfield(input, fn) && ~isstruct(default.(fn)) + if ~isfield(original, fn) && ~isstruct(default.(fn)) % simply copy the value over - input.(fn) = default.(fn); - elseif ~isfield(input, fn) && isstruct(default.(fn)) + original.(fn) = default.(fn); + elseif ~isfield(original, fn) && isstruct(default.(fn)) % simply copy the substructure over - input.(fn) = default.(fn); - elseif isfield(input, fn) && ~isstruct(default.(fn)) + original.(fn) = default.(fn); + elseif isfield(original, fn) && ~isstruct(default.(fn)) % do not copy it over, keep the original value - elseif isfield(input, fn) && isstruct(default.(fn)) + elseif isfield(original, fn) && isstruct(default.(fn)) % merge the two substructures using recursive call - input.(fn) = mergeconfig(input.(fn), default.(fn)); + original.(fn) = mergeconfig(original.(fn), default.(fn)); end end % for all default fields end % dealing with struct-arrays +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function s = remove_empty(s) +if isempty(s) + return +else + fn = fieldnames(s); + fn = fn(structfun(@isempty, s)); + s = removefields(s, fn); +end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SUBFUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function s = emptystruct(fn) +function s = empty_struct(fn) s = struct; for i=1:numel(fn) s.(fn{i}) = []; diff --git a/external/fieldtrip/private/mesh_spectrum.m b/external/fieldtrip/private/mesh_spectrum.m index 14c6fcc6..8d9d45ce 100644 --- a/external/fieldtrip/private/mesh_spectrum.m +++ b/external/fieldtrip/private/mesh_spectrum.m @@ -34,7 +34,7 @@ elseif length(pnt)==2&&j == 2 disp('Computing the spectrum of the the second hemisphere') end - [L{j},~] = mesh_laplacian(pnt{j},tri{j}); + L{j} = mesh_laplacian(pnt{j},tri{j}); L{j} = (L{j} + L{j}')/2; disp('Computing the spectrum of the negative Laplacian matrix') [H{j},D] = eigs(L{j},n,'sm'); diff --git a/external/fieldtrip/private/multivariate_decomp.m b/external/fieldtrip/private/multivariate_decomp.m new file mode 100644 index 00000000..7892eae9 --- /dev/null +++ b/external/fieldtrip/private/multivariate_decomp.m @@ -0,0 +1,171 @@ +function [E, D] = multivariate_decomp(C,x,y,method,realflag,thr) + +% MULTIVARIATE_DECOMP does a linear decomposition of multivariate time series, +% based on the covariance matrix. +% +% Use as: +% [E, D] = multivariate_decomp(C,x,y,method) +% +% Input arguments: +% C = covariance matrix (or csd) between input time series +% x = list of indices corresponding to group 1 +% y = list of indices corresponding to group 2 +% method = 'cca', or 'pls', 'mlr', decomposition method +% (canonical correlation partial least squares, or multivariate +% linear regression). In the case of mlr-like decompositions, +% the indices for x reflect the independent variable) +% realflag = true (default) or false. Do the operation on the real part +% of the matrix if the input matrix is complex-valued +% +% The implementation is based on Borga 2001, Canonical correlation, a +% tutorial (can be found online). +% +% Output arguments: +% E = projection matrix (not necessarily normalized). to get the orientation, +% do orix = E(x,1)./norm(E(x,1)), and oriy = E(y,1)./norm(E(y,1)); +% D = diagonal matrix with eigenvalues + +if nargin<6 + thr = 0.9; % percentage of variance to account for when doing ccasvd +end + +if numel(thr)==1 + thr = [thr thr]; +end + +if nargin<5 + realflag = true; +end + +if nargin<4 + method = 'cca'; +end + +A = C; +A(x,x) = 0; % put the auto covariances to 0, keep the cross-block covariances +A(y,y) = 0; + +switch method + case {'pls' 'plssvd' 'plsridge' 'plsqridge'} + % partial least-squares + B = eye(numel(x)+numel(y)); + case {'cca' 'ccasvd' 'ccaridge' 'ccaqridge'} + % canonical correlation + B = C; + B(x,y) = 0; % put the cross-block covariances to 0, keep the auto covariances + B(y,x) = 0; + + case {'mlr' 'mlrsvd' 'mlrridge' 'mlrqridge'} + % regression + B = zeros(size(A)); + B(x,x) = C(x,x); + B(y,y) = eye(numel(y)); + otherwise + error('unsupported method'); +end + +% regularize with a ridge +if ~isempty(strfind(method, 'ridge')) + if ~isempty(strfind(method, 'qridge')) + type = 'qridge'; + else + type = 'ridge'; + end + % regularize B with a ridge matrix + R = ridge_matrix(x, y, type, thr); + B = B+R; +end + +% perform the decomposition in a svd-based subspace +if ~isempty(strfind(method, 'svd')) + [ux,sx] = svd(B(x,x)); + [uy,sy] = svd(B(y,y)); + + if thr(1)<1 + keepx = cumsum(diag(sx))./sum(diag(sx))<=thr(1); keepx(1) = true; + else + keepx = false(numel(x),1); + keepx(1:min(thr(1),numel(x))) = true; + end + if thr(2)<1 + keepy = cumsum(diag(sy))./sum(diag(sy))<=thr(2); keepy(1) = true; + else + keepy = false(numel(y),1); + keepy(1:min(thr(2),numel(y))) = true; + end + U = blkdiag(ux(:,keepx),uy(:,keepy)); + + A = U'*A*U; + B = U'*B*U; + + A = (A+A')./2; + B = (B+B')./2; +end + +% ad hoc check for well-behavedness of the matrix, and do the decomposition +if cond(B)>1e8 + [E,D] = eig(pinv(B)*A); +else + if realflag + [E,D] = eig(real(B\A)); + E = real(E); + D = real(D); + else + [E,D] = eig(B\A); + end +end + +n = min(numel(x),numel(y)); +n = min(n, numel(diag(D))); +[D, order] = sort(diag(D), 'descend'); +E = E(:, order(1:n)); +D = D(1:n); + +if ~isempty(strfind(method, 'svd')) + E = U*E; +end + +[E(x,:),norm_x] = normc(E(x,:)); % norm normalise the coefficients per block +[E(y,:),norm_y] = normc(E(y,:)); +if ~isempty(strfind(method, 'mlr')) + % scale the weights for the independent variable, such that they reflect + % proper beta weights + E(x,:) = E(x,:).*D(:)'.*(norm_x./norm_y); + + % output the ssq error in D + D = trace(C(y,y))-(sum((E(y,:)'*C(y,x)).*E(x,:)',2).^2)./(sum((E(x,:)'*C(x,x)).*E(x,:)',2)); +end + +function R = ridge_matrix(x, y, type, thr) + +if numel(thr)==numel(x)+numel(y) + thr = diag(thr); +elseif numel(thr)==2 + tmp = zeros(numel(x)+numel(y),1); + tmp(x) = thr(1); + tmp(y) = thr(2); + thr = diag(tmp); +end + +nx = numel(x); +ny = numel(y); +switch type + case 'ridge' + R = eye(nx+ny); + case 'qridge' + rix = eye(nx).*2 + diag(ones(nx-1,1).*-1,1) + diag(ones(nx-1,1).*-1,-1); + rix(1) = 1; + rix(end) = 1; + riy = eye(ny).*2 + diag(ones(ny-1,1).*-1,1) + diag(ones(ny-1,1).*-1,-1); + riy(1) = 1; + riy(end) = 1; + R = blkdiag(rix,riy); +end +R = R*thr; + +function [y, x_norm] = normc(x) + +% NORMC column-wise norm normalisation + +x_norm = sqrt(sum(x.^2)); +y = x*diag(1./x_norm); diff --git a/external/fieldtrip/private/mxDeserialize.m b/external/fieldtrip/private/mxDeserialize.m index d9b4843f..243bf2ed 100644 --- a/external/fieldtrip/private/mxDeserialize.m +++ b/external/fieldtrip/private/mxDeserialize.m @@ -6,7 +6,7 @@ % See also MXSERIALIZE % Copyright (C) 2005, Brad Phelan http://xtargets.com -% Copyright (C) 2007, Robert Oostenveld http://www.fcdonders.ru.nl +% Copyright (C) 2007, Robert Oostenveld http://robertoostenveld.nl % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. diff --git a/external/fieldtrip/private/mxSerialize.m b/external/fieldtrip/private/mxSerialize.m index a2534051..b476fd28 100644 --- a/external/fieldtrip/private/mxSerialize.m +++ b/external/fieldtrip/private/mxSerialize.m @@ -6,7 +6,7 @@ % See also MXDESERIALIZE % Copyright (C) 2005, Brad Phelan http://xtargets.com -% Copyright (C) 2007, Robert Oostenveld http://www.fcdonders.ru.nl +% Copyright (C) 2007, Robert Oostenveld http://robertoostenveld.nl % % This file is part of FieldTrip, see http://www.fieldtriptoolbox.org % for the documentation and details. diff --git a/external/fieldtrip/private/parsekeyboardevent.m b/external/fieldtrip/private/parsekeyboardevent.m new file mode 100644 index 00000000..e80892f3 --- /dev/null +++ b/external/fieldtrip/private/parsekeyboardevent.m @@ -0,0 +1,24 @@ +function key = parsekeyboardevent(eventdata) + +% PARSEKEYBOARDEVENT handles keyboard events for Windows, Mac OSX and Linux systems. +% +% shift+numpad number does not work on UNIX, since the shift modifier is always sent for numpad events + +key = eventdata.Key; +if isunix() + shiftInd = match_str(eventdata.Modifier, 'shift'); + if ~isnan(str2double(eventdata.Character)) && ~isempty(shiftInd) + % now we now it was a numpad keystroke (numeric character sent AND + % shift modifier present) + key = eventdata.Character; + eventdata.Modifier(shiftInd) = []; % strip the shift modifier + end +elseif ispc() + if strfind(eventdata.Key, 'numpad') + key = eventdata.Character;d + end +end + +if ~isempty(eventdata.Modifier) + key = [eventdata.Modifier{1} '+' key]; +end diff --git a/external/fieldtrip/private/pipeline-skeleton.html b/external/fieldtrip/private/pipeline-skeleton.html index 2bff853f..5268c6db 100755 --- a/external/fieldtrip/private/pipeline-skeleton.html +++ b/external/fieldtrip/private/pipeline-skeleton.html @@ -4,15 +4,15 @@ FieldTrip analysis pipeline, ${TIMESTAMP} - + - - - + + +

Welcome to SPM12

-

Please refer to this version as "SPM12" in papers and communications.

+

Please refer to this version as "SPM12" in papers and communications.

@@ -31,7 +33,7 @@

Welcome to SPM12

The SPM12 Manual and Release Notes are available as PDF documents in the man directory of your SPM installation.

-

Updates will be made available from time to time and advertised on the SPM mailing list. You can also check for updates by clicking here.

+

Updates will be made available from time to time and advertised on the SPM mailing list. You can also check for updates by clicking here.

We would love to hear your comments or bug reports - please contact us at <fil.spm@ucl.ac.uk>.

@@ -47,7 +49,7 @@

Welcome to SPM12

of the Licence, or (at your option) any later version.

diff --git a/help/js/plotly.min.js b/help/js/plotly.min.js new file mode 100644 index 00000000..18a5516c --- /dev/null +++ b/help/js/plotly.min.js @@ -0,0 +1,7 @@ +/** +* plotly.js v1.38.3 +* Copyright 2012-2018, Plotly, Inc. +* All rights reserved. +* Licensed under the MIT license +*/ +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Plotly=t()}}(function(){return function(){return function t(e,r,n){function i(o,s){if(!r[o]){if(!e[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(a)return a(o,!0);var c=new Error("Cannot find module '"+o+"'");throw c.code="MODULE_NOT_FOUND",c}var u=r[o]={exports:{}};e[o][0].call(u.exports,function(t){var r=e[o][1][t];return i(r||t)},u,u.exports,t,e,r,n)}return r[o].exports}for(var a="function"==typeof require&&require,o=0;oMath.abs(e))c.rotate(o,0,0,-t*i*Math.PI*d.rotateSpeed/window.innerWidth);else{var s=d.zoomSpeed*a*e/window.innerHeight*(o-c.lastT())/100;c.pan(o,0,0,f*(Math.exp(s)-1))}},!0),d};var n=t("right-now"),i=t("3d-view"),a=t("mouse-change"),o=t("mouse-wheel"),s=t("mouse-event-offset"),l=t("has-passive-events")},{"3d-view":42,"has-passive-events":355,"mouse-change":378,"mouse-event-offset":379,"mouse-wheel":381,"right-now":440}],42:[function(t,e,r){"use strict";e.exports=function(t){var e=(t=t||{}).eye||[0,0,1],r=t.center||[0,0,0],s=t.up||[0,1,0],l=t.distanceLimits||[0,1/0],c=t.mode||"turntable",u=n(),f=i(),h=a();return u.setDistanceLimits(l[0],l[1]),u.lookAt(0,e,r,s),f.setDistanceLimits(l[0],l[1]),f.lookAt(0,e,r,s),h.setDistanceLimits(l[0],l[1]),h.lookAt(0,e,r,s),new o({turntable:u,orbit:f,matrix:h},c)};var n=t("turntable-camera-controller"),i=t("orbit-camera-controller"),a=t("matrix-camera-controller");function o(t,e){this._controllerNames=Object.keys(t),this._controllerList=this._controllerNames.map(function(e){return t[e]}),this._mode=e,this._active=t[e],this._active||(this._mode="turntable",this._active=t.turntable),this.modes=this._controllerNames,this.computedMatrix=this._active.computedMatrix,this.computedEye=this._active.computedEye,this.computedUp=this._active.computedUp,this.computedCenter=this._active.computedCenter,this.computedRadius=this._active.computedRadius}var s=o.prototype;[["flush",1],["idle",1],["lookAt",4],["rotate",4],["pan",4],["translate",4],["setMatrix",2],["setDistanceLimits",2],["setDistance",2]].forEach(function(t){for(var e=t[0],r=[],n=0;n0;--t)p(c*=.99),d(),h(c),d();function h(t){function r(t){return u(t.source)*t.value}i.forEach(function(n){n.forEach(function(n){if(n.targetLinks.length){var i=e.sum(n.targetLinks,r)/e.sum(n.targetLinks,f);n.y+=(i-u(n))*t}})})}function p(t){function r(t){return u(t.target)*t.value}i.slice().reverse().forEach(function(n){n.forEach(function(n){if(n.sourceLinks.length){var i=e.sum(n.sourceLinks,r)/e.sum(n.sourceLinks,f);n.y+=(i-u(n))*t}})})}function d(){i.forEach(function(t){var e,r,n,i=0,s=t.length;for(t.sort(g),n=0;n0&&(e.y+=r),i=e.y+e.dy+a;if((r=i-a-o[1])>0)for(i=e.y-=r,n=s-2;n>=0;--n)e=t[n],(r=e.y+e.dy+a-i)>0&&(e.y-=r),i=e.y})}function g(t,e){return t.y-e.y}}(n),c(),t},t.relayout=function(){return c(),t},t.link=function(){var t=.5;function e(e){var r=e.source.x+e.source.dx,i=e.target.x,a=n.interpolateNumber(r,i),o=a(t),s=a(1-t),l=e.source.y+e.sy,c=l+e.dy,u=e.target.y+e.ty,f=u+e.dy;return"M"+r+","+l+"C"+o+","+l+" "+s+","+u+" "+i+","+u+"L"+i+","+f+"C"+s+","+f+" "+o+","+c+" "+r+","+c+"Z"}return e.curvature=function(r){return arguments.length?(t=+r,e):t},e},t},Object.defineProperty(t,"__esModule",{value:!0})},"object"==typeof r&&void 0!==e?i(r,t("d3-array"),t("d3-collection"),t("d3-interpolate")):i(n.d3=n.d3||{},n.d3,n.d3,n.d3)},{"d3-array":123,"d3-collection":124,"d3-interpolate":128}],44:[function(t,e,r){"use strict";var n="undefined"==typeof WeakMap?t("weak-map"):WeakMap,i=t("gl-buffer"),a=t("gl-vao"),o=new n;e.exports=function(t){var e=o.get(t),r=e&&(e._triangleBuffer.handle||e._triangleBuffer.buffer);if(!r||!t.isBuffer(r)){var n=i(t,new Float32Array([-1,-1,-1,4,4,-1]));(e=a(t,[{buffer:n,type:t.FLOAT,size:2}]))._triangleBuffer=n,o.set(t,e)}e.bind(),t.drawArrays(t.TRIANGLES,0,3),e.unbind()}},{"gl-buffer":211,"gl-vao":284,"weak-map":490}],45:[function(t,e,r){e.exports=function(t){var e=0,r=0,n=0,i=0;return t.map(function(t){var a=(t=t.slice())[0],o=a.toUpperCase();if(a!=o)switch(t[0]=o,a){case"a":t[6]+=n,t[7]+=i;break;case"v":t[1]+=i;break;case"h":t[1]+=n;break;default:for(var s=1;si&&(i=t[o]),t[o]=0;c--)if(u[c]!==f[c])return!1;for(c=u.length-1;c>=0;c--)if(l=u[c],!y(t[l],e[l],r,n))return!1;return!0}(t,e,r,o))}return r?t===e:t==e}function x(t){return"[object Arguments]"==Object.prototype.toString.call(t)}function b(t,e){if(!t||!e)return!1;if("[object RegExp]"==Object.prototype.toString.call(e))return e.test(t);try{if(t instanceof e)return!0}catch(t){}return!Error.isPrototypeOf(e)&&!0===e.call({},t)}function _(t,e,r,n){var i;if("function"!=typeof e)throw new TypeError('"block" argument must be a function');"string"==typeof r&&(n=r,r=null),i=function(t){var e;try{t()}catch(t){e=t}return e}(e),n=(r&&r.name?" ("+r.name+").":".")+(n?" "+n:"."),t&&!i&&m(i,r,"Missing expected exception"+n);var o="string"==typeof n,s=!t&&i&&!r;if((!t&&a.isError(i)&&o&&b(i,r)||s)&&m(i,r,"Got unwanted exception"+n),t&&i&&r&&!b(i,r)||!t&&i)throw i}f.AssertionError=function(t){var e;this.name="AssertionError",this.actual=t.actual,this.expected=t.expected,this.operator=t.operator,t.message?(this.message=t.message,this.generatedMessage=!1):(this.message=d(g((e=this).actual),128)+" "+e.operator+" "+d(g(e.expected),128),this.generatedMessage=!0);var r=t.stackStartFunction||m;if(Error.captureStackTrace)Error.captureStackTrace(this,r);else{var n=new Error;if(n.stack){var i=n.stack,a=p(r),o=i.indexOf("\n"+a);if(o>=0){var s=i.indexOf("\n",o+1);i=i.substring(s+1)}this.stack=i}}},a.inherits(f.AssertionError,Error),f.fail=m,f.ok=v,f.equal=function(t,e,r){t!=e&&m(t,e,r,"==",f.equal)},f.notEqual=function(t,e,r){t==e&&m(t,e,r,"!=",f.notEqual)},f.deepEqual=function(t,e,r){y(t,e,!1)||m(t,e,r,"deepEqual",f.deepEqual)},f.deepStrictEqual=function(t,e,r){y(t,e,!0)||m(t,e,r,"deepStrictEqual",f.deepStrictEqual)},f.notDeepEqual=function(t,e,r){y(t,e,!1)&&m(t,e,r,"notDeepEqual",f.notDeepEqual)},f.notDeepStrictEqual=function t(e,r,n){y(e,r,!0)&&m(e,r,n,"notDeepStrictEqual",t)},f.strictEqual=function(t,e,r){t!==e&&m(t,e,r,"===",f.strictEqual)},f.notStrictEqual=function(t,e,r){t===e&&m(t,e,r,"!==",f.notStrictEqual)},f.throws=function(t,e,r){_(!0,t,e,r)},f.doesNotThrow=function(t,e,r){_(!1,t,e,r)},f.ifError=function(t){if(t)throw t};var w=Object.keys||function(t){var e=[];for(var r in t)o.call(t,r)&&e.push(r);return e}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"util/":487}],54:[function(t,e,r){e.exports=function(t){return atob(t)}},{}],55:[function(t,e,r){"use strict";e.exports=function(t,e){for(var r=e.length,a=new Array(r+1),o=0;o0?l-4:l;var u=0;for(e=0;e>16&255,s[u++]=n>>8&255,s[u++]=255&n;2===o?(n=i[t.charCodeAt(e)]<<2|i[t.charCodeAt(e+1)]>>4,s[u++]=255&n):1===o&&(n=i[t.charCodeAt(e)]<<10|i[t.charCodeAt(e+1)]<<4|i[t.charCodeAt(e+2)]>>2,s[u++]=n>>8&255,s[u++]=255&n);return s},r.fromByteArray=function(t){for(var e,r=t.length,i=r%3,a="",o=[],s=0,l=r-i;sl?l:s+16383));1===i?(e=t[r-1],a+=n[e>>2],a+=n[e<<4&63],a+="=="):2===i&&(e=(t[r-2]<<8)+t[r-1],a+=n[e>>10],a+=n[e>>4&63],a+=n[e<<2&63],a+="=");return o.push(a),o.join("")};for(var n=[],i=[],a="undefined"!=typeof Uint8Array?Uint8Array:Array,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0,l=o.length;s0)throw new Error("Invalid string. Length must be a multiple of 4");return"="===t[e-2]?2:"="===t[e-1]?1:0}function u(t,e,r){for(var i,a,o=[],s=e;s>18&63]+n[a>>12&63]+n[a>>6&63]+n[63&a]);return o.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},{}],57:[function(t,e,r){"use strict";var n=t("./lib/rationalize");e.exports=function(t,e){return n(t[0].mul(e[1]).add(e[0].mul(t[1])),t[1].mul(e[1]))}},{"./lib/rationalize":67}],58:[function(t,e,r){"use strict";e.exports=function(t,e){return t[0].mul(e[1]).cmp(e[0].mul(t[1]))}},{}],59:[function(t,e,r){"use strict";var n=t("./lib/rationalize");e.exports=function(t,e){return n(t[0].mul(e[1]),t[1].mul(e[0]))}},{"./lib/rationalize":67}],60:[function(t,e,r){"use strict";var n=t("./is-rat"),i=t("./lib/is-bn"),a=t("./lib/num-to-bn"),o=t("./lib/str-to-bn"),s=t("./lib/rationalize"),l=t("./div");e.exports=function t(e,r){if(n(e))return r?l(e,t(r)):[e[0].clone(),e[1].clone()];var c=0;var u,f;if(i(e))u=e.clone();else if("string"==typeof e)u=o(e);else{if(0===e)return[a(0),a(1)];if(e===Math.floor(e))u=a(e);else{for(;e!==Math.floor(e);)e*=Math.pow(2,256),c-=256;u=a(e)}}if(n(r))u.mul(r[1]),f=r[0].clone();else if(i(r))f=r.clone();else if("string"==typeof r)f=o(r);else if(r)if(r===Math.floor(r))f=a(r);else{for(;r!==Math.floor(r);)r*=Math.pow(2,256),c+=256;f=a(r)}else f=a(1);c>0?u=u.ushln(c):c<0&&(f=f.ushln(-c));return s(u,f)}},{"./div":59,"./is-rat":61,"./lib/is-bn":65,"./lib/num-to-bn":66,"./lib/rationalize":67,"./lib/str-to-bn":68}],61:[function(t,e,r){"use strict";var n=t("./lib/is-bn");e.exports=function(t){return Array.isArray(t)&&2===t.length&&n(t[0])&&n(t[1])}},{"./lib/is-bn":65}],62:[function(t,e,r){"use strict";var n=t("bn.js");e.exports=function(t){return t.cmp(new n(0))}},{"bn.js":76}],63:[function(t,e,r){"use strict";var n=t("./bn-sign");e.exports=function(t){var e=t.length,r=t.words,i=0;if(1===e)i=r[0];else if(2===e)i=r[0]+67108864*r[1];else for(var a=0;a20)return 52;return r+32}},{"bit-twiddle":74,"double-bits":134}],65:[function(t,e,r){"use strict";t("bn.js");e.exports=function(t){return t&&"object"==typeof t&&Boolean(t.words)}},{"bn.js":76}],66:[function(t,e,r){"use strict";var n=t("bn.js"),i=t("double-bits");e.exports=function(t){var e=i.exponent(t);return e<52?new n(t):new n(t*Math.pow(2,52-e)).ushln(e-52)}},{"bn.js":76,"double-bits":134}],67:[function(t,e,r){"use strict";var n=t("./num-to-bn"),i=t("./bn-sign");e.exports=function(t,e){var r=i(t),a=i(e);if(0===r)return[n(0),n(1)];if(0===a)return[n(0),n(0)];a<0&&(t=t.neg(),e=e.neg());var o=t.gcd(e);if(o.cmpn(1))return[t.div(o),e.div(o)];return[t,e]}},{"./bn-sign":62,"./num-to-bn":66}],68:[function(t,e,r){"use strict";var n=t("bn.js");e.exports=function(t){return new n(t)}},{"bn.js":76}],69:[function(t,e,r){"use strict";var n=t("./lib/rationalize");e.exports=function(t,e){return n(t[0].mul(e[0]),t[1].mul(e[1]))}},{"./lib/rationalize":67}],70:[function(t,e,r){"use strict";var n=t("./lib/bn-sign");e.exports=function(t){return n(t[0])*n(t[1])}},{"./lib/bn-sign":62}],71:[function(t,e,r){"use strict";var n=t("./lib/rationalize");e.exports=function(t,e){return n(t[0].mul(e[1]).sub(t[1].mul(e[0])),t[1].mul(e[1]))}},{"./lib/rationalize":67}],72:[function(t,e,r){"use strict";var n=t("./lib/bn-to-num"),i=t("./lib/ctz");e.exports=function(t){var e=t[0],r=t[1];if(0===e.cmpn(0))return 0;var a=e.abs().divmod(r.abs()),o=a.div,s=n(o),l=a.mod,c=e.negative!==r.negative?-1:1;if(0===l.cmpn(0))return c*s;if(s){var u=i(s)+4,f=n(l.ushln(u).divRound(r));return c*(s+f*Math.pow(2,-u))}var h=r.bitLength()-l.bitLength()+53,f=n(l.ushln(h).divRound(r));return h<1023?c*f*Math.pow(2,-h):(f*=Math.pow(2,-1023),c*f*Math.pow(2,1023-h))}},{"./lib/bn-to-num":63,"./lib/ctz":64}],73:[function(t,e,r){"use strict";function n(t,e,r,n,i,a){var o=["function ",t,"(a,l,h,",n.join(","),"){",a?"":"var i=",r?"l-1":"h+1",";while(l<=h){var m=(l+h)>>>1,x=a",i?".get(m)":"[m]"];return a?e.indexOf("c")<0?o.push(";if(x===y){return m}else if(x<=y){"):o.push(";var p=c(x,y);if(p===0){return m}else if(p<=0){"):o.push(";if(",e,"){i=m;"),r?o.push("l=m+1}else{h=m-1}"):o.push("h=m-1}else{l=m+1}"),o.push("}"),a?o.push("return -1};"):o.push("return i};"),o.join("")}function i(t,e,r,i){return new Function([n("A","x"+t+"y",e,["y"],!1,i),n("B","x"+t+"y",e,["y"],!0,i),n("P","c(x,y)"+t+"0",e,["y","c"],!1,i),n("Q","c(x,y)"+t+"0",e,["y","c"],!0,i),"function dispatchBsearch",r,"(a,y,c,l,h){if(a.shape){if(typeof(c)==='function'){return Q(a,(l===undefined)?0:l|0,(h===undefined)?a.shape[0]-1:h|0,y,c)}else{return B(a,(c===undefined)?0:c|0,(l===undefined)?a.shape[0]-1:l|0,y)}}else{if(typeof(c)==='function'){return P(a,(l===undefined)?0:l|0,(h===undefined)?a.length-1:h|0,y,c)}else{return A(a,(c===undefined)?0:c|0,(l===undefined)?a.length-1:l|0,y)}}}return dispatchBsearch",r].join(""))()}e.exports={ge:i(">=",!1,"GE"),gt:i(">",!1,"GT"),lt:i("<",!0,"LT"),le:i("<=",!0,"LE"),eq:i("-",!0,"EQ",!0)}},{}],74:[function(t,e,r){"use strict";"use restrict";function n(t){var e=32;return(t&=-t)&&e--,65535&t&&(e-=16),16711935&t&&(e-=8),252645135&t&&(e-=4),858993459&t&&(e-=2),1431655765&t&&(e-=1),e}r.INT_BITS=32,r.INT_MAX=2147483647,r.INT_MIN=-1<<31,r.sign=function(t){return(t>0)-(t<0)},r.abs=function(t){var e=t>>31;return(t^e)-e},r.min=function(t,e){return e^(t^e)&-(t65535)<<4,e|=r=((t>>>=e)>255)<<3,e|=r=((t>>>=r)>15)<<2,(e|=r=((t>>>=r)>3)<<1)|(t>>>=r)>>1},r.log10=function(t){return t>=1e9?9:t>=1e8?8:t>=1e7?7:t>=1e6?6:t>=1e5?5:t>=1e4?4:t>=1e3?3:t>=100?2:t>=10?1:0},r.popCount=function(t){return 16843009*((t=(858993459&(t-=t>>>1&1431655765))+(t>>>2&858993459))+(t>>>4)&252645135)>>>24},r.countTrailingZeros=n,r.nextPow2=function(t){return t+=0===t,--t,t|=t>>>1,t|=t>>>2,t|=t>>>4,t|=t>>>8,(t|=t>>>16)+1},r.prevPow2=function(t){return t|=t>>>1,t|=t>>>2,t|=t>>>4,t|=t>>>8,(t|=t>>>16)-(t>>>1)},r.parity=function(t){return t^=t>>>16,t^=t>>>8,t^=t>>>4,27030>>>(t&=15)&1};var i=new Array(256);!function(t){for(var e=0;e<256;++e){var r=e,n=e,i=7;for(r>>>=1;r;r>>>=1)n<<=1,n|=1&r,--i;t[e]=n<>>8&255]<<16|i[t>>>16&255]<<8|i[t>>>24&255]},r.interleave2=function(t,e){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t&=65535)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e&=65535)|e<<8))|e<<4))|e<<2))|e<<1))<<1},r.deinterleave2=function(t,e){return(t=65535&((t=16711935&((t=252645135&((t=858993459&((t=t>>>e&1431655765)|t>>>1))|t>>>2))|t>>>4))|t>>>16))<<16>>16},r.interleave3=function(t,e,r){return t=1227133513&((t=3272356035&((t=251719695&((t=4278190335&((t&=1023)|t<<16))|t<<8))|t<<4))|t<<2),(t|=(e=1227133513&((e=3272356035&((e=251719695&((e=4278190335&((e&=1023)|e<<16))|e<<8))|e<<4))|e<<2))<<1)|(r=1227133513&((r=3272356035&((r=251719695&((r=4278190335&((r&=1023)|r<<16))|r<<8))|r<<4))|r<<2))<<2},r.deinterleave3=function(t,e){return(t=1023&((t=4278190335&((t=251719695&((t=3272356035&((t=t>>>e&1227133513)|t>>>2))|t>>>4))|t>>>8))|t>>>16))<<22>>22},r.nextCombination=function(t){var e=t|t-1;return e+1|(~e&-~e)-1>>>n(t)+1}},{}],75:[function(t,e,r){"use strict";var n=t("clamp");e.exports=function(t,e){e||(e={});var r,o,s,l,c,u,f,h,p,d,g,m=null==e.cutoff?.25:e.cutoff,v=null==e.radius?8:e.radius,y=e.channel||0;if(ArrayBuffer.isView(t)||Array.isArray(t)){if(!e.width||!e.height)throw Error("For raw data width and height should be provided by options");r=e.width,o=e.height,l=t,u=e.stride?e.stride:Math.floor(t.length/r/o)}else window.HTMLCanvasElement&&t instanceof window.HTMLCanvasElement?(f=(h=t).getContext("2d"),r=h.width,o=h.height,p=f.getImageData(0,0,r,o),l=p.data,u=4):window.CanvasRenderingContext2D&&t instanceof window.CanvasRenderingContext2D?(h=t.canvas,f=t,r=h.width,o=h.height,p=f.getImageData(0,0,r,o),l=p.data,u=4):window.ImageData&&t instanceof window.ImageData&&(p=t,r=t.width,o=t.height,l=p.data,u=4);if(s=Math.max(r,o),window.Uint8ClampedArray&&l instanceof window.Uint8ClampedArray||window.Uint8Array&&l instanceof window.Uint8Array)for(c=l,l=Array(r*o),d=0,g=c.length;d=49&&o<=54?o-49+10:o>=17&&o<=22?o-17+10:15&o}return n}function l(t,e,r,n){for(var i=0,a=Math.min(t.length,r),o=e;o=49?s-49+10:s>=17?s-17+10:s}return i}a.isBN=function(t){return t instanceof a||null!==t&&"object"==typeof t&&t.constructor.wordSize===a.wordSize&&Array.isArray(t.words)},a.max=function(t,e){return t.cmp(e)>0?t:e},a.min=function(t,e){return t.cmp(e)<0?t:e},a.prototype._init=function(t,e,r){if("number"==typeof t)return this._initNumber(t,e,r);if("object"==typeof t)return this._initArray(t,e,r);"hex"===e&&(e=16),n(e===(0|e)&&e>=2&&e<=36);var i=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&i++,16===e?this._parseHex(t,i):this._parseBase(t,e,i),"-"===t[0]&&(this.negative=1),this.strip(),"le"===r&&this._initArray(this.toArray(),e,r)},a.prototype._initNumber=function(t,e,r){t<0&&(this.negative=1,t=-t),t<67108864?(this.words=[67108863&t],this.length=1):t<4503599627370496?(this.words=[67108863&t,t/67108864&67108863],this.length=2):(n(t<9007199254740992),this.words=[67108863&t,t/67108864&67108863,1],this.length=3),"le"===r&&this._initArray(this.toArray(),e,r)},a.prototype._initArray=function(t,e,r){if(n("number"==typeof t.length),t.length<=0)return this.words=[0],this.length=1,this;this.length=Math.ceil(t.length/3),this.words=new Array(this.length);for(var i=0;i=0;i-=3)o=t[i]|t[i-1]<<8|t[i-2]<<16,this.words[a]|=o<>>26-s&67108863,(s+=24)>=26&&(s-=26,a++);else if("le"===r)for(i=0,a=0;i>>26-s&67108863,(s+=24)>=26&&(s-=26,a++);return this.strip()},a.prototype._parseHex=function(t,e){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var r=0;r=e;r-=6)i=s(t,r,r+6),this.words[n]|=i<>>26-a&4194303,(a+=24)>=26&&(a-=26,n++);r+6!==e&&(i=s(t,e,r+6),this.words[n]|=i<>>26-a&4194303),this.strip()},a.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var n=0,i=1;i<=67108863;i*=e)n++;n--,i=i/e|0;for(var a=t.length-r,o=a%n,s=Math.min(a,a-o)+r,c=0,u=r;u1&&0===this.words[this.length-1];)this.length--;return this._normSign()},a.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},a.prototype.inspect=function(){return(this.red?""};var c=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],u=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],f=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function h(t,e,r){r.negative=e.negative^t.negative;var n=t.length+e.length|0;r.length=n,n=n-1|0;var i=0|t.words[0],a=0|e.words[0],o=i*a,s=67108863&o,l=o/67108864|0;r.words[0]=s;for(var c=1;c>>26,f=67108863&l,h=Math.min(c,e.length-1),p=Math.max(0,c-t.length+1);p<=h;p++){var d=c-p|0;u+=(o=(i=0|t.words[d])*(a=0|e.words[p])+f)/67108864|0,f=67108863&o}r.words[c]=0|f,l=0|u}return 0!==l?r.words[c]=0|l:r.length--,r.strip()}a.prototype.toString=function(t,e){var r;if(e=0|e||1,16===(t=t||10)||"hex"===t){r="";for(var i=0,a=0,o=0;o>>24-i&16777215)||o!==this.length-1?c[6-l.length]+l+r:l+r,(i+=2)>=26&&(i-=26,o--)}for(0!==a&&(r=a.toString(16)+r);r.length%e!=0;)r="0"+r;return 0!==this.negative&&(r="-"+r),r}if(t===(0|t)&&t>=2&&t<=36){var h=u[t],p=f[t];r="";var d=this.clone();for(d.negative=0;!d.isZero();){var g=d.modn(p).toString(t);r=(d=d.idivn(p)).isZero()?g+r:c[h-g.length]+g+r}for(this.isZero()&&(r="0"+r);r.length%e!=0;)r="0"+r;return 0!==this.negative&&(r="-"+r),r}n(!1,"Base should be between 2 and 36")},a.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&n(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},a.prototype.toJSON=function(){return this.toString(16)},a.prototype.toBuffer=function(t,e){return n(void 0!==o),this.toArrayLike(o,t,e)},a.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)},a.prototype.toArrayLike=function(t,e,r){var i=this.byteLength(),a=r||Math.max(1,i);n(i<=a,"byte array longer than desired length"),n(a>0,"Requested array length <= 0"),this.strip();var o,s,l="le"===e,c=new t(a),u=this.clone();if(l){for(s=0;!u.isZero();s++)o=u.andln(255),u.iushrn(8),c[s]=o;for(;s=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},a.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},a.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},a.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},a.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},a.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},a.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},a.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var n=0;nt.length?this.clone().ixor(t):t.clone().ixor(this)},a.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},a.prototype.inotn=function(t){n("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),r=t%26;this._expand(e),r>0&&e--;for(var i=0;i0&&(this.words[i]=~this.words[i]&67108863>>26-r),this.strip()},a.prototype.notn=function(t){return this.clone().inotn(t)},a.prototype.setn=function(t,e){n("number"==typeof t&&t>=0);var r=t/26|0,i=t%26;return this._expand(r+1),this.words[r]=e?this.words[r]|1<t.length?(r=this,n=t):(r=t,n=this);for(var i=0,a=0;a>>26;for(;0!==i&&a>>26;if(this.length=r.length,0!==i)this.words[this.length]=i,this.length++;else if(r!==this)for(;at.length?this.clone().iadd(t):t.clone().iadd(this)},a.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,n,i=this.cmp(t);if(0===i)return this.negative=0,this.length=1,this.words[0]=0,this;i>0?(r=this,n=t):(r=t,n=this);for(var a=0,o=0;o>26,this.words[o]=67108863&e;for(;0!==a&&o>26,this.words[o]=67108863&e;if(0===a&&o>>13,p=0|o[1],d=8191&p,g=p>>>13,m=0|o[2],v=8191&m,y=m>>>13,x=0|o[3],b=8191&x,_=x>>>13,w=0|o[4],k=8191&w,M=w>>>13,A=0|o[5],T=8191&A,S=A>>>13,C=0|o[6],E=8191&C,L=C>>>13,z=0|o[7],P=8191&z,D=z>>>13,O=0|o[8],I=8191&O,R=O>>>13,B=0|o[9],F=8191&B,N=B>>>13,j=0|s[0],V=8191&j,U=j>>>13,q=0|s[1],H=8191&q,G=q>>>13,W=0|s[2],Y=8191&W,X=W>>>13,Z=0|s[3],J=8191&Z,K=Z>>>13,Q=0|s[4],$=8191&Q,tt=Q>>>13,et=0|s[5],rt=8191&et,nt=et>>>13,it=0|s[6],at=8191&it,ot=it>>>13,st=0|s[7],lt=8191&st,ct=st>>>13,ut=0|s[8],ft=8191&ut,ht=ut>>>13,pt=0|s[9],dt=8191&pt,gt=pt>>>13;r.negative=t.negative^e.negative,r.length=19;var mt=(c+(n=Math.imul(f,V))|0)+((8191&(i=(i=Math.imul(f,U))+Math.imul(h,V)|0))<<13)|0;c=((a=Math.imul(h,U))+(i>>>13)|0)+(mt>>>26)|0,mt&=67108863,n=Math.imul(d,V),i=(i=Math.imul(d,U))+Math.imul(g,V)|0,a=Math.imul(g,U);var vt=(c+(n=n+Math.imul(f,H)|0)|0)+((8191&(i=(i=i+Math.imul(f,G)|0)+Math.imul(h,H)|0))<<13)|0;c=((a=a+Math.imul(h,G)|0)+(i>>>13)|0)+(vt>>>26)|0,vt&=67108863,n=Math.imul(v,V),i=(i=Math.imul(v,U))+Math.imul(y,V)|0,a=Math.imul(y,U),n=n+Math.imul(d,H)|0,i=(i=i+Math.imul(d,G)|0)+Math.imul(g,H)|0,a=a+Math.imul(g,G)|0;var yt=(c+(n=n+Math.imul(f,Y)|0)|0)+((8191&(i=(i=i+Math.imul(f,X)|0)+Math.imul(h,Y)|0))<<13)|0;c=((a=a+Math.imul(h,X)|0)+(i>>>13)|0)+(yt>>>26)|0,yt&=67108863,n=Math.imul(b,V),i=(i=Math.imul(b,U))+Math.imul(_,V)|0,a=Math.imul(_,U),n=n+Math.imul(v,H)|0,i=(i=i+Math.imul(v,G)|0)+Math.imul(y,H)|0,a=a+Math.imul(y,G)|0,n=n+Math.imul(d,Y)|0,i=(i=i+Math.imul(d,X)|0)+Math.imul(g,Y)|0,a=a+Math.imul(g,X)|0;var xt=(c+(n=n+Math.imul(f,J)|0)|0)+((8191&(i=(i=i+Math.imul(f,K)|0)+Math.imul(h,J)|0))<<13)|0;c=((a=a+Math.imul(h,K)|0)+(i>>>13)|0)+(xt>>>26)|0,xt&=67108863,n=Math.imul(k,V),i=(i=Math.imul(k,U))+Math.imul(M,V)|0,a=Math.imul(M,U),n=n+Math.imul(b,H)|0,i=(i=i+Math.imul(b,G)|0)+Math.imul(_,H)|0,a=a+Math.imul(_,G)|0,n=n+Math.imul(v,Y)|0,i=(i=i+Math.imul(v,X)|0)+Math.imul(y,Y)|0,a=a+Math.imul(y,X)|0,n=n+Math.imul(d,J)|0,i=(i=i+Math.imul(d,K)|0)+Math.imul(g,J)|0,a=a+Math.imul(g,K)|0;var bt=(c+(n=n+Math.imul(f,$)|0)|0)+((8191&(i=(i=i+Math.imul(f,tt)|0)+Math.imul(h,$)|0))<<13)|0;c=((a=a+Math.imul(h,tt)|0)+(i>>>13)|0)+(bt>>>26)|0,bt&=67108863,n=Math.imul(T,V),i=(i=Math.imul(T,U))+Math.imul(S,V)|0,a=Math.imul(S,U),n=n+Math.imul(k,H)|0,i=(i=i+Math.imul(k,G)|0)+Math.imul(M,H)|0,a=a+Math.imul(M,G)|0,n=n+Math.imul(b,Y)|0,i=(i=i+Math.imul(b,X)|0)+Math.imul(_,Y)|0,a=a+Math.imul(_,X)|0,n=n+Math.imul(v,J)|0,i=(i=i+Math.imul(v,K)|0)+Math.imul(y,J)|0,a=a+Math.imul(y,K)|0,n=n+Math.imul(d,$)|0,i=(i=i+Math.imul(d,tt)|0)+Math.imul(g,$)|0,a=a+Math.imul(g,tt)|0;var _t=(c+(n=n+Math.imul(f,rt)|0)|0)+((8191&(i=(i=i+Math.imul(f,nt)|0)+Math.imul(h,rt)|0))<<13)|0;c=((a=a+Math.imul(h,nt)|0)+(i>>>13)|0)+(_t>>>26)|0,_t&=67108863,n=Math.imul(E,V),i=(i=Math.imul(E,U))+Math.imul(L,V)|0,a=Math.imul(L,U),n=n+Math.imul(T,H)|0,i=(i=i+Math.imul(T,G)|0)+Math.imul(S,H)|0,a=a+Math.imul(S,G)|0,n=n+Math.imul(k,Y)|0,i=(i=i+Math.imul(k,X)|0)+Math.imul(M,Y)|0,a=a+Math.imul(M,X)|0,n=n+Math.imul(b,J)|0,i=(i=i+Math.imul(b,K)|0)+Math.imul(_,J)|0,a=a+Math.imul(_,K)|0,n=n+Math.imul(v,$)|0,i=(i=i+Math.imul(v,tt)|0)+Math.imul(y,$)|0,a=a+Math.imul(y,tt)|0,n=n+Math.imul(d,rt)|0,i=(i=i+Math.imul(d,nt)|0)+Math.imul(g,rt)|0,a=a+Math.imul(g,nt)|0;var wt=(c+(n=n+Math.imul(f,at)|0)|0)+((8191&(i=(i=i+Math.imul(f,ot)|0)+Math.imul(h,at)|0))<<13)|0;c=((a=a+Math.imul(h,ot)|0)+(i>>>13)|0)+(wt>>>26)|0,wt&=67108863,n=Math.imul(P,V),i=(i=Math.imul(P,U))+Math.imul(D,V)|0,a=Math.imul(D,U),n=n+Math.imul(E,H)|0,i=(i=i+Math.imul(E,G)|0)+Math.imul(L,H)|0,a=a+Math.imul(L,G)|0,n=n+Math.imul(T,Y)|0,i=(i=i+Math.imul(T,X)|0)+Math.imul(S,Y)|0,a=a+Math.imul(S,X)|0,n=n+Math.imul(k,J)|0,i=(i=i+Math.imul(k,K)|0)+Math.imul(M,J)|0,a=a+Math.imul(M,K)|0,n=n+Math.imul(b,$)|0,i=(i=i+Math.imul(b,tt)|0)+Math.imul(_,$)|0,a=a+Math.imul(_,tt)|0,n=n+Math.imul(v,rt)|0,i=(i=i+Math.imul(v,nt)|0)+Math.imul(y,rt)|0,a=a+Math.imul(y,nt)|0,n=n+Math.imul(d,at)|0,i=(i=i+Math.imul(d,ot)|0)+Math.imul(g,at)|0,a=a+Math.imul(g,ot)|0;var kt=(c+(n=n+Math.imul(f,lt)|0)|0)+((8191&(i=(i=i+Math.imul(f,ct)|0)+Math.imul(h,lt)|0))<<13)|0;c=((a=a+Math.imul(h,ct)|0)+(i>>>13)|0)+(kt>>>26)|0,kt&=67108863,n=Math.imul(I,V),i=(i=Math.imul(I,U))+Math.imul(R,V)|0,a=Math.imul(R,U),n=n+Math.imul(P,H)|0,i=(i=i+Math.imul(P,G)|0)+Math.imul(D,H)|0,a=a+Math.imul(D,G)|0,n=n+Math.imul(E,Y)|0,i=(i=i+Math.imul(E,X)|0)+Math.imul(L,Y)|0,a=a+Math.imul(L,X)|0,n=n+Math.imul(T,J)|0,i=(i=i+Math.imul(T,K)|0)+Math.imul(S,J)|0,a=a+Math.imul(S,K)|0,n=n+Math.imul(k,$)|0,i=(i=i+Math.imul(k,tt)|0)+Math.imul(M,$)|0,a=a+Math.imul(M,tt)|0,n=n+Math.imul(b,rt)|0,i=(i=i+Math.imul(b,nt)|0)+Math.imul(_,rt)|0,a=a+Math.imul(_,nt)|0,n=n+Math.imul(v,at)|0,i=(i=i+Math.imul(v,ot)|0)+Math.imul(y,at)|0,a=a+Math.imul(y,ot)|0,n=n+Math.imul(d,lt)|0,i=(i=i+Math.imul(d,ct)|0)+Math.imul(g,lt)|0,a=a+Math.imul(g,ct)|0;var Mt=(c+(n=n+Math.imul(f,ft)|0)|0)+((8191&(i=(i=i+Math.imul(f,ht)|0)+Math.imul(h,ft)|0))<<13)|0;c=((a=a+Math.imul(h,ht)|0)+(i>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,n=Math.imul(F,V),i=(i=Math.imul(F,U))+Math.imul(N,V)|0,a=Math.imul(N,U),n=n+Math.imul(I,H)|0,i=(i=i+Math.imul(I,G)|0)+Math.imul(R,H)|0,a=a+Math.imul(R,G)|0,n=n+Math.imul(P,Y)|0,i=(i=i+Math.imul(P,X)|0)+Math.imul(D,Y)|0,a=a+Math.imul(D,X)|0,n=n+Math.imul(E,J)|0,i=(i=i+Math.imul(E,K)|0)+Math.imul(L,J)|0,a=a+Math.imul(L,K)|0,n=n+Math.imul(T,$)|0,i=(i=i+Math.imul(T,tt)|0)+Math.imul(S,$)|0,a=a+Math.imul(S,tt)|0,n=n+Math.imul(k,rt)|0,i=(i=i+Math.imul(k,nt)|0)+Math.imul(M,rt)|0,a=a+Math.imul(M,nt)|0,n=n+Math.imul(b,at)|0,i=(i=i+Math.imul(b,ot)|0)+Math.imul(_,at)|0,a=a+Math.imul(_,ot)|0,n=n+Math.imul(v,lt)|0,i=(i=i+Math.imul(v,ct)|0)+Math.imul(y,lt)|0,a=a+Math.imul(y,ct)|0,n=n+Math.imul(d,ft)|0,i=(i=i+Math.imul(d,ht)|0)+Math.imul(g,ft)|0,a=a+Math.imul(g,ht)|0;var At=(c+(n=n+Math.imul(f,dt)|0)|0)+((8191&(i=(i=i+Math.imul(f,gt)|0)+Math.imul(h,dt)|0))<<13)|0;c=((a=a+Math.imul(h,gt)|0)+(i>>>13)|0)+(At>>>26)|0,At&=67108863,n=Math.imul(F,H),i=(i=Math.imul(F,G))+Math.imul(N,H)|0,a=Math.imul(N,G),n=n+Math.imul(I,Y)|0,i=(i=i+Math.imul(I,X)|0)+Math.imul(R,Y)|0,a=a+Math.imul(R,X)|0,n=n+Math.imul(P,J)|0,i=(i=i+Math.imul(P,K)|0)+Math.imul(D,J)|0,a=a+Math.imul(D,K)|0,n=n+Math.imul(E,$)|0,i=(i=i+Math.imul(E,tt)|0)+Math.imul(L,$)|0,a=a+Math.imul(L,tt)|0,n=n+Math.imul(T,rt)|0,i=(i=i+Math.imul(T,nt)|0)+Math.imul(S,rt)|0,a=a+Math.imul(S,nt)|0,n=n+Math.imul(k,at)|0,i=(i=i+Math.imul(k,ot)|0)+Math.imul(M,at)|0,a=a+Math.imul(M,ot)|0,n=n+Math.imul(b,lt)|0,i=(i=i+Math.imul(b,ct)|0)+Math.imul(_,lt)|0,a=a+Math.imul(_,ct)|0,n=n+Math.imul(v,ft)|0,i=(i=i+Math.imul(v,ht)|0)+Math.imul(y,ft)|0,a=a+Math.imul(y,ht)|0;var Tt=(c+(n=n+Math.imul(d,dt)|0)|0)+((8191&(i=(i=i+Math.imul(d,gt)|0)+Math.imul(g,dt)|0))<<13)|0;c=((a=a+Math.imul(g,gt)|0)+(i>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,n=Math.imul(F,Y),i=(i=Math.imul(F,X))+Math.imul(N,Y)|0,a=Math.imul(N,X),n=n+Math.imul(I,J)|0,i=(i=i+Math.imul(I,K)|0)+Math.imul(R,J)|0,a=a+Math.imul(R,K)|0,n=n+Math.imul(P,$)|0,i=(i=i+Math.imul(P,tt)|0)+Math.imul(D,$)|0,a=a+Math.imul(D,tt)|0,n=n+Math.imul(E,rt)|0,i=(i=i+Math.imul(E,nt)|0)+Math.imul(L,rt)|0,a=a+Math.imul(L,nt)|0,n=n+Math.imul(T,at)|0,i=(i=i+Math.imul(T,ot)|0)+Math.imul(S,at)|0,a=a+Math.imul(S,ot)|0,n=n+Math.imul(k,lt)|0,i=(i=i+Math.imul(k,ct)|0)+Math.imul(M,lt)|0,a=a+Math.imul(M,ct)|0,n=n+Math.imul(b,ft)|0,i=(i=i+Math.imul(b,ht)|0)+Math.imul(_,ft)|0,a=a+Math.imul(_,ht)|0;var St=(c+(n=n+Math.imul(v,dt)|0)|0)+((8191&(i=(i=i+Math.imul(v,gt)|0)+Math.imul(y,dt)|0))<<13)|0;c=((a=a+Math.imul(y,gt)|0)+(i>>>13)|0)+(St>>>26)|0,St&=67108863,n=Math.imul(F,J),i=(i=Math.imul(F,K))+Math.imul(N,J)|0,a=Math.imul(N,K),n=n+Math.imul(I,$)|0,i=(i=i+Math.imul(I,tt)|0)+Math.imul(R,$)|0,a=a+Math.imul(R,tt)|0,n=n+Math.imul(P,rt)|0,i=(i=i+Math.imul(P,nt)|0)+Math.imul(D,rt)|0,a=a+Math.imul(D,nt)|0,n=n+Math.imul(E,at)|0,i=(i=i+Math.imul(E,ot)|0)+Math.imul(L,at)|0,a=a+Math.imul(L,ot)|0,n=n+Math.imul(T,lt)|0,i=(i=i+Math.imul(T,ct)|0)+Math.imul(S,lt)|0,a=a+Math.imul(S,ct)|0,n=n+Math.imul(k,ft)|0,i=(i=i+Math.imul(k,ht)|0)+Math.imul(M,ft)|0,a=a+Math.imul(M,ht)|0;var Ct=(c+(n=n+Math.imul(b,dt)|0)|0)+((8191&(i=(i=i+Math.imul(b,gt)|0)+Math.imul(_,dt)|0))<<13)|0;c=((a=a+Math.imul(_,gt)|0)+(i>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,n=Math.imul(F,$),i=(i=Math.imul(F,tt))+Math.imul(N,$)|0,a=Math.imul(N,tt),n=n+Math.imul(I,rt)|0,i=(i=i+Math.imul(I,nt)|0)+Math.imul(R,rt)|0,a=a+Math.imul(R,nt)|0,n=n+Math.imul(P,at)|0,i=(i=i+Math.imul(P,ot)|0)+Math.imul(D,at)|0,a=a+Math.imul(D,ot)|0,n=n+Math.imul(E,lt)|0,i=(i=i+Math.imul(E,ct)|0)+Math.imul(L,lt)|0,a=a+Math.imul(L,ct)|0,n=n+Math.imul(T,ft)|0,i=(i=i+Math.imul(T,ht)|0)+Math.imul(S,ft)|0,a=a+Math.imul(S,ht)|0;var Et=(c+(n=n+Math.imul(k,dt)|0)|0)+((8191&(i=(i=i+Math.imul(k,gt)|0)+Math.imul(M,dt)|0))<<13)|0;c=((a=a+Math.imul(M,gt)|0)+(i>>>13)|0)+(Et>>>26)|0,Et&=67108863,n=Math.imul(F,rt),i=(i=Math.imul(F,nt))+Math.imul(N,rt)|0,a=Math.imul(N,nt),n=n+Math.imul(I,at)|0,i=(i=i+Math.imul(I,ot)|0)+Math.imul(R,at)|0,a=a+Math.imul(R,ot)|0,n=n+Math.imul(P,lt)|0,i=(i=i+Math.imul(P,ct)|0)+Math.imul(D,lt)|0,a=a+Math.imul(D,ct)|0,n=n+Math.imul(E,ft)|0,i=(i=i+Math.imul(E,ht)|0)+Math.imul(L,ft)|0,a=a+Math.imul(L,ht)|0;var Lt=(c+(n=n+Math.imul(T,dt)|0)|0)+((8191&(i=(i=i+Math.imul(T,gt)|0)+Math.imul(S,dt)|0))<<13)|0;c=((a=a+Math.imul(S,gt)|0)+(i>>>13)|0)+(Lt>>>26)|0,Lt&=67108863,n=Math.imul(F,at),i=(i=Math.imul(F,ot))+Math.imul(N,at)|0,a=Math.imul(N,ot),n=n+Math.imul(I,lt)|0,i=(i=i+Math.imul(I,ct)|0)+Math.imul(R,lt)|0,a=a+Math.imul(R,ct)|0,n=n+Math.imul(P,ft)|0,i=(i=i+Math.imul(P,ht)|0)+Math.imul(D,ft)|0,a=a+Math.imul(D,ht)|0;var zt=(c+(n=n+Math.imul(E,dt)|0)|0)+((8191&(i=(i=i+Math.imul(E,gt)|0)+Math.imul(L,dt)|0))<<13)|0;c=((a=a+Math.imul(L,gt)|0)+(i>>>13)|0)+(zt>>>26)|0,zt&=67108863,n=Math.imul(F,lt),i=(i=Math.imul(F,ct))+Math.imul(N,lt)|0,a=Math.imul(N,ct),n=n+Math.imul(I,ft)|0,i=(i=i+Math.imul(I,ht)|0)+Math.imul(R,ft)|0,a=a+Math.imul(R,ht)|0;var Pt=(c+(n=n+Math.imul(P,dt)|0)|0)+((8191&(i=(i=i+Math.imul(P,gt)|0)+Math.imul(D,dt)|0))<<13)|0;c=((a=a+Math.imul(D,gt)|0)+(i>>>13)|0)+(Pt>>>26)|0,Pt&=67108863,n=Math.imul(F,ft),i=(i=Math.imul(F,ht))+Math.imul(N,ft)|0,a=Math.imul(N,ht);var Dt=(c+(n=n+Math.imul(I,dt)|0)|0)+((8191&(i=(i=i+Math.imul(I,gt)|0)+Math.imul(R,dt)|0))<<13)|0;c=((a=a+Math.imul(R,gt)|0)+(i>>>13)|0)+(Dt>>>26)|0,Dt&=67108863;var Ot=(c+(n=Math.imul(F,dt))|0)+((8191&(i=(i=Math.imul(F,gt))+Math.imul(N,dt)|0))<<13)|0;return c=((a=Math.imul(N,gt))+(i>>>13)|0)+(Ot>>>26)|0,Ot&=67108863,l[0]=mt,l[1]=vt,l[2]=yt,l[3]=xt,l[4]=bt,l[5]=_t,l[6]=wt,l[7]=kt,l[8]=Mt,l[9]=At,l[10]=Tt,l[11]=St,l[12]=Ct,l[13]=Et,l[14]=Lt,l[15]=zt,l[16]=Pt,l[17]=Dt,l[18]=Ot,0!==c&&(l[19]=c,r.length++),r};function d(t,e,r){return(new g).mulp(t,e,r)}function g(t,e){this.x=t,this.y=e}Math.imul||(p=h),a.prototype.mulTo=function(t,e){var r=this.length+t.length;return 10===this.length&&10===t.length?p(this,t,e):r<63?h(this,t,e):r<1024?function(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var n=0,i=0,a=0;a>>26)|0)>>>26,o&=67108863}r.words[a]=s,n=o,o=i}return 0!==n?r.words[a]=n:r.length--,r.strip()}(this,t,e):d(this,t,e)},g.prototype.makeRBT=function(t){for(var e=new Array(t),r=a.prototype._countBits(t)-1,n=0;n>=1;return n},g.prototype.permute=function(t,e,r,n,i,a){for(var o=0;o>>=1)i++;return 1<>>=13,r[2*o+1]=8191&a,a>>>=13;for(o=2*e;o>=26,e+=i/67108864|0,e+=a>>>26,this.words[r]=67108863&a}return 0!==e&&(this.words[r]=e,this.length++),this},a.prototype.muln=function(t){return this.clone().imuln(t)},a.prototype.sqr=function(){return this.mul(this)},a.prototype.isqr=function(){return this.imul(this.clone())},a.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>i}return e}(t);if(0===e.length)return new a(1);for(var r=this,n=0;n=0);var e,r=t%26,i=(t-r)/26,a=67108863>>>26-r<<26-r;if(0!==r){var o=0;for(e=0;e>>26-r}o&&(this.words[e]=o,this.length++)}if(0!==i){for(e=this.length-1;e>=0;e--)this.words[e+i]=this.words[e];for(e=0;e=0),i=e?(e-e%26)/26:0;var a=t%26,o=Math.min((t-a)/26,this.length),s=67108863^67108863>>>a<o)for(this.length-=o,c=0;c=0&&(0!==u||c>=i);c--){var f=0|this.words[c];this.words[c]=u<<26-a|f>>>a,u=f&s}return l&&0!==u&&(l.words[l.length++]=u),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},a.prototype.ishrn=function(t,e,r){return n(0===this.negative),this.iushrn(t,e,r)},a.prototype.shln=function(t){return this.clone().ishln(t)},a.prototype.ushln=function(t){return this.clone().iushln(t)},a.prototype.shrn=function(t){return this.clone().ishrn(t)},a.prototype.ushrn=function(t){return this.clone().iushrn(t)},a.prototype.testn=function(t){n("number"==typeof t&&t>=0);var e=t%26,r=(t-e)/26,i=1<=0);var e=t%26,r=(t-e)/26;if(n(0===this.negative,"imaskn works only with positive numbers"),this.length<=r)return this;if(0!==e&&r++,this.length=Math.min(r,this.length),0!==e){var i=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},a.prototype.isubn=function(t){if(n("number"==typeof t),n(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(l/67108864|0),this.words[i+r]=67108863&a}for(;i>26,this.words[i+r]=67108863&a;if(0===s)return this.strip();for(n(-1===s),s=0,i=0;i>26,this.words[i]=67108863&a;return this.negative=1,this.strip()},a.prototype._wordDiv=function(t,e){var r=(this.length,t.length),n=this.clone(),i=t,o=0|i.words[i.length-1];0!==(r=26-this._countBits(o))&&(i=i.ushln(r),n.iushln(r),o=0|i.words[i.length-1]);var s,l=n.length-i.length;if("mod"!==e){(s=new a(null)).length=l+1,s.words=new Array(s.length);for(var c=0;c=0;f--){var h=67108864*(0|n.words[i.length+f])+(0|n.words[i.length+f-1]);for(h=Math.min(h/o|0,67108863),n._ishlnsubmul(i,h,f);0!==n.negative;)h--,n.negative=0,n._ishlnsubmul(i,1,f),n.isZero()||(n.negative^=1);s&&(s.words[f]=h)}return s&&s.strip(),n.strip(),"div"!==e&&0!==r&&n.iushrn(r),{div:s||null,mod:n}},a.prototype.divmod=function(t,e,r){return n(!t.isZero()),this.isZero()?{div:new a(0),mod:new a(0)}:0!==this.negative&&0===t.negative?(s=this.neg().divmod(t,e),"mod"!==e&&(i=s.div.neg()),"div"!==e&&(o=s.mod.neg(),r&&0!==o.negative&&o.iadd(t)),{div:i,mod:o}):0===this.negative&&0!==t.negative?(s=this.divmod(t.neg(),e),"mod"!==e&&(i=s.div.neg()),{div:i,mod:s.mod}):0!=(this.negative&t.negative)?(s=this.neg().divmod(t.neg(),e),"div"!==e&&(o=s.mod.neg(),r&&0!==o.negative&&o.isub(t)),{div:s.div,mod:o}):t.length>this.length||this.cmp(t)<0?{div:new a(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new a(this.modn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new a(this.modn(t.words[0]))}:this._wordDiv(t,e);var i,o,s},a.prototype.div=function(t){return this.divmod(t,"div",!1).div},a.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},a.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},a.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,n=t.ushrn(1),i=t.andln(1),a=r.cmp(n);return a<0||1===i&&0===a?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},a.prototype.modn=function(t){n(t<=67108863);for(var e=(1<<26)%t,r=0,i=this.length-1;i>=0;i--)r=(e*r+(0|this.words[i]))%t;return r},a.prototype.idivn=function(t){n(t<=67108863);for(var e=0,r=this.length-1;r>=0;r--){var i=(0|this.words[r])+67108864*e;this.words[r]=i/t|0,e=i%t}return this.strip()},a.prototype.divn=function(t){return this.clone().idivn(t)},a.prototype.egcd=function(t){n(0===t.negative),n(!t.isZero());var e=this,r=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var i=new a(1),o=new a(0),s=new a(0),l=new a(1),c=0;e.isEven()&&r.isEven();)e.iushrn(1),r.iushrn(1),++c;for(var u=r.clone(),f=e.clone();!e.isZero();){for(var h=0,p=1;0==(e.words[0]&p)&&h<26;++h,p<<=1);if(h>0)for(e.iushrn(h);h-- >0;)(i.isOdd()||o.isOdd())&&(i.iadd(u),o.isub(f)),i.iushrn(1),o.iushrn(1);for(var d=0,g=1;0==(r.words[0]&g)&&d<26;++d,g<<=1);if(d>0)for(r.iushrn(d);d-- >0;)(s.isOdd()||l.isOdd())&&(s.iadd(u),l.isub(f)),s.iushrn(1),l.iushrn(1);e.cmp(r)>=0?(e.isub(r),i.isub(s),o.isub(l)):(r.isub(e),s.isub(i),l.isub(o))}return{a:s,b:l,gcd:r.iushln(c)}},a.prototype._invmp=function(t){n(0===t.negative),n(!t.isZero());var e=this,r=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var i,o=new a(1),s=new a(0),l=r.clone();e.cmpn(1)>0&&r.cmpn(1)>0;){for(var c=0,u=1;0==(e.words[0]&u)&&c<26;++c,u<<=1);if(c>0)for(e.iushrn(c);c-- >0;)o.isOdd()&&o.iadd(l),o.iushrn(1);for(var f=0,h=1;0==(r.words[0]&h)&&f<26;++f,h<<=1);if(f>0)for(r.iushrn(f);f-- >0;)s.isOdd()&&s.iadd(l),s.iushrn(1);e.cmp(r)>=0?(e.isub(r),o.isub(s)):(r.isub(e),s.isub(o))}return(i=0===e.cmpn(1)?o:s).cmpn(0)<0&&i.iadd(t),i},a.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var n=0;e.isEven()&&r.isEven();n++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var i=e.cmp(r);if(i<0){var a=e;e=r,r=a}else if(0===i||0===r.cmpn(1))break;e.isub(r)}return r.iushln(n)},a.prototype.invm=function(t){return this.egcd(t).a.umod(t)},a.prototype.isEven=function(){return 0==(1&this.words[0])},a.prototype.isOdd=function(){return 1==(1&this.words[0])},a.prototype.andln=function(t){return this.words[0]&t},a.prototype.bincn=function(t){n("number"==typeof t);var e=t%26,r=(t-e)/26,i=1<>>26,s&=67108863,this.words[o]=s}return 0!==a&&(this.words[o]=a,this.length++),this},a.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},a.prototype.cmpn=function(t){var e,r=t<0;if(0!==this.negative&&!r)return-1;if(0===this.negative&&r)return 1;if(this.strip(),this.length>1)e=1;else{r&&(t=-t),n(t<=67108863,"Number is too big");var i=0|this.words[0];e=i===t?0:it.length)return 1;if(this.length=0;r--){var n=0|this.words[r],i=0|t.words[r];if(n!==i){ni&&(e=1);break}}return e},a.prototype.gtn=function(t){return 1===this.cmpn(t)},a.prototype.gt=function(t){return 1===this.cmp(t)},a.prototype.gten=function(t){return this.cmpn(t)>=0},a.prototype.gte=function(t){return this.cmp(t)>=0},a.prototype.ltn=function(t){return-1===this.cmpn(t)},a.prototype.lt=function(t){return-1===this.cmp(t)},a.prototype.lten=function(t){return this.cmpn(t)<=0},a.prototype.lte=function(t){return this.cmp(t)<=0},a.prototype.eqn=function(t){return 0===this.cmpn(t)},a.prototype.eq=function(t){return 0===this.cmp(t)},a.red=function(t){return new w(t)},a.prototype.toRed=function(t){return n(!this.red,"Already a number in reduction context"),n(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},a.prototype.fromRed=function(){return n(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},a.prototype._forceRed=function(t){return this.red=t,this},a.prototype.forceRed=function(t){return n(!this.red,"Already a number in reduction context"),this._forceRed(t)},a.prototype.redAdd=function(t){return n(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},a.prototype.redIAdd=function(t){return n(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},a.prototype.redSub=function(t){return n(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},a.prototype.redISub=function(t){return n(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},a.prototype.redShl=function(t){return n(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},a.prototype.redMul=function(t){return n(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},a.prototype.redIMul=function(t){return n(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},a.prototype.redSqr=function(){return n(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},a.prototype.redISqr=function(){return n(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},a.prototype.redSqrt=function(){return n(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},a.prototype.redInvm=function(){return n(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},a.prototype.redNeg=function(){return n(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},a.prototype.redPow=function(t){return n(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var m={k256:null,p224:null,p192:null,p25519:null};function v(t,e){this.name=t,this.p=new a(e,16),this.n=this.p.bitLength(),this.k=new a(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function y(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function x(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function b(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function _(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function w(t){if("string"==typeof t){var e=a._prime(t);this.m=e.p,this.prime=e}else n(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function k(t){w.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new a(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var t=new a(null);return t.words=new Array(Math.ceil(this.n/13)),t},v.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var n=e0?r.isub(this.p):r.strip(),r},v.prototype.split=function(t,e){t.iushrn(this.n,0,e)},v.prototype.imulK=function(t){return t.imul(this.k)},i(y,v),y.prototype.split=function(t,e){for(var r=Math.min(t.length,9),n=0;n>>22,i=a}i>>>=22,t.words[n-10]=i,0===i&&t.length>10?t.length-=10:t.length-=9},y.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=i,e=n}return 0!==e&&(t.words[t.length++]=e),t},a._prime=function(t){if(m[t])return m[t];var e;if("k256"===t)e=new y;else if("p224"===t)e=new x;else if("p192"===t)e=new b;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new _}return m[t]=e,e},w.prototype._verify1=function(t){n(0===t.negative,"red works only with positives"),n(t.red,"red works only with red numbers")},w.prototype._verify2=function(t,e){n(0==(t.negative|e.negative),"red works only with positives"),n(t.red&&t.red===e.red,"red works only with red numbers")},w.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):t.umod(this.m)._forceRed(this)},w.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},w.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},w.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},w.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},w.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},w.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},w.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},w.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},w.prototype.isqr=function(t){return this.imul(t,t.clone())},w.prototype.sqr=function(t){return this.mul(t,t)},w.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(n(e%2==1),3===e){var r=this.m.add(new a(1)).iushrn(2);return this.pow(t,r)}for(var i=this.m.subn(1),o=0;!i.isZero()&&0===i.andln(1);)o++,i.iushrn(1);n(!i.isZero());var s=new a(1).toRed(this),l=s.redNeg(),c=this.m.subn(1).iushrn(1),u=this.m.bitLength();for(u=new a(2*u*u).toRed(this);0!==this.pow(u,c).cmp(l);)u.redIAdd(l);for(var f=this.pow(u,i),h=this.pow(t,i.addn(1).iushrn(1)),p=this.pow(t,i),d=o;0!==p.cmp(s);){for(var g=p,m=0;0!==g.cmp(s);m++)g=g.redSqr();n(m=0;n--){for(var c=e.words[n],u=l-1;u>=0;u--){var f=c>>u&1;i!==r[0]&&(i=this.sqr(i)),0!==f||0!==o?(o<<=1,o|=f,(4===++s||0===n&&0===u)&&(i=this.mul(i,r[o]),s=0,o=0)):s=0}l=26}return i},w.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},w.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},a.mont=function(t){return new k(t)},i(k,w),k.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},k.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},k.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),n=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=r.isub(n).iushrn(this.shift),a=i;return i.cmp(this.m)>=0?a=i.isub(this.m):i.cmpn(0)<0&&(a=i.iadd(this.m)),a._forceRed(this)},k.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new a(0)._forceRed(this);var r=t.mul(e),n=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=r.isub(n).iushrn(this.shift),o=i;return i.cmp(this.m)>=0?o=i.isub(this.m):i.cmpn(0)<0&&(o=i.iadd(this.m)),o._forceRed(this)},k.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===e||e,this)},{buffer:85}],77:[function(t,e,r){"use strict";e.exports=function(t){var e,r,n,i=t.length,a=0;for(e=0;e>>1;if(!(u<=0)){var f,h=i.mallocDouble(2*u*s),p=i.mallocInt32(s);if((s=l(t,u,h,p))>0){if(1===u&&n)a.init(s),f=a.sweepComplete(u,r,0,s,h,p,0,s,h,p);else{var d=i.mallocDouble(2*u*c),g=i.mallocInt32(c);(c=l(e,u,d,g))>0&&(a.init(s+c),f=1===u?a.sweepBipartite(u,r,0,s,h,p,0,c,d,g):o(u,r,n,s,h,p,c,d,g),i.free(d),i.free(g))}i.free(h),i.free(p)}return f}}}function u(t,e){n.push([t,e])}},{"./lib/intersect":80,"./lib/sweep":84,"typedarray-pool":481}],79:[function(t,e,r){"use strict";var n="d",i="ax",a="vv",o="fp",s="es",l="rs",c="re",u="rb",f="ri",h="rp",p="bs",d="be",g="bb",m="bi",v="bp",y="rv",x="Q",b=[n,i,a,l,c,u,f,p,d,g,m];function _(t){var e="bruteForce"+(t?"Full":"Partial"),r=[],_=b.slice();t||_.splice(3,0,o);var w=["function "+e+"("+_.join()+"){"];function k(e,o){var _=function(t,e,r){var o="bruteForce"+(t?"Red":"Blue")+(e?"Flip":"")+(r?"Full":""),_=["function ",o,"(",b.join(),"){","var ",s,"=2*",n,";"],w="for(var i="+l+","+h+"="+s+"*"+l+";i<"+c+";++i,"+h+"+="+s+"){var x0="+u+"["+i+"+"+h+"],x1="+u+"["+i+"+"+h+"+"+n+"],xi="+f+"[i];",k="for(var j="+p+","+v+"="+s+"*"+p+";j<"+d+";++j,"+v+"+="+s+"){var y0="+g+"["+i+"+"+v+"],"+(r?"y1="+g+"["+i+"+"+v+"+"+n+"],":"")+"yi="+m+"[j];";return t?_.push(w,x,":",k):_.push(k,x,":",w),r?_.push("if(y1"+d+"-"+p+"){"),t?(k(!0,!1),w.push("}else{"),k(!1,!1)):(w.push("if("+o+"){"),k(!0,!0),w.push("}else{"),k(!0,!1),w.push("}}else{if("+o+"){"),k(!1,!0),w.push("}else{"),k(!1,!1),w.push("}")),w.push("}}return "+e);var M=r.join("")+w.join("");return new Function(M)()}r.partial=_(!1),r.full=_(!0)},{}],80:[function(t,e,r){"use strict";e.exports=function(t,e,r,a,u,S,C,E,L){!function(t,e){var r=8*i.log2(e+1)*(t+1)|0,a=i.nextPow2(b*r);w.length0;){var O=(P-=1)*b,I=w[O],R=w[O+1],B=w[O+2],F=w[O+3],N=w[O+4],j=w[O+5],V=P*_,U=k[V],q=k[V+1],H=1&j,G=!!(16&j),W=u,Y=S,X=E,Z=L;if(H&&(W=E,Y=L,X=u,Z=S),!(2&j&&(B=m(t,I,R,B,W,Y,q),R>=B)||4&j&&(R=v(t,I,R,B,W,Y,U))>=B)){var J=B-R,K=N-F;if(G){if(t*J*(J+K)=p0)&&!(p1>=hi)",["p0","p1"]),g=u("lo===p0",["p0"]),m=u("lo>>1,h=2*t,p=f,d=s[h*f+e];for(;c=x?(p=y,d=x):v>=_?(p=m,d=v):(p=b,d=_):x>=_?(p=y,d=x):_>=v?(p=m,d=v):(p=b,d=_);for(var w=h*(u-1),k=h*p,M=0;Mr&&i[f+e]>c;--u,f-=o){for(var h=f,p=f+o,d=0;d=0&&i.push("lo=e[k+n]");t.indexOf("hi")>=0&&i.push("hi=e[k+o]");return r.push(n.replace("_",i.join()).replace("$",t)),Function.apply(void 0,r)};var n="for(var j=2*a,k=j*c,l=k,m=c,n=b,o=a+b,p=c;d>p;++p,k+=j){var _;if($)if(m===p)m+=1,l+=j;else{for(var s=0;j>s;++s){var t=e[k+s];e[k+s]=e[l],e[l++]=t}var u=f[p];f[p]=f[m],f[m++]=u}}return m"},{}],83:[function(t,e,r){"use strict";e.exports=function(t,e){e<=4*n?i(0,e-1,t):function t(e,r,f){var h=(r-e+1)/6|0,p=e+h,d=r-h,g=e+r>>1,m=g-h,v=g+h,y=p,x=m,b=g,_=v,w=d,k=e+1,M=r-1,A=0;c(y,x,f)&&(A=y,y=x,x=A);c(_,w,f)&&(A=_,_=w,w=A);c(y,b,f)&&(A=y,y=b,b=A);c(x,b,f)&&(A=x,x=b,b=A);c(y,_,f)&&(A=y,y=_,_=A);c(b,_,f)&&(A=b,b=_,_=A);c(x,w,f)&&(A=x,x=w,w=A);c(x,b,f)&&(A=x,x=b,b=A);c(_,w,f)&&(A=_,_=w,w=A);var T=f[2*x];var S=f[2*x+1];var C=f[2*_];var E=f[2*_+1];var L=2*y;var z=2*b;var P=2*w;var D=2*p;var O=2*g;var I=2*d;for(var R=0;R<2;++R){var B=f[L+R],F=f[z+R],N=f[P+R];f[D+R]=B,f[O+R]=F,f[I+R]=N}o(m,e,f);o(v,r,f);for(var j=k;j<=M;++j)if(u(j,T,S,f))j!==k&&a(j,k,f),++k;else if(!u(j,C,E,f))for(;;){if(u(M,C,E,f)){u(M,T,S,f)?(s(j,k,M,f),++k,--M):(a(j,M,f),--M);break}if(--Mt;){var c=r[l-2],u=r[l-1];if(cr[e+1])}function u(t,e,r,n){var i=n[t*=2];return i>>1;a(p,S);for(var C=0,E=0,k=0;k=o)d(c,u,E--,L=L-o|0);else if(L>=0)d(s,l,C--,L);else if(L<=-o){L=-L-o|0;for(var z=0;z>>1;a(p,C);for(var E=0,L=0,z=0,M=0;M>1==p[2*M+3]>>1&&(D=2,M+=1),P<0){for(var O=-(P>>1)-1,I=0;I>1)-1;0===D?d(s,l,E--,O):1===D?d(c,u,L--,O):2===D&&d(f,h,z--,O)}}},scanBipartite:function(t,e,r,n,i,c,u,f,h,m,v,y){var x=0,b=2*t,_=e,w=e+t,k=1,M=1;n?M=o:k=o;for(var A=i;A>>1;a(p,E);for(var L=0,A=0;A=o?(P=!n,T-=o):(P=!!n,T-=1),P)g(s,l,L++,T);else{var D=y[T],O=b*T,I=v[O+e+1],R=v[O+e+1+t];t:for(var B=0;B>>1;a(p,k);for(var M=0,x=0;x=o)s[M++]=b-o;else{var T=d[b-=1],S=m*b,C=h[S+e+1],E=h[S+e+1+t];t:for(var L=0;L=0;--L)if(s[L]===b){for(var O=L+1;Oa)throw new RangeError("Invalid typed array length");var e=new Uint8Array(t);return e.__proto__=s.prototype,e}function s(t,e,r){if("number"==typeof t){if("string"==typeof e)throw new Error("If encoding is specified then the first argument must be a string");return u(t)}return l(t,e,r)}function l(t,e,r){if("number"==typeof t)throw new TypeError('"value" argument must not be a number');return j(t)||t&&j(t.buffer)?function(t,e,r){if(e<0||t.byteLength=a)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a.toString(16)+" bytes");return 0|t}function p(t,e){if(s.isBuffer(t))return t.length;if(ArrayBuffer.isView(t)||j(t))return t.byteLength;"string"!=typeof t&&(t=""+t);var r=t.length;if(0===r)return 0;for(var n=!1;;)switch(e){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return B(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return F(t).length;default:if(n)return B(t).length;e=(""+e).toLowerCase(),n=!0}}function d(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}function g(t,e,r,n,i){if(0===t.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),V(r=+r)&&(r=i?0:t.length-1),r<0&&(r=t.length+r),r>=t.length){if(i)return-1;r=t.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof e&&(e=s.from(e,n)),s.isBuffer(e))return 0===e.length?-1:m(t,e,r,n,i);if("number"==typeof e)return e&=255,"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(t,e,r):Uint8Array.prototype.lastIndexOf.call(t,e,r):m(t,[e],r,n,i);throw new TypeError("val must be string, number or Buffer")}function m(t,e,r,n,i){var a,o=1,s=t.length,l=e.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(t.length<2||e.length<2)return-1;o=2,s/=2,l/=2,r/=2}function c(t,e){return 1===o?t[e]:t.readUInt16BE(e*o)}if(i){var u=-1;for(a=r;as&&(r=s-l),a=r;a>=0;a--){for(var f=!0,h=0;hi&&(n=i):n=i;var a=e.length;n>a/2&&(n=a/2);for(var o=0;o>8,i=r%256,a.push(i),a.push(n);return a}(e,t.length-r),t,r,n)}function k(t,e,r){return 0===e&&r===t.length?n.fromByteArray(t):n.fromByteArray(t.slice(e,r))}function M(t,e,r){r=Math.min(t.length,r);for(var n=[],i=e;i239?4:c>223?3:c>191?2:1;if(i+f<=r)switch(f){case 1:c<128&&(u=c);break;case 2:128==(192&(a=t[i+1]))&&(l=(31&c)<<6|63&a)>127&&(u=l);break;case 3:a=t[i+1],o=t[i+2],128==(192&a)&&128==(192&o)&&(l=(15&c)<<12|(63&a)<<6|63&o)>2047&&(l<55296||l>57343)&&(u=l);break;case 4:a=t[i+1],o=t[i+2],s=t[i+3],128==(192&a)&&128==(192&o)&&128==(192&s)&&(l=(15&c)<<18|(63&a)<<12|(63&o)<<6|63&s)>65535&&l<1114112&&(u=l)}null===u?(u=65533,f=1):u>65535&&(u-=65536,n.push(u>>>10&1023|55296),u=56320|1023&u),n.push(u),i+=f}return function(t){var e=t.length;if(e<=A)return String.fromCharCode.apply(String,t);var r="",n=0;for(;nthis.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return C(this,e,r);case"utf8":case"utf-8":return M(this,e,r);case"ascii":return T(this,e,r);case"latin1":case"binary":return S(this,e,r);case"base64":return k(this,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return E(this,e,r);default:if(n)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),n=!0}}.apply(this,arguments)},s.prototype.toLocaleString=s.prototype.toString,s.prototype.equals=function(t){if(!s.isBuffer(t))throw new TypeError("Argument must be a Buffer");return this===t||0===s.compare(this,t)},s.prototype.inspect=function(){var t="",e=r.INSPECT_MAX_BYTES;return this.length>0&&(t=this.toString("hex",0,e).match(/.{2}/g).join(" "),this.length>e&&(t+=" ... ")),""},s.prototype.compare=function(t,e,r,n,i){if(!s.isBuffer(t))throw new TypeError("Argument must be a Buffer");if(void 0===e&&(e=0),void 0===r&&(r=t?t.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),e<0||r>t.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&e>=r)return 0;if(n>=i)return-1;if(e>=r)return 1;if(this===t)return 0;for(var a=(i>>>=0)-(n>>>=0),o=(r>>>=0)-(e>>>=0),l=Math.min(a,o),c=this.slice(n,i),u=t.slice(e,r),f=0;f>>=0,isFinite(r)?(r>>>=0,void 0===n&&(n="utf8")):(n=r,r=void 0)}var i=this.length-e;if((void 0===r||r>i)&&(r=i),t.length>0&&(r<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var a=!1;;)switch(n){case"hex":return v(this,t,e,r);case"utf8":case"utf-8":return y(this,t,e,r);case"ascii":return x(this,t,e,r);case"latin1":case"binary":return b(this,t,e,r);case"base64":return _(this,t,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return w(this,t,e,r);default:if(a)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),a=!0}},s.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var A=4096;function T(t,e,r){var n="";r=Math.min(t.length,r);for(var i=e;in)&&(r=n);for(var i="",a=e;ar)throw new RangeError("Trying to access beyond buffer length")}function z(t,e,r,n,i,a){if(!s.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>i||et.length)throw new RangeError("Index out of range")}function P(t,e,r,n,i,a){if(r+n>t.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function D(t,e,r,n,a){return e=+e,r>>>=0,a||P(t,0,r,4),i.write(t,e,r,n,23,4),r+4}function O(t,e,r,n,a){return e=+e,r>>>=0,a||P(t,0,r,8),i.write(t,e,r,n,52,8),r+8}s.prototype.slice=function(t,e){var r=this.length;(t=~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),(e=void 0===e?r:~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),e>>=0,e>>>=0,r||L(t,e,this.length);for(var n=this[t],i=1,a=0;++a>>=0,e>>>=0,r||L(t,e,this.length);for(var n=this[t+--e],i=1;e>0&&(i*=256);)n+=this[t+--e]*i;return n},s.prototype.readUInt8=function(t,e){return t>>>=0,e||L(t,1,this.length),this[t]},s.prototype.readUInt16LE=function(t,e){return t>>>=0,e||L(t,2,this.length),this[t]|this[t+1]<<8},s.prototype.readUInt16BE=function(t,e){return t>>>=0,e||L(t,2,this.length),this[t]<<8|this[t+1]},s.prototype.readUInt32LE=function(t,e){return t>>>=0,e||L(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},s.prototype.readUInt32BE=function(t,e){return t>>>=0,e||L(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},s.prototype.readIntLE=function(t,e,r){t>>>=0,e>>>=0,r||L(t,e,this.length);for(var n=this[t],i=1,a=0;++a=(i*=128)&&(n-=Math.pow(2,8*e)),n},s.prototype.readIntBE=function(t,e,r){t>>>=0,e>>>=0,r||L(t,e,this.length);for(var n=e,i=1,a=this[t+--n];n>0&&(i*=256);)a+=this[t+--n]*i;return a>=(i*=128)&&(a-=Math.pow(2,8*e)),a},s.prototype.readInt8=function(t,e){return t>>>=0,e||L(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},s.prototype.readInt16LE=function(t,e){t>>>=0,e||L(t,2,this.length);var r=this[t]|this[t+1]<<8;return 32768&r?4294901760|r:r},s.prototype.readInt16BE=function(t,e){t>>>=0,e||L(t,2,this.length);var r=this[t+1]|this[t]<<8;return 32768&r?4294901760|r:r},s.prototype.readInt32LE=function(t,e){return t>>>=0,e||L(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},s.prototype.readInt32BE=function(t,e){return t>>>=0,e||L(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},s.prototype.readFloatLE=function(t,e){return t>>>=0,e||L(t,4,this.length),i.read(this,t,!0,23,4)},s.prototype.readFloatBE=function(t,e){return t>>>=0,e||L(t,4,this.length),i.read(this,t,!1,23,4)},s.prototype.readDoubleLE=function(t,e){return t>>>=0,e||L(t,8,this.length),i.read(this,t,!0,52,8)},s.prototype.readDoubleBE=function(t,e){return t>>>=0,e||L(t,8,this.length),i.read(this,t,!1,52,8)},s.prototype.writeUIntLE=function(t,e,r,n){(t=+t,e>>>=0,r>>>=0,n)||z(this,t,e,r,Math.pow(2,8*r)-1,0);var i=1,a=0;for(this[e]=255&t;++a>>=0,r>>>=0,n)||z(this,t,e,r,Math.pow(2,8*r)-1,0);var i=r-1,a=1;for(this[e+i]=255&t;--i>=0&&(a*=256);)this[e+i]=t/a&255;return e+r},s.prototype.writeUInt8=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,1,255,0),this[e]=255&t,e+1},s.prototype.writeUInt16LE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,2,65535,0),this[e]=255&t,this[e+1]=t>>>8,e+2},s.prototype.writeUInt16BE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,2,65535,0),this[e]=t>>>8,this[e+1]=255&t,e+2},s.prototype.writeUInt32LE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,4,4294967295,0),this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t,e+4},s.prototype.writeUInt32BE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,4,4294967295,0),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},s.prototype.writeIntLE=function(t,e,r,n){if(t=+t,e>>>=0,!n){var i=Math.pow(2,8*r-1);z(this,t,e,r,i-1,-i)}var a=0,o=1,s=0;for(this[e]=255&t;++a>0)-s&255;return e+r},s.prototype.writeIntBE=function(t,e,r,n){if(t=+t,e>>>=0,!n){var i=Math.pow(2,8*r-1);z(this,t,e,r,i-1,-i)}var a=r-1,o=1,s=0;for(this[e+a]=255&t;--a>=0&&(o*=256);)t<0&&0===s&&0!==this[e+a+1]&&(s=1),this[e+a]=(t/o>>0)-s&255;return e+r},s.prototype.writeInt8=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,1,127,-128),t<0&&(t=255+t+1),this[e]=255&t,e+1},s.prototype.writeInt16LE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,2,32767,-32768),this[e]=255&t,this[e+1]=t>>>8,e+2},s.prototype.writeInt16BE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,2,32767,-32768),this[e]=t>>>8,this[e+1]=255&t,e+2},s.prototype.writeInt32LE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,4,2147483647,-2147483648),this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24,e+4},s.prototype.writeInt32BE=function(t,e,r){return t=+t,e>>>=0,r||z(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},s.prototype.writeFloatLE=function(t,e,r){return D(this,t,e,!0,r)},s.prototype.writeFloatBE=function(t,e,r){return D(this,t,e,!1,r)},s.prototype.writeDoubleLE=function(t,e,r){return O(this,t,e,!0,r)},s.prototype.writeDoubleBE=function(t,e,r){return O(this,t,e,!1,r)},s.prototype.copy=function(t,e,r,n){if(!s.isBuffer(t))throw new TypeError("argument should be a Buffer");if(r||(r=0),n||0===n||(n=this.length),e>=t.length&&(e=t.length),e||(e=0),n>0&&n=this.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),t.length-e=0;--a)t[a+e]=this[a+r];else Uint8Array.prototype.set.call(t,this.subarray(r,n),e);return i},s.prototype.fill=function(t,e,r,n){if("string"==typeof t){if("string"==typeof e?(n=e,e=0,r=this.length):"string"==typeof r&&(n=r,r=this.length),void 0!==n&&"string"!=typeof n)throw new TypeError("encoding must be a string");if("string"==typeof n&&!s.isEncoding(n))throw new TypeError("Unknown encoding: "+n);if(1===t.length){var i=t.charCodeAt(0);("utf8"===n&&i<128||"latin1"===n)&&(t=i)}}else"number"==typeof t&&(t&=255);if(e<0||this.length>>=0,r=void 0===r?this.length:r>>>0,t||(t=0),"number"==typeof t)for(a=e;a55295&&r<57344){if(!i){if(r>56319){(e-=3)>-1&&a.push(239,191,189);continue}if(o+1===n){(e-=3)>-1&&a.push(239,191,189);continue}i=r;continue}if(r<56320){(e-=3)>-1&&a.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320)}else i&&(e-=3)>-1&&a.push(239,191,189);if(i=null,r<128){if((e-=1)<0)break;a.push(r)}else if(r<2048){if((e-=2)<0)break;a.push(r>>6|192,63&r|128)}else if(r<65536){if((e-=3)<0)break;a.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;a.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return a}function F(t){return n.toByteArray(function(t){if((t=(t=t.split("=")[0]).trim().replace(I,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function N(t,e,r,n){for(var i=0;i=e.length||i>=t.length);++i)e[i+r]=t[i];return i}function j(t){return t instanceof ArrayBuffer||null!=t&&null!=t.constructor&&"ArrayBuffer"===t.constructor.name&&"number"==typeof t.byteLength}function V(t){return t!=t}},{"base64-js":56,ieee754:356}],87:[function(t,e,r){"use strict";var n=t("./lib/monotone"),i=t("./lib/triangulation"),a=t("./lib/delaunay"),o=t("./lib/filter");function s(t){return[Math.min(t[0],t[1]),Math.max(t[0],t[1])]}function l(t,e){return t[0]-e[0]||t[1]-e[1]}function c(t,e,r){return e in t?t[e]:r}e.exports=function(t,e,r){Array.isArray(e)?(r=r||{},e=e||[]):(r=e||{},e=[]);var u=!!c(r,"delaunay",!0),f=!!c(r,"interior",!0),h=!!c(r,"exterior",!0),p=!!c(r,"infinity",!1);if(!f&&!h||0===t.length)return[];var d=n(t,e);if(u||f!==h||p){for(var g=i(t.length,function(t){return t.map(s).sort(l)}(e)),m=0;m0;){for(var u=r.pop(),s=r.pop(),f=-1,h=-1,l=o[s],d=1;d=0||(e.flip(s,u),i(t,e,r,f,s,h),i(t,e,r,s,h,f),i(t,e,r,h,u,f),i(t,e,r,u,f,h)))}}},{"binary-search-bounds":92,"robust-in-sphere":444}],89:[function(t,e,r){"use strict";var n,i=t("binary-search-bounds");function a(t,e,r,n,i,a,o){this.cells=t,this.neighbor=e,this.flags=n,this.constraint=r,this.active=i,this.next=a,this.boundary=o}function o(t,e){return t[0]-e[0]||t[1]-e[1]||t[2]-e[2]}e.exports=function(t,e,r){var n=function(t,e){for(var r=t.cells(),n=r.length,i=0;i0||l.length>0;){for(;s.length>0;){var p=s.pop();if(c[p]!==-i){c[p]=i;u[p];for(var d=0;d<3;++d){var g=h[3*p+d];g>=0&&0===c[g]&&(f[3*p+d]?l.push(g):(s.push(g),c[g]=i))}}}var m=l;l=s,s=m,l.length=0,i=-i}var v=function(t,e,r){for(var n=0,i=0;i1&&i(r[h[p-2]],r[h[p-1]],a)>0;)t.push([h[p-1],h[p-2],o]),p-=1;h.length=p,h.push(o);var d=u.upperIds;for(p=d.length;p>1&&i(r[d[p-2]],r[d[p-1]],a)<0;)t.push([d[p-2],d[p-1],o]),p-=1;d.length=p,d.push(o)}}function p(t,e){var r;return(r=t.a[0]v[0]&&i.push(new c(v,m,s,f),new c(m,v,o,f))}i.sort(u);for(var y=i[0].a[0]-(1+Math.abs(i[0].a[0]))*Math.pow(2,-52),x=[new l([y,1],[y,0],-1,[],[],[],[])],b=[],f=0,_=i.length;f<_;++f){var w=i[f],k=w.type;k===a?h(b,x,t,w.a,w.idx):k===s?d(x,t,w):g(x,t,w)}return b}},{"binary-search-bounds":92,"robust-orientation":446}],91:[function(t,e,r){"use strict";var n=t("binary-search-bounds");function i(t,e){this.stars=t,this.edges=e}e.exports=function(t,e){for(var r=new Array(t),n=0;n=0}}(),a.removeTriangle=function(t,e,r){var n=this.stars;o(n[t],e,r),o(n[e],r,t),o(n[r],t,e)},a.addTriangle=function(t,e,r){var n=this.stars;n[t].push(e,r),n[e].push(r,t),n[r].push(t,e)},a.opposite=function(t,e){for(var r=this.stars[e],n=1,i=r.length;n>>1,x=a[m]"];return i?e.indexOf("c")<0?a.push(";if(x===y){return m}else if(x<=y){"):a.push(";var p=c(x,y);if(p===0){return m}else if(p<=0){"):a.push(";if(",e,"){i=m;"),r?a.push("l=m+1}else{h=m-1}"):a.push("h=m-1}else{l=m+1}"),a.push("}"),i?a.push("return -1};"):a.push("return i};"),a.join("")}function i(t,e,r,i){return new Function([n("A","x"+t+"y",e,["y"],i),n("P","c(x,y)"+t+"0",e,["y","c"],i),"function dispatchBsearch",r,"(a,y,c,l,h){if(typeof(c)==='function'){return P(a,(l===void 0)?0:l|0,(h===void 0)?a.length-1:h|0,y,c)}else{return A(a,(c===void 0)?0:c|0,(l===void 0)?a.length-1:l|0,y)}}return dispatchBsearch",r].join(""))()}e.exports={ge:i(">=",!1,"GE"),gt:i(">",!1,"GT"),lt:i("<",!0,"LT"),le:i("<=",!0,"LE"),eq:i("-",!0,"EQ",!0)}},{}],93:[function(t,e,r){"use strict";e.exports=function(t){for(var e=1,r=1;rr?r:t:te?e:t}},{}],97:[function(t,e,r){"use strict";e.exports=function(t,e,r){var n;if(r){n=e;for(var i=new Array(e.length),a=0;ae[2]?1:0)}function v(t,e,r){if(0!==t.length){if(e)for(var n=0;n=0;--a){var x=e[u=(S=n[a])[0]],b=x[0],_=x[1],w=t[b],k=t[_];if((w[0]-k[0]||w[1]-k[1])<0){var M=b;b=_,_=M}x[0]=b;var A,T=x[1]=S[1];for(i&&(A=x[2]);a>0&&n[a-1][0]===u;){var S,C=(S=n[--a])[1];i?e.push([T,C,A]):e.push([T,C]),T=C}i?e.push([T,_,A]):e.push([T,_])}return h}(t,e,h,m,r));return v(e,y,r),!!y||(h.length>0||m.length>0)}},{"./lib/rat-seg-intersect":98,"big-rat":60,"big-rat/cmp":58,"big-rat/to-float":72,"box-intersect":78,nextafter:394,"rat-vec":428,"robust-segment-intersect":449,"union-find":482}],98:[function(t,e,r){"use strict";e.exports=function(t,e,r,n){var a=s(e,t),f=s(n,r),h=u(a,f);if(0===o(h))return null;var p=s(t,r),d=u(f,p),g=i(d,h),m=c(a,g);return l(t,m)};var n=t("big-rat/mul"),i=t("big-rat/div"),a=t("big-rat/sub"),o=t("big-rat/sign"),s=t("rat-vec/sub"),l=t("rat-vec/add"),c=t("rat-vec/muls");function u(t,e){return a(n(t[0],e[1]),n(t[1],e[0]))}},{"big-rat/div":59,"big-rat/mul":69,"big-rat/sign":70,"big-rat/sub":71,"rat-vec/add":427,"rat-vec/muls":429,"rat-vec/sub":430}],99:[function(t,e,r){"use strict";var n=t("clamp");function i(t,e){null==e&&(e=!0);var r=t[0],i=t[1],a=t[2],o=t[3];return null==o&&(o=e?1:255),e&&(r*=255,i*=255,a*=255,o*=255),16777216*(r=255&n(r,0,255))+((i=255&n(i,0,255))<<16)+((a=255&n(a,0,255))<<8)+(o=255&n(o,0,255))}e.exports=i,e.exports.to=i,e.exports.from=function(t,e){var r=(t=+t)>>>24,n=(16711680&t)>>>16,i=(65280&t)>>>8,a=255&t;return!1===e?[r,n,i,a]:[r/255,n/255,i/255,a/255]}},{clamp:96}],100:[function(t,e,r){"use strict";e.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}},{}],101:[function(t,e,r){"use strict";var n=t("color-rgba"),i=t("clamp"),a=t("dtype");e.exports=function(t,e){"float"!==e&&e||(e="array"),"uint"===e&&(e="uint8"),"uint_clamped"===e&&(e="uint8_clamped");var r=a(e),o=new r(4);if(t instanceof r)return Array.isArray(t)?t.slice():(o.set(t),o);var s="uint8"!==e&&"uint8_clamped"!==e;return t instanceof Uint8Array||t instanceof Uint8ClampedArray?(o[0]=t[0],o[1]=t[1],o[2]=t[2],o[3]=null!=t[3]?t[3]:255,s&&(o[0]/=255,o[1]/=255,o[2]/=255,o[3]/=255),o):(t.length&&"string"!=typeof t||((t=n(t))[0]/=255,t[1]/=255,t[2]/=255),s?(o[0]=t[0],o[1]=t[1],o[2]=t[2],o[3]=null!=t[3]?t[3]:1):(o[0]=i(Math.round(255*t[0]),0,255),o[1]=i(Math.round(255*t[1]),0,255),o[2]=i(Math.round(255*t[2]),0,255),o[3]=null==t[3]?255:i(Math.floor(255*t[3]),0,255)),o)}},{clamp:96,"color-rgba":103,dtype:136}],102:[function(t,e,r){(function(r){"use strict";var n=t("color-name"),i=t("is-plain-obj"),a=t("defined");e.exports=function(t){var e,s,l=[],c=1;if("string"==typeof t)if(n[t])l=n[t].slice(),s="rgb";else if("transparent"===t)c=0,s="rgb",l=[0,0,0];else if(/^#[A-Fa-f0-9]+$/.test(t)){var u=t.slice(1),f=u.length,h=f<=4;c=1,h?(l=[parseInt(u[0]+u[0],16),parseInt(u[1]+u[1],16),parseInt(u[2]+u[2],16)],4===f&&(c=parseInt(u[3]+u[3],16)/255)):(l=[parseInt(u[0]+u[1],16),parseInt(u[2]+u[3],16),parseInt(u[4]+u[5],16)],8===f&&(c=parseInt(u[6]+u[7],16)/255)),l[0]||(l[0]=0),l[1]||(l[1]=0),l[2]||(l[2]=0),s="rgb"}else if(e=/^((?:rgb|hs[lvb]|hwb|cmyk?|xy[zy]|gray|lab|lchu?v?|[ly]uv|lms)a?)\s*\(([^\)]*)\)/.exec(t)){var p=e[1],u=p.replace(/a$/,"");s=u;var f="cmyk"===u?4:"gray"===u?1:3;l=e[2].trim().split(/\s*,\s*/).map(function(t,e){if(/%$/.test(t))return e===f?parseFloat(t)/100:"rgb"===u?255*parseFloat(t)/100:parseFloat(t);if("h"===u[e]){if(/deg$/.test(t))return parseFloat(t);if(void 0!==o[t])return o[t]}return parseFloat(t)}),p===u&&l.push(1),c=void 0===l[f]?1:l[f],l=l.slice(0,f)}else t.length>10&&/[0-9](?:\s|\/)/.test(t)&&(l=t.match(/([0-9]+)/g).map(function(t){return parseFloat(t)}),s=t.match(/([a-z])/ig).join("").toLowerCase());else if("number"==typeof t)s="rgb",l=[t>>>16,(65280&t)>>>8,255&t];else if(i(t)){var d=a(t.r,t.red,t.R,null);null!==d?(s="rgb",l=[d,a(t.g,t.green,t.G),a(t.b,t.blue,t.B)]):(s="hsl",l=[a(t.h,t.hue,t.H),a(t.s,t.saturation,t.S),a(t.l,t.lightness,t.L,t.b,t.brightness)]),c=a(t.a,t.alpha,t.opacity,1),null!=t.opacity&&(c/=100)}else(Array.isArray(t)||r.ArrayBuffer&&ArrayBuffer.isView&&ArrayBuffer.isView(t))&&(l=[t[0],t[1],t[2]],s="rgb",c=4===t.length?t[3]:1);return{space:s,values:l,alpha:c}};var o={red:0,orange:60,yellow:120,green:180,blue:240,purple:300}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"color-name":100,defined:132,"is-plain-obj":366}],103:[function(t,e,r){"use strict";var n=t("color-parse"),i=t("color-space/hsl"),a=t("clamp");e.exports=function(t){var e;if("string"!=typeof t)throw Error("Argument should be a string");var r=n(t);return r.space?((e=Array(3))[0]=a(r.values[0],0,255),e[1]=a(r.values[1],0,255),e[2]=a(r.values[2],0,255),"h"===r.space[0]&&(e=i.rgb(e)),e.push(a(r.alpha,0,1)),e):[]}},{clamp:96,"color-parse":102,"color-space/hsl":104}],104:[function(t,e,r){"use strict";var n=t("./rgb");e.exports={name:"hsl",min:[0,0,0],max:[360,100,100],channel:["hue","saturation","lightness"],alias:["HSL"],rgb:function(t){var e,r,n,i,a,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[a=255*l,a,a];e=2*l-(r=l<.5?l*(1+s):l+s-l*s),i=[0,0,0];for(var c=0;c<3;c++)(n=o+1/3*-(c-1))<0?n++:n>1&&n--,a=6*n<1?e+6*(r-e)*n:2*n<1?r:3*n<2?e+(r-e)*(2/3-n)*6:e,i[c]=255*a;return i}},n.hsl=function(t){var e,r,n=t[0]/255,i=t[1]/255,a=t[2]/255,o=Math.min(n,i,a),s=Math.max(n,i,a),l=s-o;return s===o?e=0:n===s?e=(i-a)/l:i===s?e=2+(a-n)/l:a===s&&(e=4+(n-i)/l),(e=Math.min(60*e,360))<0&&(e+=360),r=(o+s)/2,[e,100*(s===o?0:r<=.5?l/(s+o):l/(2-s-o)),100*r]}},{"./rgb":105}],105:[function(t,e,r){"use strict";e.exports={name:"rgb",min:[0,0,0],max:[255,255,255],channel:["red","green","blue"],alias:["RGB"]}},{}],106:[function(t,e,r){e.exports={jet:[{index:0,rgb:[0,0,131]},{index:.125,rgb:[0,60,170]},{index:.375,rgb:[5,255,255]},{index:.625,rgb:[255,255,0]},{index:.875,rgb:[250,0,0]},{index:1,rgb:[128,0,0]}],hsv:[{index:0,rgb:[255,0,0]},{index:.169,rgb:[253,255,2]},{index:.173,rgb:[247,255,2]},{index:.337,rgb:[0,252,4]},{index:.341,rgb:[0,252,10]},{index:.506,rgb:[1,249,255]},{index:.671,rgb:[2,0,253]},{index:.675,rgb:[8,0,253]},{index:.839,rgb:[255,0,251]},{index:.843,rgb:[255,0,245]},{index:1,rgb:[255,0,6]}],hot:[{index:0,rgb:[0,0,0]},{index:.3,rgb:[230,0,0]},{index:.6,rgb:[255,210,0]},{index:1,rgb:[255,255,255]}],cool:[{index:0,rgb:[0,255,255]},{index:1,rgb:[255,0,255]}],spring:[{index:0,rgb:[255,0,255]},{index:1,rgb:[255,255,0]}],summer:[{index:0,rgb:[0,128,102]},{index:1,rgb:[255,255,102]}],autumn:[{index:0,rgb:[255,0,0]},{index:1,rgb:[255,255,0]}],winter:[{index:0,rgb:[0,0,255]},{index:1,rgb:[0,255,128]}],bone:[{index:0,rgb:[0,0,0]},{index:.376,rgb:[84,84,116]},{index:.753,rgb:[169,200,200]},{index:1,rgb:[255,255,255]}],copper:[{index:0,rgb:[0,0,0]},{index:.804,rgb:[255,160,102]},{index:1,rgb:[255,199,127]}],greys:[{index:0,rgb:[0,0,0]},{index:1,rgb:[255,255,255]}],yignbu:[{index:0,rgb:[8,29,88]},{index:.125,rgb:[37,52,148]},{index:.25,rgb:[34,94,168]},{index:.375,rgb:[29,145,192]},{index:.5,rgb:[65,182,196]},{index:.625,rgb:[127,205,187]},{index:.75,rgb:[199,233,180]},{index:.875,rgb:[237,248,217]},{index:1,rgb:[255,255,217]}],greens:[{index:0,rgb:[0,68,27]},{index:.125,rgb:[0,109,44]},{index:.25,rgb:[35,139,69]},{index:.375,rgb:[65,171,93]},{index:.5,rgb:[116,196,118]},{index:.625,rgb:[161,217,155]},{index:.75,rgb:[199,233,192]},{index:.875,rgb:[229,245,224]},{index:1,rgb:[247,252,245]}],yiorrd:[{index:0,rgb:[128,0,38]},{index:.125,rgb:[189,0,38]},{index:.25,rgb:[227,26,28]},{index:.375,rgb:[252,78,42]},{index:.5,rgb:[253,141,60]},{index:.625,rgb:[254,178,76]},{index:.75,rgb:[254,217,118]},{index:.875,rgb:[255,237,160]},{index:1,rgb:[255,255,204]}],bluered:[{index:0,rgb:[0,0,255]},{index:1,rgb:[255,0,0]}],rdbu:[{index:0,rgb:[5,10,172]},{index:.35,rgb:[106,137,247]},{index:.5,rgb:[190,190,190]},{index:.6,rgb:[220,170,132]},{index:.7,rgb:[230,145,90]},{index:1,rgb:[178,10,28]}],picnic:[{index:0,rgb:[0,0,255]},{index:.1,rgb:[51,153,255]},{index:.2,rgb:[102,204,255]},{index:.3,rgb:[153,204,255]},{index:.4,rgb:[204,204,255]},{index:.5,rgb:[255,255,255]},{index:.6,rgb:[255,204,255]},{index:.7,rgb:[255,153,255]},{index:.8,rgb:[255,102,204]},{index:.9,rgb:[255,102,102]},{index:1,rgb:[255,0,0]}],rainbow:[{index:0,rgb:[150,0,90]},{index:.125,rgb:[0,0,200]},{index:.25,rgb:[0,25,255]},{index:.375,rgb:[0,152,255]},{index:.5,rgb:[44,255,150]},{index:.625,rgb:[151,255,0]},{index:.75,rgb:[255,234,0]},{index:.875,rgb:[255,111,0]},{index:1,rgb:[255,0,0]}],portland:[{index:0,rgb:[12,51,131]},{index:.25,rgb:[10,136,186]},{index:.5,rgb:[242,211,56]},{index:.75,rgb:[242,143,56]},{index:1,rgb:[217,30,30]}],blackbody:[{index:0,rgb:[0,0,0]},{index:.2,rgb:[230,0,0]},{index:.4,rgb:[230,210,0]},{index:.7,rgb:[255,255,255]},{index:1,rgb:[160,200,255]}],earth:[{index:0,rgb:[0,0,130]},{index:.1,rgb:[0,180,180]},{index:.2,rgb:[40,210,40]},{index:.4,rgb:[230,230,50]},{index:.6,rgb:[120,70,20]},{index:1,rgb:[255,255,255]}],electric:[{index:0,rgb:[0,0,0]},{index:.15,rgb:[30,0,100]},{index:.4,rgb:[120,0,100]},{index:.6,rgb:[160,90,0]},{index:.8,rgb:[230,200,0]},{index:1,rgb:[255,250,220]}],alpha:[{index:0,rgb:[255,255,255,0]},{index:1,rgb:[255,255,255,1]}],viridis:[{index:0,rgb:[68,1,84]},{index:.13,rgb:[71,44,122]},{index:.25,rgb:[59,81,139]},{index:.38,rgb:[44,113,142]},{index:.5,rgb:[33,144,141]},{index:.63,rgb:[39,173,129]},{index:.75,rgb:[92,200,99]},{index:.88,rgb:[170,220,50]},{index:1,rgb:[253,231,37]}],inferno:[{index:0,rgb:[0,0,4]},{index:.13,rgb:[31,12,72]},{index:.25,rgb:[85,15,109]},{index:.38,rgb:[136,34,106]},{index:.5,rgb:[186,54,85]},{index:.63,rgb:[227,89,51]},{index:.75,rgb:[249,140,10]},{index:.88,rgb:[249,201,50]},{index:1,rgb:[252,255,164]}],magma:[{index:0,rgb:[0,0,4]},{index:.13,rgb:[28,16,68]},{index:.25,rgb:[79,18,123]},{index:.38,rgb:[129,37,129]},{index:.5,rgb:[181,54,122]},{index:.63,rgb:[229,80,100]},{index:.75,rgb:[251,135,97]},{index:.88,rgb:[254,194,135]},{index:1,rgb:[252,253,191]}],plasma:[{index:0,rgb:[13,8,135]},{index:.13,rgb:[75,3,161]},{index:.25,rgb:[125,3,168]},{index:.38,rgb:[168,34,150]},{index:.5,rgb:[203,70,121]},{index:.63,rgb:[229,107,93]},{index:.75,rgb:[248,148,65]},{index:.88,rgb:[253,195,40]},{index:1,rgb:[240,249,33]}],warm:[{index:0,rgb:[125,0,179]},{index:.13,rgb:[172,0,187]},{index:.25,rgb:[219,0,170]},{index:.38,rgb:[255,0,130]},{index:.5,rgb:[255,63,74]},{index:.63,rgb:[255,123,0]},{index:.75,rgb:[234,176,0]},{index:.88,rgb:[190,228,0]},{index:1,rgb:[147,255,0]}],cool:[{index:0,rgb:[125,0,179]},{index:.13,rgb:[116,0,218]},{index:.25,rgb:[98,74,237]},{index:.38,rgb:[68,146,231]},{index:.5,rgb:[0,204,197]},{index:.63,rgb:[0,247,146]},{index:.75,rgb:[0,255,88]},{index:.88,rgb:[40,255,8]},{index:1,rgb:[147,255,0]}],"rainbow-soft":[{index:0,rgb:[125,0,179]},{index:.1,rgb:[199,0,180]},{index:.2,rgb:[255,0,121]},{index:.3,rgb:[255,108,0]},{index:.4,rgb:[222,194,0]},{index:.5,rgb:[150,255,0]},{index:.6,rgb:[0,255,55]},{index:.7,rgb:[0,246,150]},{index:.8,rgb:[50,167,222]},{index:.9,rgb:[103,51,235]},{index:1,rgb:[124,0,186]}],bathymetry:[{index:0,rgb:[40,26,44]},{index:.13,rgb:[59,49,90]},{index:.25,rgb:[64,76,139]},{index:.38,rgb:[63,110,151]},{index:.5,rgb:[72,142,158]},{index:.63,rgb:[85,174,163]},{index:.75,rgb:[120,206,163]},{index:.88,rgb:[187,230,172]},{index:1,rgb:[253,254,204]}],cdom:[{index:0,rgb:[47,15,62]},{index:.13,rgb:[87,23,86]},{index:.25,rgb:[130,28,99]},{index:.38,rgb:[171,41,96]},{index:.5,rgb:[206,67,86]},{index:.63,rgb:[230,106,84]},{index:.75,rgb:[242,149,103]},{index:.88,rgb:[249,193,135]},{index:1,rgb:[254,237,176]}],chlorophyll:[{index:0,rgb:[18,36,20]},{index:.13,rgb:[25,63,41]},{index:.25,rgb:[24,91,59]},{index:.38,rgb:[13,119,72]},{index:.5,rgb:[18,148,80]},{index:.63,rgb:[80,173,89]},{index:.75,rgb:[132,196,122]},{index:.88,rgb:[175,221,162]},{index:1,rgb:[215,249,208]}],density:[{index:0,rgb:[54,14,36]},{index:.13,rgb:[89,23,80]},{index:.25,rgb:[110,45,132]},{index:.38,rgb:[120,77,178]},{index:.5,rgb:[120,113,213]},{index:.63,rgb:[115,151,228]},{index:.75,rgb:[134,185,227]},{index:.88,rgb:[177,214,227]},{index:1,rgb:[230,241,241]}],"freesurface-blue":[{index:0,rgb:[30,4,110]},{index:.13,rgb:[47,14,176]},{index:.25,rgb:[41,45,236]},{index:.38,rgb:[25,99,212]},{index:.5,rgb:[68,131,200]},{index:.63,rgb:[114,156,197]},{index:.75,rgb:[157,181,203]},{index:.88,rgb:[200,208,216]},{index:1,rgb:[241,237,236]}],"freesurface-red":[{index:0,rgb:[60,9,18]},{index:.13,rgb:[100,17,27]},{index:.25,rgb:[142,20,29]},{index:.38,rgb:[177,43,27]},{index:.5,rgb:[192,87,63]},{index:.63,rgb:[205,125,105]},{index:.75,rgb:[216,162,148]},{index:.88,rgb:[227,199,193]},{index:1,rgb:[241,237,236]}],oxygen:[{index:0,rgb:[64,5,5]},{index:.13,rgb:[106,6,15]},{index:.25,rgb:[144,26,7]},{index:.38,rgb:[168,64,3]},{index:.5,rgb:[188,100,4]},{index:.63,rgb:[206,136,11]},{index:.75,rgb:[220,174,25]},{index:.88,rgb:[231,215,44]},{index:1,rgb:[248,254,105]}],par:[{index:0,rgb:[51,20,24]},{index:.13,rgb:[90,32,35]},{index:.25,rgb:[129,44,34]},{index:.38,rgb:[159,68,25]},{index:.5,rgb:[182,99,19]},{index:.63,rgb:[199,134,22]},{index:.75,rgb:[212,171,35]},{index:.88,rgb:[221,210,54]},{index:1,rgb:[225,253,75]}],phase:[{index:0,rgb:[145,105,18]},{index:.13,rgb:[184,71,38]},{index:.25,rgb:[186,58,115]},{index:.38,rgb:[160,71,185]},{index:.5,rgb:[110,97,218]},{index:.63,rgb:[50,123,164]},{index:.75,rgb:[31,131,110]},{index:.88,rgb:[77,129,34]},{index:1,rgb:[145,105,18]}],salinity:[{index:0,rgb:[42,24,108]},{index:.13,rgb:[33,50,162]},{index:.25,rgb:[15,90,145]},{index:.38,rgb:[40,118,137]},{index:.5,rgb:[59,146,135]},{index:.63,rgb:[79,175,126]},{index:.75,rgb:[120,203,104]},{index:.88,rgb:[193,221,100]},{index:1,rgb:[253,239,154]}],temperature:[{index:0,rgb:[4,35,51]},{index:.13,rgb:[23,51,122]},{index:.25,rgb:[85,59,157]},{index:.38,rgb:[129,79,143]},{index:.5,rgb:[175,95,130]},{index:.63,rgb:[222,112,101]},{index:.75,rgb:[249,146,66]},{index:.88,rgb:[249,196,65]},{index:1,rgb:[232,250,91]}],turbidity:[{index:0,rgb:[34,31,27]},{index:.13,rgb:[65,50,41]},{index:.25,rgb:[98,69,52]},{index:.38,rgb:[131,89,57]},{index:.5,rgb:[161,112,59]},{index:.63,rgb:[185,140,66]},{index:.75,rgb:[202,174,88]},{index:.88,rgb:[216,209,126]},{index:1,rgb:[233,246,171]}],"velocity-blue":[{index:0,rgb:[17,32,64]},{index:.13,rgb:[35,52,116]},{index:.25,rgb:[29,81,156]},{index:.38,rgb:[31,113,162]},{index:.5,rgb:[50,144,169]},{index:.63,rgb:[87,173,176]},{index:.75,rgb:[149,196,189]},{index:.88,rgb:[203,221,211]},{index:1,rgb:[254,251,230]}],"velocity-green":[{index:0,rgb:[23,35,19]},{index:.13,rgb:[24,64,38]},{index:.25,rgb:[11,95,45]},{index:.38,rgb:[39,123,35]},{index:.5,rgb:[95,146,12]},{index:.63,rgb:[152,165,18]},{index:.75,rgb:[201,186,69]},{index:.88,rgb:[233,216,137]},{index:1,rgb:[255,253,205]}],cubehelix:[{index:0,rgb:[0,0,0]},{index:.07,rgb:[22,5,59]},{index:.13,rgb:[60,4,105]},{index:.2,rgb:[109,1,135]},{index:.27,rgb:[161,0,147]},{index:.33,rgb:[210,2,142]},{index:.4,rgb:[251,11,123]},{index:.47,rgb:[255,29,97]},{index:.53,rgb:[255,54,69]},{index:.6,rgb:[255,85,46]},{index:.67,rgb:[255,120,34]},{index:.73,rgb:[255,157,37]},{index:.8,rgb:[241,191,57]},{index:.87,rgb:[224,220,93]},{index:.93,rgb:[218,241,142]},{index:1,rgb:[227,253,198]}]}},{}],107:[function(t,e,r){"use strict";var n=t("./colorScale"),i=t("lerp");function a(t){return[t[0]/255,t[1]/255,t[2]/255,t[3]]}function o(t){for(var e,r="#",n=0;n<3;++n)r+=("00"+(e=(e=t[n]).toString(16))).substr(e.length);return r}function s(t){return"rgba("+t.join(",")+")"}e.exports=function(t){var e,r,l,c,u,f,h,p,d,g;t||(t={});p=(t.nshades||72)-1,h=t.format||"hex",(f=t.colormap)||(f="jet");if("string"==typeof f){if(f=f.toLowerCase(),!n[f])throw Error(f+" not a supported colorscale");u=n[f]}else{if(!Array.isArray(f))throw Error("unsupported colormap option",f);u=f.slice()}if(u.length>p)throw new Error(f+" map requires nshades to be at least size "+u.length);d=Array.isArray(t.alpha)?2!==t.alpha.length?[1,1]:t.alpha.slice():"number"==typeof t.alpha?[t.alpha,t.alpha]:[1,1];e=u.map(function(t){return Math.round(t.index*p)}),d[0]=Math.min(Math.max(d[0],0),1),d[1]=Math.min(Math.max(d[1],0),1);var m=u.map(function(t,e){var r=u[e].index,n=u[e].rgb.slice();return 4===n.length&&n[3]>=0&&n[3]<=1?n:(n[3]=d[0]+(d[1]-d[0])*r,n)}),v=[];for(g=0;g0?-1:l(t,e,a)?-1:1:0===s?c>0?1:l(t,e,r)?1:-1:i(c-s)}var h=n(t,e,r);if(h>0)return o>0&&n(t,e,a)>0?1:-1;if(h<0)return o>0||n(t,e,a)>0?1:-1;var p=n(t,e,a);return p>0?1:l(t,e,r)?1:-1};var n=t("robust-orientation"),i=t("signum"),a=t("two-sum"),o=t("robust-product"),s=t("robust-sum");function l(t,e,r){var n=a(t[0],-e[0]),i=a(t[1],-e[1]),l=a(r[0],-e[0]),c=a(r[1],-e[1]),u=s(o(n,l),o(i,c));return u[u.length-1]>=0}},{"robust-orientation":446,"robust-product":447,"robust-sum":451,signum:452,"two-sum":480}],109:[function(t,e,r){e.exports=function(t,e){var r=t.length,a=t.length-e.length;if(a)return a;switch(r){case 0:return 0;case 1:return t[0]-e[0];case 2:return t[0]+t[1]-e[0]-e[1]||n(t[0],t[1])-n(e[0],e[1]);case 3:var o=t[0]+t[1],s=e[0]+e[1];if(a=o+t[2]-(s+e[2]))return a;var l=n(t[0],t[1]),c=n(e[0],e[1]);return n(l,t[2])-n(c,e[2])||n(l+t[2],o)-n(c+e[2],s);case 4:var u=t[0],f=t[1],h=t[2],p=t[3],d=e[0],g=e[1],m=e[2],v=e[3];return u+f+h+p-(d+g+m+v)||n(u,f,h,p)-n(d,g,m,v,d)||n(u+f,u+h,u+p,f+h,f+p,h+p)-n(d+g,d+m,d+v,g+m,g+v,m+v)||n(u+f+h,u+f+p,u+h+p,f+h+p)-n(d+g+m,d+g+v,d+m+v,g+m+v);default:for(var y=t.slice().sort(i),x=e.slice().sort(i),b=0;bt[r][0]&&(r=n);return er?[[r],[e]]:[[e]]}},{}],113:[function(t,e,r){"use strict";e.exports=function(t){var e=n(t),r=e.length;if(r<=2)return[];for(var i=new Array(r),a=e[r-1],o=0;o=e[l]&&(s+=1);a[o]=s}}return t}(o,r)}};var n=t("incremental-convex-hull"),i=t("affine-hull")},{"affine-hull":47,"incremental-convex-hull":357}],115:[function(t,e,r){e.exports={AFG:"afghan",ALA:"\\b\\wland",ALB:"albania",DZA:"algeria",ASM:"^(?=.*americ).*samoa",AND:"andorra",AGO:"angola",AIA:"anguill?a",ATA:"antarctica",ATG:"antigua",ARG:"argentin",ARM:"armenia",ABW:"^(?!.*bonaire).*\\baruba",AUS:"australia",AUT:"^(?!.*hungary).*austria|\\baustri.*\\bemp",AZE:"azerbaijan",BHS:"bahamas",BHR:"bahrain",BGD:"bangladesh|^(?=.*east).*paki?stan",BRB:"barbados",BLR:"belarus|byelo",BEL:"^(?!.*luxem).*belgium",BLZ:"belize|^(?=.*british).*honduras",BEN:"benin|dahome",BMU:"bermuda",BTN:"bhutan",BOL:"bolivia",BES:"^(?=.*bonaire).*eustatius|^(?=.*carib).*netherlands|\\bbes.?islands",BIH:"herzegovina|bosnia",BWA:"botswana|bechuana",BVT:"bouvet",BRA:"brazil",IOT:"british.?indian.?ocean",BRN:"brunei",BGR:"bulgaria",BFA:"burkina|\\bfaso|upper.?volta",BDI:"burundi",CPV:"verde",KHM:"cambodia|kampuchea|khmer",CMR:"cameroon",CAN:"canada",CYM:"cayman",CAF:"\\bcentral.african.republic",TCD:"\\bchad",CHL:"\\bchile",CHN:"^(?!.*\\bmac)(?!.*\\bhong)(?!.*\\btai)(?!.*\\brep).*china|^(?=.*peo)(?=.*rep).*china",CXR:"christmas",CCK:"\\bcocos|keeling",COL:"colombia",COM:"comoro",COG:"^(?!.*\\bdem)(?!.*\\bd[\\.]?r)(?!.*kinshasa)(?!.*zaire)(?!.*belg)(?!.*l.opoldville)(?!.*free).*\\bcongo",COK:"\\bcook",CRI:"costa.?rica",CIV:"ivoire|ivory",HRV:"croatia",CUB:"\\bcuba",CUW:"^(?!.*bonaire).*\\bcura(c|\xe7)ao",CYP:"cyprus",CSK:"czechoslovakia",CZE:"^(?=.*rep).*czech|czechia|bohemia",COD:"\\bdem.*congo|congo.*\\bdem|congo.*\\bd[\\.]?r|\\bd[\\.]?r.*congo|belgian.?congo|congo.?free.?state|kinshasa|zaire|l.opoldville|drc|droc|rdc",DNK:"denmark",DJI:"djibouti",DMA:"dominica(?!n)",DOM:"dominican.rep",ECU:"ecuador",EGY:"egypt",SLV:"el.?salvador",GNQ:"guine.*eq|eq.*guine|^(?=.*span).*guinea",ERI:"eritrea",EST:"estonia",ETH:"ethiopia|abyssinia",FLK:"falkland|malvinas",FRO:"faroe|faeroe",FJI:"fiji",FIN:"finland",FRA:"^(?!.*\\bdep)(?!.*martinique).*france|french.?republic|\\bgaul",GUF:"^(?=.*french).*guiana",PYF:"french.?polynesia|tahiti",ATF:"french.?southern",GAB:"gabon",GMB:"gambia",GEO:"^(?!.*south).*georgia",DDR:"german.?democratic.?republic|democratic.?republic.*germany|east.germany",DEU:"^(?!.*east).*germany|^(?=.*\\bfed.*\\brep).*german",GHA:"ghana|gold.?coast",GIB:"gibraltar",GRC:"greece|hellenic|hellas",GRL:"greenland",GRD:"grenada",GLP:"guadeloupe",GUM:"\\bguam",GTM:"guatemala",GGY:"guernsey",GIN:"^(?!.*eq)(?!.*span)(?!.*bissau)(?!.*portu)(?!.*new).*guinea",GNB:"bissau|^(?=.*portu).*guinea",GUY:"guyana|british.?guiana",HTI:"haiti",HMD:"heard.*mcdonald",VAT:"holy.?see|vatican|papal.?st",HND:"^(?!.*brit).*honduras",HKG:"hong.?kong",HUN:"^(?!.*austr).*hungary",ISL:"iceland",IND:"india(?!.*ocea)",IDN:"indonesia",IRN:"\\biran|persia",IRQ:"\\biraq|mesopotamia",IRL:"(^ireland)|(^republic.*ireland)",IMN:"^(?=.*isle).*\\bman",ISR:"israel",ITA:"italy",JAM:"jamaica",JPN:"japan",JEY:"jersey",JOR:"jordan",KAZ:"kazak",KEN:"kenya|british.?east.?africa|east.?africa.?prot",KIR:"kiribati",PRK:"^(?=.*democrat|people|north|d.*p.*.r).*\\bkorea|dprk|korea.*(d.*p.*r)",KWT:"kuwait",KGZ:"kyrgyz|kirghiz",LAO:"\\blaos?\\b",LVA:"latvia",LBN:"lebanon",LSO:"lesotho|basuto",LBR:"liberia",LBY:"libya",LIE:"liechtenstein",LTU:"lithuania",LUX:"^(?!.*belg).*luxem",MAC:"maca(o|u)",MDG:"madagascar|malagasy",MWI:"malawi|nyasa",MYS:"malaysia",MDV:"maldive",MLI:"\\bmali\\b",MLT:"\\bmalta",MHL:"marshall",MTQ:"martinique",MRT:"mauritania",MUS:"mauritius",MYT:"\\bmayotte",MEX:"\\bmexic",FSM:"fed.*micronesia|micronesia.*fed",MCO:"monaco",MNG:"mongolia",MNE:"^(?!.*serbia).*montenegro",MSR:"montserrat",MAR:"morocco|\\bmaroc",MOZ:"mozambique",MMR:"myanmar|burma",NAM:"namibia",NRU:"nauru",NPL:"nepal",NLD:"^(?!.*\\bant)(?!.*\\bcarib).*netherlands",ANT:"^(?=.*\\bant).*(nether|dutch)",NCL:"new.?caledonia",NZL:"new.?zealand",NIC:"nicaragua",NER:"\\bniger(?!ia)",NGA:"nigeria",NIU:"niue",NFK:"norfolk",MNP:"mariana",NOR:"norway",OMN:"\\boman|trucial",PAK:"^(?!.*east).*paki?stan",PLW:"palau",PSE:"palestin|\\bgaza|west.?bank",PAN:"panama",PNG:"papua|new.?guinea",PRY:"paraguay",PER:"peru",PHL:"philippines",PCN:"pitcairn",POL:"poland",PRT:"portugal",PRI:"puerto.?rico",QAT:"qatar",KOR:"^(?!.*d.*p.*r)(?!.*democrat)(?!.*people)(?!.*north).*\\bkorea(?!.*d.*p.*r)",MDA:"moldov|b(a|e)ssarabia",REU:"r(e|\xe9)union",ROU:"r(o|u|ou)mania",RUS:"\\brussia|soviet.?union|u\\.?s\\.?s\\.?r|socialist.?republics",RWA:"rwanda",BLM:"barth(e|\xe9)lemy",SHN:"helena",KNA:"kitts|\\bnevis",LCA:"\\blucia",MAF:"^(?=.*collectivity).*martin|^(?=.*france).*martin(?!ique)|^(?=.*french).*martin(?!ique)",SPM:"miquelon",VCT:"vincent",WSM:"^(?!.*amer).*samoa",SMR:"san.?marino",STP:"\\bs(a|\xe3)o.?tom(e|\xe9)",SAU:"\\bsa\\w*.?arabia",SEN:"senegal",SRB:"^(?!.*monte).*serbia",SYC:"seychell",SLE:"sierra",SGP:"singapore",SXM:"^(?!.*martin)(?!.*saba).*maarten",SVK:"^(?!.*cze).*slovak",SVN:"slovenia",SLB:"solomon",SOM:"somali",ZAF:"south.africa|s\\\\..?africa",SGS:"south.?georgia|sandwich",SSD:"\\bs\\w*.?sudan",ESP:"spain",LKA:"sri.?lanka|ceylon",SDN:"^(?!.*\\bs(?!u)).*sudan",SUR:"surinam|dutch.?guiana",SJM:"svalbard",SWZ:"swaziland",SWE:"sweden",CHE:"switz|swiss",SYR:"syria",TWN:"taiwan|taipei|formosa|^(?!.*peo)(?=.*rep).*china",TJK:"tajik",THA:"thailand|\\bsiam",MKD:"macedonia|fyrom",TLS:"^(?=.*leste).*timor|^(?=.*east).*timor",TGO:"togo",TKL:"tokelau",TON:"tonga",TTO:"trinidad|tobago",TUN:"tunisia",TUR:"turkey",TKM:"turkmen",TCA:"turks",TUV:"tuvalu",UGA:"uganda",UKR:"ukrain",ARE:"emirates|^u\\.?a\\.?e\\.?$|united.?arab.?em",GBR:"united.?kingdom|britain|^u\\.?k\\.?$",TZA:"tanzania",USA:"united.?states\\b(?!.*islands)|\\bu\\.?s\\.?a\\.?\\b|^\\s*u\\.?s\\.?\\b(?!.*islands)",UMI:"minor.?outlying.?is",URY:"uruguay",UZB:"uzbek",VUT:"vanuatu|new.?hebrides",VEN:"venezuela",VNM:"^(?!.*republic).*viet.?nam|^(?=.*socialist).*viet.?nam",VGB:"^(?=.*\\bu\\.?\\s?k).*virgin|^(?=.*brit).*virgin|^(?=.*kingdom).*virgin",VIR:"^(?=.*\\bu\\.?\\s?s).*virgin|^(?=.*states).*virgin",WLF:"futuna|wallis",ESH:"western.sahara",YEM:"^(?!.*arab)(?!.*north)(?!.*sana)(?!.*peo)(?!.*dem)(?!.*south)(?!.*aden)(?!.*\\bp\\.?d\\.?r).*yemen",YMD:"^(?=.*peo).*yemen|^(?!.*rep)(?=.*dem).*yemen|^(?=.*south).*yemen|^(?=.*aden).*yemen|^(?=.*\\bp\\.?d\\.?r).*yemen",YUG:"yugoslavia",ZMB:"zambia|northern.?rhodesia",EAZ:"zanzibar",ZWE:"zimbabwe|^(?!.*northern).*rhodesia"}},{}],116:[function(t,e,r){"use strict";e.exports=function(t,e,r,n,i,a){var o=i-1,s=i*i,l=o*o,c=(1+2*i)*l,u=i*l,f=s*(3-2*i),h=s*o;if(t.length){a||(a=new Array(t.length));for(var p=t.length-1;p>=0;--p)a[p]=c*t[p]+u*e[p]+f*r[p]+h*n[p];return a}return c*t+u*e+f*r+h*n},e.exports.derivative=function(t,e,r,n,i,a){var o=6*i*i-6*i,s=3*i*i-4*i+1,l=-6*i*i+6*i,c=3*i*i-2*i;if(t.length){a||(a=new Array(t.length));for(var u=t.length-1;u>=0;--u)a[u]=o*t[u]+s*e[u]+l*r[u]+c*n[u];return a}return o*t+s*e+l*r[u]+c*n}},{}],117:[function(t,e,r){"use strict";var n=t("./lib/thunk.js");function i(){this.argTypes=[],this.shimArgs=[],this.arrayArgs=[],this.arrayBlockIndices=[],this.scalarArgs=[],this.offsetArgs=[],this.offsetArgIndex=[],this.indexArgs=[],this.shapeArgs=[],this.funcName="",this.pre=null,this.body=null,this.post=null,this.debug=!1}e.exports=function(t){var e=new i;e.pre=t.pre,e.body=t.body,e.post=t.post;var r=t.args.slice(0);e.argTypes=r;for(var a=0;a0)throw new Error("cwise: pre() block may not reference array args");if(a0)throw new Error("cwise: post() block may not reference array args")}else if("scalar"===o)e.scalarArgs.push(a),e.shimArgs.push("scalar"+a);else if("index"===o){if(e.indexArgs.push(a),a0)throw new Error("cwise: pre() block may not reference array index");if(a0)throw new Error("cwise: post() block may not reference array index")}else if("shape"===o){if(e.shapeArgs.push(a),ar.length)throw new Error("cwise: Too many arguments in pre() block");if(e.body.args.length>r.length)throw new Error("cwise: Too many arguments in body() block");if(e.post.args.length>r.length)throw new Error("cwise: Too many arguments in post() block");return e.debug=!!t.printCode||!!t.debug,e.funcName=t.funcName||"cwise",e.blockSize=t.blockSize||64,n(e)}},{"./lib/thunk.js":119}],118:[function(t,e,r){"use strict";var n=t("uniq");function i(t,e,r){var n,i,a=t.length,o=e.arrayArgs.length,s=e.indexArgs.length>0,l=[],c=[],u=0,f=0;for(n=0;n0&&l.push("var "+c.join(",")),n=a-1;n>=0;--n)u=t[n],l.push(["for(i",n,"=0;i",n,"0&&l.push(["index[",f,"]-=s",f].join("")),l.push(["++index[",u,"]"].join(""))),l.push("}")}return l.join("\n")}function a(t,e,r){for(var n=t.body,i=[],a=[],o=0;o0&&y.push("shape=SS.slice(0)"),t.indexArgs.length>0){var x=new Array(r);for(l=0;l0&&v.push("var "+y.join(",")),l=0;l3&&v.push(a(t.pre,t,s));var k=a(t.body,t,s),M=function(t){for(var e=0,r=t[0].length;e0,c=[],u=0;u0;){"].join("")),c.push(["if(j",u,"<",s,"){"].join("")),c.push(["s",e[u],"=j",u].join("")),c.push(["j",u,"=0"].join("")),c.push(["}else{s",e[u],"=",s].join("")),c.push(["j",u,"-=",s,"}"].join("")),l&&c.push(["index[",e[u],"]=j",u].join(""));for(u=0;u3&&v.push(a(t.post,t,s)),t.debug&&console.log("-----Generated cwise routine for ",e,":\n"+v.join("\n")+"\n----------");var A=[t.funcName||"unnamed","_cwise_loop_",o[0].join("s"),"m",M,function(t){for(var e=new Array(t.length),r=!0,n=0;n0&&(r=r&&e[n]===e[n-1])}return r?e[0]:e.join("")}(s)].join("");return new Function(["function ",A,"(",m.join(","),"){",v.join("\n"),"} return ",A].join(""))()}},{uniq:483}],119:[function(t,e,r){"use strict";var n=t("./compile.js");e.exports=function(t){var e=["'use strict'","var CACHED={}"],r=[],i=t.funcName+"_cwise_thunk";e.push(["return function ",i,"(",t.shimArgs.join(","),"){"].join(""));for(var a=[],o=[],s=[["array",t.arrayArgs[0],".shape.slice(",Math.max(0,t.arrayBlockIndices[0]),t.arrayBlockIndices[0]<0?","+t.arrayBlockIndices[0]+")":")"].join("")],l=[],c=[],u=0;u0&&(l.push("array"+t.arrayArgs[0]+".shape.length===array"+f+".shape.length+"+(Math.abs(t.arrayBlockIndices[0])-Math.abs(t.arrayBlockIndices[u]))),c.push("array"+t.arrayArgs[0]+".shape[shapeIndex+"+Math.max(0,t.arrayBlockIndices[0])+"]===array"+f+".shape[shapeIndex+"+Math.max(0,t.arrayBlockIndices[u])+"]"))}for(t.arrayArgs.length>1&&(e.push("if (!("+l.join(" && ")+")) throw new Error('cwise: Arrays do not all have the same dimensionality!')"),e.push("for(var shapeIndex=array"+t.arrayArgs[0]+".shape.length-"+Math.abs(t.arrayBlockIndices[0])+"; shapeIndex--\x3e0;) {"),e.push("if (!("+c.join(" && ")+")) throw new Error('cwise: Arrays do not all have the same shape!')"),e.push("}")),u=0;ue?1:t>=e?0:NaN},r=function(t){var r;return 1===t.length&&(r=t,t=function(t,n){return e(r(t),n)}),{left:function(e,r,n,i){for(null==n&&(n=0),null==i&&(i=e.length);n>>1;t(e[a],r)<0?n=a+1:i=a}return n},right:function(e,r,n,i){for(null==n&&(n=0),null==i&&(i=e.length);n>>1;t(e[a],r)>0?i=a:n=a+1}return n}}};var n=r(e),i=n.right,a=n.left;function o(t,e){return[t,e]}var s=function(t){return null===t?NaN:+t},l=function(t,e){var r,n,i=t.length,a=0,o=-1,l=0,c=0;if(null==e)for(;++o1)return c/(a-1)},c=function(t,e){var r=l(t,e);return r?Math.sqrt(r):r},u=function(t,e){var r,n,i,a=t.length,o=-1;if(null==e){for(;++o=r)for(n=i=r;++or&&(n=r),i=r)for(n=i=r;++or&&(n=r),i=0?(a>=v?10:a>=y?5:a>=x?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=v?10:a>=y?5:a>=x?2:1)}function _(t,e,r){var n=Math.abs(e-t)/Math.max(0,r),i=Math.pow(10,Math.floor(Math.log(n)/Math.LN10)),a=n/i;return a>=v?i*=10:a>=y?i*=5:a>=x&&(i*=2),e=1)return+r(t[n-1],n-1,t);var n,i=(n-1)*e,a=Math.floor(i),o=+r(t[a],a,t);return o+(+r(t[a+1],a+1,t)-o)*(i-a)}},M=function(t,e){var r,n,i=t.length,a=-1;if(null==e){for(;++a=r)for(n=r;++ar&&(n=r)}else for(;++a=r)for(n=r;++ar&&(n=r);return n},A=function(t){if(!(i=t.length))return[];for(var e=-1,r=M(t,T),n=new Array(r);++et?1:e>=t?0:NaN},t.deviation=c,t.extent=u,t.histogram=function(){var t=g,e=u,r=w;function n(n){var a,o,s=n.length,l=new Array(s);for(a=0;af;)h.pop(),--p;var d,g=new Array(p+1);for(a=0;a<=p;++a)(d=g[a]=[]).x0=a>0?h[a-1]:u,d.x1=a=r)for(n=r;++an&&(n=r)}else for(;++a=r)for(n=r;++an&&(n=r);return n},t.mean=function(t,e){var r,n=t.length,i=n,a=-1,o=0;if(null==e)for(;++a=0;)for(e=(n=t[i]).length;--e>=0;)r[--o]=n[e];return r},t.min=M,t.pairs=function(t,e){null==e&&(e=o);for(var r=0,n=t.length-1,i=t[0],a=new Array(n<0?0:n);r0)return[t];if((n=e0)for(t=Math.ceil(t/o),e=Math.floor(e/o),a=new Array(i=Math.ceil(e-t+1));++s=l.length)return null!=t&&n.sort(t),null!=e?e(n):n;for(var s,c,f,h=-1,p=n.length,d=l[i++],g=r(),m=a();++hl.length)return r;var i,a=c[n-1];return null!=e&&n>=l.length?i=r.entries():(i=[],r.each(function(e,r){i.push({key:r,values:t(e,n)})})),null!=a?i.sort(function(t,e){return a(t.key,e.key)}):i}(u(t,0,a,o),0)},key:function(t){return l.push(t),s},sortKeys:function(t){return c[l.length-1]=t,s},sortValues:function(e){return t=e,s},rollup:function(t){return e=t,s}}},t.set=c,t.map=r,t.keys=function(t){var e=[];for(var r in t)e.push(r);return e},t.values=function(t){var e=[];for(var r in t)e.push(t[r]);return e},t.entries=function(t){var e=[];for(var r in t)e.push({key:r,value:t[r]});return e},Object.defineProperty(t,"__esModule",{value:!0})}("object"==typeof r&&void 0!==e?r:n.d3=n.d3||{})},{}],125:[function(t,e,r){var n;n=this,function(t){"use strict";var e=function(t,e,r){t.prototype=e.prototype=r,r.constructor=t};function r(t,e){var r=Object.create(t.prototype);for(var n in e)r[n]=e[n];return r}function n(){}var i="\\s*([+-]?\\d+)\\s*",a="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",o="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",s=/^#([0-9a-f]{3})$/,l=/^#([0-9a-f]{6})$/,c=new RegExp("^rgb\\("+[i,i,i]+"\\)$"),u=new RegExp("^rgb\\("+[o,o,o]+"\\)$"),f=new RegExp("^rgba\\("+[i,i,i,a]+"\\)$"),h=new RegExp("^rgba\\("+[o,o,o,a]+"\\)$"),p=new RegExp("^hsl\\("+[a,o,o]+"\\)$"),d=new RegExp("^hsla\\("+[a,o,o,a]+"\\)$"),g={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function m(t){var e;return t=(t+"").trim().toLowerCase(),(e=s.exec(t))?new _((e=parseInt(e[1],16))>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):(e=l.exec(t))?v(parseInt(e[1],16)):(e=c.exec(t))?new _(e[1],e[2],e[3],1):(e=u.exec(t))?new _(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=f.exec(t))?y(e[1],e[2],e[3],e[4]):(e=h.exec(t))?y(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=p.exec(t))?w(e[1],e[2]/100,e[3]/100,1):(e=d.exec(t))?w(e[1],e[2]/100,e[3]/100,e[4]):g.hasOwnProperty(t)?v(g[t]):"transparent"===t?new _(NaN,NaN,NaN,0):null}function v(t){return new _(t>>16&255,t>>8&255,255&t,1)}function y(t,e,r,n){return n<=0&&(t=e=r=NaN),new _(t,e,r,n)}function x(t){return t instanceof n||(t=m(t)),t?new _((t=t.rgb()).r,t.g,t.b,t.opacity):new _}function b(t,e,r,n){return 1===arguments.length?x(t):new _(t,e,r,null==n?1:n)}function _(t,e,r,n){this.r=+t,this.g=+e,this.b=+r,this.opacity=+n}function w(t,e,r,n){return n<=0?t=e=r=NaN:r<=0||r>=1?t=e=NaN:e<=0&&(t=NaN),new M(t,e,r,n)}function k(t,e,r,i){return 1===arguments.length?function(t){if(t instanceof M)return new M(t.h,t.s,t.l,t.opacity);if(t instanceof n||(t=m(t)),!t)return new M;if(t instanceof M)return t;var e=(t=t.rgb()).r/255,r=t.g/255,i=t.b/255,a=Math.min(e,r,i),o=Math.max(e,r,i),s=NaN,l=o-a,c=(o+a)/2;return l?(s=e===o?(r-i)/l+6*(r0&&c<1?0:s,new M(s,l,c,t.opacity)}(t):new M(t,e,r,null==i?1:i)}function M(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}function A(t,e,r){return 255*(t<60?e+(r-e)*t/60:t<180?r:t<240?e+(r-e)*(240-t)/60:e)}e(n,m,{displayable:function(){return this.rgb().displayable()},toString:function(){return this.rgb()+""}}),e(_,b,r(n,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new _(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new _(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return 0<=this.r&&this.r<=255&&0<=this.g&&this.g<=255&&0<=this.b&&this.b<=255&&0<=this.opacity&&this.opacity<=1},toString:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}})),e(M,k,r(n,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new M(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new M(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*e,i=2*r-n;return new _(A(t>=240?t-240:t+120,i,n),A(t,i,n),A(t<120?t+240:t-120,i,n),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var T=Math.PI/180,S=180/Math.PI,C=.95047,E=1,L=1.08883,z=4/29,P=6/29,D=3*P*P,O=P*P*P;function I(t){if(t instanceof B)return new B(t.l,t.a,t.b,t.opacity);if(t instanceof q){var e=t.h*T;return new B(t.l,Math.cos(e)*t.c,Math.sin(e)*t.c,t.opacity)}t instanceof _||(t=x(t));var r=V(t.r),n=V(t.g),i=V(t.b),a=F((.4124564*r+.3575761*n+.1804375*i)/C),o=F((.2126729*r+.7151522*n+.072175*i)/E);return new B(116*o-16,500*(a-o),200*(o-F((.0193339*r+.119192*n+.9503041*i)/L)),t.opacity)}function R(t,e,r,n){return 1===arguments.length?I(t):new B(t,e,r,null==n?1:n)}function B(t,e,r,n){this.l=+t,this.a=+e,this.b=+r,this.opacity=+n}function F(t){return t>O?Math.pow(t,1/3):t/D+z}function N(t){return t>P?t*t*t:D*(t-z)}function j(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function V(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function U(t,e,r,n){return 1===arguments.length?function(t){if(t instanceof q)return new q(t.h,t.c,t.l,t.opacity);t instanceof B||(t=I(t));var e=Math.atan2(t.b,t.a)*S;return new q(e<0?e+360:e,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}(t):new q(t,e,r,null==n?1:n)}function q(t,e,r,n){this.h=+t,this.c=+e,this.l=+r,this.opacity=+n}e(B,R,r(n,{brighter:function(t){return new B(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new B(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,r=isNaN(this.b)?t:t-this.b/200;return t=E*N(t),new _(j(3.2404542*(e=C*N(e))-1.5371385*t-.4985314*(r=L*N(r))),j(-.969266*e+1.8760108*t+.041556*r),j(.0556434*e-.2040259*t+1.0572252*r),this.opacity)}})),e(q,U,r(n,{brighter:function(t){return new q(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new q(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return I(this).rgb()}}));var H=-.14861,G=1.78277,W=-.29227,Y=-.90649,X=1.97294,Z=X*Y,J=X*G,K=G*W-Y*H;function Q(t,e,r,n){return 1===arguments.length?function(t){if(t instanceof $)return new $(t.h,t.s,t.l,t.opacity);t instanceof _||(t=x(t));var e=t.r/255,r=t.g/255,n=t.b/255,i=(K*n+Z*e-J*r)/(K+Z-J),a=n-i,o=(X*(r-i)-W*a)/Y,s=Math.sqrt(o*o+a*a)/(X*i*(1-i)),l=s?Math.atan2(o,a)*S-120:NaN;return new $(l<0?l+360:l,s,i,t.opacity)}(t):new $(t,e,r,null==n?1:n)}function $(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}e($,Q,r(n,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new $(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new $(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*T,e=+this.l,r=isNaN(this.s)?0:this.s*e*(1-e),n=Math.cos(t),i=Math.sin(t);return new _(255*(e+r*(H*n+G*i)),255*(e+r*(W*n+Y*i)),255*(e+r*(X*n)),this.opacity)}})),t.color=m,t.rgb=b,t.hsl=k,t.lab=R,t.hcl=U,t.cubehelix=Q,Object.defineProperty(t,"__esModule",{value:!0})}("object"==typeof r&&void 0!==e?r:n.d3=n.d3||{})},{}],126:[function(t,e,r){var n;n=this,function(t){"use strict";var e={value:function(){}};function r(){for(var t,e=0,r=arguments.length,i={};e=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}})),l=-1,c=s.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++l0)for(var r,n,i=new Array(r),a=0;ah+c||np+c||au.index){var f=h-s.x-s.vx,m=p-s.y-s.vy,v=f*f+m*m;vt.r&&(t.r=t[e].r)}function h(){if(r){var e,i,a=r.length;for(n=new Array(a),e=0;e=c)){(t.data!==r||t.next)&&(0===f&&(d+=(f=o())*f),0===h&&(d+=(h=o())*h),d1?(null==r?u.remove(t):u.set(t,y(r)),e):u.get(t)},find:function(e,r,n){var i,a,o,s,l,c=0,u=t.length;for(null==n?n=1/0:n*=n,c=0;c1?(h.on(t,r),e):h.on(t)}}},t.forceX=function(t){var e,r,n,i=a(.1);function o(t){for(var i,a=0,o=e.length;a=1?(n=1,e-1):Math.floor(n*e),a=t[i],o=t[i+1],s=i>0?t[i-1]:2*a-o,l=i180||r<-180?r-360*Math.round(r/360):r):a(isNaN(t)?e:t)}function l(t){return 1==(t=+t)?c:function(e,r){return r-e?function(t,e,r){return t=Math.pow(t,r),e=Math.pow(e,r)-t,r=1/r,function(n){return Math.pow(t+n*e,r)}}(e,r,t):a(isNaN(e)?r:e)}}function c(t,e){var r=e-t;return r?o(t,r):a(isNaN(t)?e:t)}var u=function t(r){var n=l(r);function i(t,r){var i=n((t=e.rgb(t)).r,(r=e.rgb(r)).r),a=n(t.g,r.g),o=n(t.b,r.b),s=c(t.opacity,r.opacity);return function(e){return t.r=i(e),t.g=a(e),t.b=o(e),t.opacity=s(e),t+""}}return i.gamma=t,i}(1);function f(t){return function(r){var n,i,a=r.length,o=new Array(a),s=new Array(a),l=new Array(a);for(n=0;na&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(r=r[0])===(n=n[0])?s[o]?s[o]+=n:s[++o]=n:(s[++o]=null,l.push({i:o,x:m(r,n)})),a=x.lastIndex;return a180?e+=360:e-t>180&&(t+=360),a.push({i:r.push(i(r)+"rotate(",null,n)-2,x:m(t,e)})):e&&r.push(i(r)+"rotate("+e+n)}(a.rotate,o.rotate,s,l),function(t,e,r,a){t!==e?a.push({i:r.push(i(r)+"skewX(",null,n)-2,x:m(t,e)}):e&&r.push(i(r)+"skewX("+e+n)}(a.skewX,o.skewX,s,l),function(t,e,r,n,a,o){if(t!==r||e!==n){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:m(t,r)},{i:s-2,x:m(e,n)})}else 1===r&&1===n||a.push(i(a)+"scale("+r+","+n+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,l),a=o=null,function(t){for(var e,r=-1,n=l.length;++r=(a=(g+v)/2))?g=a:v=a,(u=r>=(o=(m+y)/2))?m=o:y=o,i=p,!(p=p[f=u<<1|c]))return i[f]=d,t;if(s=+t._x.call(null,p.data),l=+t._y.call(null,p.data),e===s&&r===l)return d.next=p,i?i[f]=d:t._root=d,t;do{i=i?i[f]=new Array(4):t._root=new Array(4),(c=e>=(a=(g+v)/2))?g=a:v=a,(u=r>=(o=(m+y)/2))?m=o:y=o}while((f=u<<1|c)==(h=(l>=o)<<1|s>=a));return i[h]=p,i[f]=d,t}var r=function(t,e,r,n,i){this.node=t,this.x0=e,this.y0=r,this.x1=n,this.y1=i};function n(t){return t[0]}function i(t){return t[1]}function a(t,e,r){var a=new o(null==e?n:e,null==r?i:r,NaN,NaN,NaN,NaN);return null==t?a:a.addAll(t)}function o(t,e,r,n,i,a){this._x=t,this._y=e,this._x0=r,this._y0=n,this._x1=i,this._y1=a,this._root=void 0}function s(t){for(var e={data:t.data},r=e;t=t.next;)r=r.next={data:t.data};return e}var l=a.prototype=o.prototype;l.copy=function(){var t,e,r=new o(this._x,this._y,this._x0,this._y0,this._x1,this._y1),n=this._root;if(!n)return r;if(!n.length)return r._root=s(n),r;for(t=[{source:n,target:r._root=new Array(4)}];n=t.pop();)for(var i=0;i<4;++i)(e=n.source[i])&&(e.length?t.push({source:e,target:n.target[i]=new Array(4)}):n.target[i]=s(e));return r},l.add=function(t){var r=+this._x.call(null,t),n=+this._y.call(null,t);return e(this.cover(r,n),r,n,t)},l.addAll=function(t){var r,n,i,a,o=t.length,s=new Array(o),l=new Array(o),c=1/0,u=1/0,f=-1/0,h=-1/0;for(n=0;nf&&(f=i),ah&&(h=a));for(ft||t>i||n>e||e>a))return this;var o,s,l=i-r,c=this._root;switch(s=(e<(n+a)/2)<<1|t<(r+i)/2){case 0:do{(o=new Array(4))[s]=c,c=o}while(a=n+(l*=2),t>(i=r+l)||e>a);break;case 1:do{(o=new Array(4))[s]=c,c=o}while(a=n+(l*=2),(r=i-l)>t||e>a);break;case 2:do{(o=new Array(4))[s]=c,c=o}while(n=a-(l*=2),t>(i=r+l)||n>e);break;case 3:do{(o=new Array(4))[s]=c,c=o}while(n=a-(l*=2),(r=i-l)>t||n>e)}this._root&&this._root.length&&(this._root=c)}return this._x0=r,this._y0=n,this._x1=i,this._y1=a,this},l.data=function(){var t=[];return this.visit(function(e){if(!e.length)do{t.push(e.data)}while(e=e.next)}),t},l.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},l.find=function(t,e,n){var i,a,o,s,l,c,u,f=this._x0,h=this._y0,p=this._x1,d=this._y1,g=[],m=this._root;for(m&&g.push(new r(m,f,h,p,d)),null==n?n=1/0:(f=t-n,h=e-n,p=t+n,d=e+n,n*=n);c=g.pop();)if(!(!(m=c.node)||(a=c.x0)>p||(o=c.y0)>d||(s=c.x1)=y)<<1|t>=v)&&(c=g[g.length-1],g[g.length-1]=g[g.length-1-u],g[g.length-1-u]=c)}else{var x=t-+this._x.call(null,m.data),b=e-+this._y.call(null,m.data),_=x*x+b*b;if(_=(s=(d+m)/2))?d=s:m=s,(u=o>=(l=(g+v)/2))?g=l:v=l,e=p,!(p=p[f=u<<1|c]))return this;if(!p.length)break;(e[f+1&3]||e[f+2&3]||e[f+3&3])&&(r=e,h=f)}for(;p.data!==t;)if(n=p,!(p=p.next))return this;return(i=p.next)&&delete p.next,n?(i?n.next=i:delete n.next,this):e?(i?e[f]=i:delete e[f],(p=e[0]||e[1]||e[2]||e[3])&&p===(e[3]||e[2]||e[1]||e[0])&&!p.length&&(r?r[h]=p:this._root=p),this):(this._root=i,this)},l.removeAll=function(t){for(var e=0,r=t.length;e=0&&r._call.call(null,t),r=r._next;--n}function v(){l=(s=u.now())+c,n=i=0;try{m()}finally{n=0,function(){var t,n,i=e,a=1/0;for(;i;)i._call?(a>i._time&&(a=i._time),t=i,i=i._next):(n=i._next,i._next=null,i=t?t._next=n:e=n);r=t,x(a)}(),l=0}}function y(){var t=u.now(),e=t-s;e>o&&(c-=e,s=t)}function x(t){n||(i&&(i=clearTimeout(i)),t-l>24?(t<1/0&&(i=setTimeout(v,t-u.now()-c)),a&&(a=clearInterval(a))):(a||(s=u.now(),a=setInterval(y,o)),n=1,f(v)))}d.prototype=g.prototype={constructor:d,restart:function(t,n,i){if("function"!=typeof t)throw new TypeError("callback is not a function");i=(null==i?h():+i)+(null==n?0:+n),this._next||r===this||(r?r._next=this:e=this,r=this),this._call=t,this._time=i,x()},stop:function(){this._call&&(this._call=null,this._time=1/0,x())}};t.now=h,t.timer=g,t.timerFlush=m,t.timeout=function(t,e,r){var n=new d;return e=null==e?0:+e,n.restart(function(r){n.stop(),t(r+e)},e,r),n},t.interval=function(t,e,r){var n=new d,i=e;return null==e?(n.restart(t,e,r),n):(e=+e,r=null==r?h():+r,n.restart(function a(o){o+=i,n.restart(a,i+=e,r),t(o)},e,r),n)},Object.defineProperty(t,"__esModule",{value:!0})}("object"==typeof r&&void 0!==e?r:n.d3=n.d3||{})},{}],131:[function(t,e,r){!function(){var t={version:"3.5.17"},r=[].slice,n=function(t){return r.call(t)},i=this.document;function a(t){return t&&(t.ownerDocument||t.document||t).documentElement}function o(t){return t&&(t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView)}if(i)try{n(i.documentElement.childNodes)[0].nodeType}catch(t){n=function(t){for(var e=t.length,r=new Array(e);e--;)r[e]=t[e];return r}}if(Date.now||(Date.now=function(){return+new Date}),i)try{i.createElement("DIV").style.setProperty("opacity",0,"")}catch(t){var s=this.Element.prototype,l=s.setAttribute,c=s.setAttributeNS,u=this.CSSStyleDeclaration.prototype,f=u.setProperty;s.setAttribute=function(t,e){l.call(this,t,e+"")},s.setAttributeNS=function(t,e,r){c.call(this,t,e,r+"")},u.setProperty=function(t,e,r){f.call(this,t,e+"",r)}}function h(t,e){return te?1:t>=e?0:NaN}function p(t){return null===t?NaN:+t}function d(t){return!isNaN(t)}function g(t){return{left:function(e,r,n,i){for(arguments.length<3&&(n=0),arguments.length<4&&(i=e.length);n>>1;t(e[a],r)<0?n=a+1:i=a}return n},right:function(e,r,n,i){for(arguments.length<3&&(n=0),arguments.length<4&&(i=e.length);n>>1;t(e[a],r)>0?i=a:n=a+1}return n}}}t.ascending=h,t.descending=function(t,e){return et?1:e>=t?0:NaN},t.min=function(t,e){var r,n,i=-1,a=t.length;if(1===arguments.length){for(;++i=n){r=n;break}for(;++in&&(r=n)}else{for(;++i=n){r=n;break}for(;++in&&(r=n)}return r},t.max=function(t,e){var r,n,i=-1,a=t.length;if(1===arguments.length){for(;++i=n){r=n;break}for(;++ir&&(r=n)}else{for(;++i=n){r=n;break}for(;++ir&&(r=n)}return r},t.extent=function(t,e){var r,n,i,a=-1,o=t.length;if(1===arguments.length){for(;++a=n){r=i=n;break}for(;++an&&(r=n),i=n){r=i=n;break}for(;++an&&(r=n),i1)return o/(l-1)},t.deviation=function(){var e=t.variance.apply(this,arguments);return e?Math.sqrt(e):e};var m=g(h);function v(t){return t.length}t.bisectLeft=m.left,t.bisect=t.bisectRight=m.right,t.bisector=function(t){return g(1===t.length?function(e,r){return h(t(e),r)}:t)},t.shuffle=function(t,e,r){(a=arguments.length)<3&&(r=t.length,a<2&&(e=0));for(var n,i,a=r-e;a;)i=Math.random()*a--|0,n=t[a+e],t[a+e]=t[i+e],t[i+e]=n;return t},t.permute=function(t,e){for(var r=e.length,n=new Array(r);r--;)n[r]=t[e[r]];return n},t.pairs=function(t){for(var e=0,r=t.length-1,n=t[0],i=new Array(r<0?0:r);e=0;)for(e=(n=t[i]).length;--e>=0;)r[--o]=n[e];return r};var y=Math.abs;function x(t,e){for(var r in e)Object.defineProperty(t.prototype,r,{value:e[r],enumerable:!1})}function b(){this._=Object.create(null)}t.range=function(t,e,r){if(arguments.length<3&&(r=1,arguments.length<2&&(e=t,t=0)),(e-t)/r==1/0)throw new Error("infinite range");var n,i=[],a=function(t){var e=1;for(;t*e%1;)e*=10;return e}(y(r)),o=-1;if(t*=a,e*=a,(r*=a)<0)for(;(n=t+r*++o)>e;)i.push(n/a);else for(;(n=t+r*++o)=i.length)return r?r.call(n,a):e?a.sort(e):a;for(var l,c,u,f,h=-1,p=a.length,d=i[s++],g=new b;++h=i.length)return e;var n=[],o=a[r++];return e.forEach(function(e,i){n.push({key:e,values:t(i,r)})}),o?n.sort(function(t,e){return o(t.key,e.key)}):n}(o(t.map,e,0),0)},n.key=function(t){return i.push(t),n},n.sortKeys=function(t){return a[i.length-1]=t,n},n.sortValues=function(t){return e=t,n},n.rollup=function(t){return r=t,n},n},t.set=function(t){var e=new L;if(t)for(var r=0,n=t.length;r=0&&(n=t.slice(r+1),t=t.slice(0,r)),t)return arguments.length<2?this[t].on(n):this[t].on(n,e);if(2===arguments.length){if(null==e)for(t in this)this.hasOwnProperty(t)&&this[t].on(n,null);return this}},t.event=null,t.requote=function(t){return t.replace(V,"\\$&")};var V=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,U={}.__proto__?function(t,e){t.__proto__=e}:function(t,e){for(var r in e)t[r]=e[r]};function q(t){return U(t,Y),t}var H=function(t,e){return e.querySelector(t)},G=function(t,e){return e.querySelectorAll(t)},W=function(t,e){var r=t.matches||t[D(t,"matchesSelector")];return(W=function(t,e){return r.call(t,e)})(t,e)};"function"==typeof Sizzle&&(H=function(t,e){return Sizzle(t,e)[0]||null},G=Sizzle,W=Sizzle.matchesSelector),t.selection=function(){return t.select(i.documentElement)};var Y=t.selection.prototype=[];function X(t){return"function"==typeof t?t:function(){return H(t,this)}}function Z(t){return"function"==typeof t?t:function(){return G(t,this)}}Y.select=function(t){var e,r,n,i,a=[];t=X(t);for(var o=-1,s=this.length;++o=0&&"xmlns"!==(r=t.slice(0,e))&&(t=t.slice(e+1)),K.hasOwnProperty(r)?{space:K[r],local:t}:t}},Y.attr=function(e,r){if(arguments.length<2){if("string"==typeof e){var n=this.node();return(e=t.ns.qualify(e)).local?n.getAttributeNS(e.space,e.local):n.getAttribute(e)}for(r in e)this.each(Q(r,e[r]));return this}return this.each(Q(e,r))},Y.classed=function(t,e){if(arguments.length<2){if("string"==typeof t){var r=this.node(),n=(t=et(t)).length,i=-1;if(e=r.classList){for(;++i=0;)(r=n[i])&&(a&&a!==r.nextSibling&&a.parentNode.insertBefore(r,a),a=r);return this},Y.sort=function(t){t=function(t){arguments.length||(t=h);return function(e,r){return e&&r?t(e.__data__,r.__data__):!e-!r}}.apply(this,arguments);for(var e=-1,r=this.length;++e0&&(e=e.slice(0,o));var l=dt.get(e);function c(){var t=this[a];t&&(this.removeEventListener(e,t,t.$),delete this[a])}return l&&(e=l,s=mt),o?r?function(){var t=s(r,n(arguments));c.call(this),this.addEventListener(e,this[a]=t,t.$=i),t._=r}:c:r?I:function(){var r,n=new RegExp("^__on([^.]+)"+t.requote(e)+"$");for(var i in this)if(r=i.match(n)){var a=this[i];this.removeEventListener(r[1],a,a.$),delete this[i]}}}t.selection.enter=ft,t.selection.enter.prototype=ht,ht.append=Y.append,ht.empty=Y.empty,ht.node=Y.node,ht.call=Y.call,ht.size=Y.size,ht.select=function(t){for(var e,r,n,i,a,o=[],s=-1,l=this.length;++s=n&&(n=e+1);!(o=s[n])&&++n0?1:t<0?-1:0}function Pt(t,e,r){return(e[0]-t[0])*(r[1]-t[1])-(e[1]-t[1])*(r[0]-t[0])}function Dt(t){return t>1?0:t<-1?At:Math.acos(t)}function Ot(t){return t>1?Ct:t<-1?-Ct:Math.asin(t)}function It(t){return((t=Math.exp(t))+1/t)/2}function Rt(t){return(t=Math.sin(t/2))*t}var Bt=Math.SQRT2;t.interpolateZoom=function(t,e){var r,n,i=t[0],a=t[1],o=t[2],s=e[0],l=e[1],c=e[2],u=s-i,f=l-a,h=u*u+f*f;if(h0&&(e=e.transition().duration(g)),e.call(w.event)}function S(){c&&c.domain(l.range().map(function(t){return(t-h.x)/h.k}).map(l.invert)),f&&f.domain(u.range().map(function(t){return(t-h.y)/h.k}).map(u.invert))}function C(t){m++||t({type:"zoomstart"})}function E(t){S(),t({type:"zoom",scale:h.k,translate:[h.x,h.y]})}function L(t){--m||(t({type:"zoomend"}),r=null)}function z(){var e=this,r=_.of(e,arguments),n=0,i=t.select(o(e)).on(y,function(){n=1,A(t.mouse(e),a),E(r)}).on(x,function(){i.on(y,null).on(x,null),s(n),L(r)}),a=k(t.mouse(e)),s=xt(e);fs.call(e),C(r)}function P(){var e,r=this,n=_.of(r,arguments),i={},a=0,o=".zoom-"+t.event.changedTouches[0].identifier,l="touchmove"+o,c="touchend"+o,u=[],f=t.select(r),p=xt(r);function d(){var n=t.touches(r);return e=h.k,n.forEach(function(t){t.identifier in i&&(i[t.identifier]=k(t))}),n}function g(){var e=t.event.target;t.select(e).on(l,m).on(c,y),u.push(e);for(var n=t.event.changedTouches,o=0,f=n.length;o1){v=p[0];var x=p[1],b=v[0]-x[0],_=v[1]-x[1];a=b*b+_*_}}function m(){var o,l,c,u,f=t.touches(r);fs.call(r);for(var h=0,p=f.length;h360?t-=360:t<0&&(t+=360),t<60?n+(i-n)*t/60:t<180?i:t<240?n+(i-n)*(240-t)/60:n}(t))}return t=isNaN(t)?0:(t%=360)<0?t+360:t,e=isNaN(e)?0:e<0?0:e>1?1:e,n=2*(r=r<0?0:r>1?1:r)-(i=r<=.5?r*(1+e):r+e-r*e),new ae(a(t+120),a(t),a(t-120))}function Gt(e,r,n){return this instanceof Gt?(this.h=+e,this.c=+r,void(this.l=+n)):arguments.length<2?e instanceof Gt?new Gt(e.h,e.c,e.l):ee(e instanceof Xt?e.l:(e=he((e=t.rgb(e)).r,e.g,e.b)).l,e.a,e.b):new Gt(e,r,n)}qt.brighter=function(t){return t=Math.pow(.7,arguments.length?t:1),new Ut(this.h,this.s,this.l/t)},qt.darker=function(t){return t=Math.pow(.7,arguments.length?t:1),new Ut(this.h,this.s,t*this.l)},qt.rgb=function(){return Ht(this.h,this.s,this.l)},t.hcl=Gt;var Wt=Gt.prototype=new Vt;function Yt(t,e,r){return isNaN(t)&&(t=0),isNaN(e)&&(e=0),new Xt(r,Math.cos(t*=Et)*e,Math.sin(t)*e)}function Xt(t,e,r){return this instanceof Xt?(this.l=+t,this.a=+e,void(this.b=+r)):arguments.length<2?t instanceof Xt?new Xt(t.l,t.a,t.b):t instanceof Gt?Yt(t.h,t.c,t.l):he((t=ae(t)).r,t.g,t.b):new Xt(t,e,r)}Wt.brighter=function(t){return new Gt(this.h,this.c,Math.min(100,this.l+Zt*(arguments.length?t:1)))},Wt.darker=function(t){return new Gt(this.h,this.c,Math.max(0,this.l-Zt*(arguments.length?t:1)))},Wt.rgb=function(){return Yt(this.h,this.c,this.l).rgb()},t.lab=Xt;var Zt=18,Jt=.95047,Kt=1,Qt=1.08883,$t=Xt.prototype=new Vt;function te(t,e,r){var n=(t+16)/116,i=n+e/500,a=n-r/200;return new ae(ie(3.2404542*(i=re(i)*Jt)-1.5371385*(n=re(n)*Kt)-.4985314*(a=re(a)*Qt)),ie(-.969266*i+1.8760108*n+.041556*a),ie(.0556434*i-.2040259*n+1.0572252*a))}function ee(t,e,r){return t>0?new Gt(Math.atan2(r,e)*Lt,Math.sqrt(e*e+r*r),t):new Gt(NaN,NaN,t)}function re(t){return t>.206893034?t*t*t:(t-4/29)/7.787037}function ne(t){return t>.008856?Math.pow(t,1/3):7.787037*t+4/29}function ie(t){return Math.round(255*(t<=.00304?12.92*t:1.055*Math.pow(t,1/2.4)-.055))}function ae(t,e,r){return this instanceof ae?(this.r=~~t,this.g=~~e,void(this.b=~~r)):arguments.length<2?t instanceof ae?new ae(t.r,t.g,t.b):ue(""+t,ae,Ht):new ae(t,e,r)}function oe(t){return new ae(t>>16,t>>8&255,255&t)}function se(t){return oe(t)+""}$t.brighter=function(t){return new Xt(Math.min(100,this.l+Zt*(arguments.length?t:1)),this.a,this.b)},$t.darker=function(t){return new Xt(Math.max(0,this.l-Zt*(arguments.length?t:1)),this.a,this.b)},$t.rgb=function(){return te(this.l,this.a,this.b)},t.rgb=ae;var le=ae.prototype=new Vt;function ce(t){return t<16?"0"+Math.max(0,t).toString(16):Math.min(255,t).toString(16)}function ue(t,e,r){var n,i,a,o=0,s=0,l=0;if(n=/([a-z]+)\((.*)\)/.exec(t=t.toLowerCase()))switch(i=n[2].split(","),n[1]){case"hsl":return r(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return e(de(i[0]),de(i[1]),de(i[2]))}return(a=ge.get(t))?e(a.r,a.g,a.b):(null==t||"#"!==t.charAt(0)||isNaN(a=parseInt(t.slice(1),16))||(4===t.length?(o=(3840&a)>>4,o|=o>>4,s=240&a,s|=s>>4,l=15&a,l|=l<<4):7===t.length&&(o=(16711680&a)>>16,s=(65280&a)>>8,l=255&a)),e(o,s,l))}function fe(t,e,r){var n,i,a=Math.min(t/=255,e/=255,r/=255),o=Math.max(t,e,r),s=o-a,l=(o+a)/2;return s?(i=l<.5?s/(o+a):s/(2-o-a),n=t==o?(e-r)/s+(e0&&l<1?0:n),new Ut(n,i,l)}function he(t,e,r){var n=ne((.4124564*(t=pe(t))+.3575761*(e=pe(e))+.1804375*(r=pe(r)))/Jt),i=ne((.2126729*t+.7151522*e+.072175*r)/Kt);return Xt(116*i-16,500*(n-i),200*(i-ne((.0193339*t+.119192*e+.9503041*r)/Qt)))}function pe(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function de(t){var e=parseFloat(t);return"%"===t.charAt(t.length-1)?Math.round(2.55*e):e}le.brighter=function(t){t=Math.pow(.7,arguments.length?t:1);var e=this.r,r=this.g,n=this.b,i=30;return e||r||n?(e&&e=200&&e<300||304===e){try{t=i.call(o,c)}catch(t){return void s.error.call(o,t)}s.load.call(o,t)}else s.error.call(o,c)}return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(e)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=f:c.onreadystatechange=function(){c.readyState>3&&f()},c.onprogress=function(e){var r=t.event;t.event=e;try{s.progress.call(o,c)}finally{t.event=r}},o.header=function(t,e){return t=(t+"").toLowerCase(),arguments.length<2?l[t]:(null==e?delete l[t]:l[t]=e+"",o)},o.mimeType=function(t){return arguments.length?(r=null==t?null:t+"",o):r},o.responseType=function(t){return arguments.length?(u=t,o):u},o.response=function(t){return i=t,o},["get","post"].forEach(function(t){o[t]=function(){return o.send.apply(o,[t].concat(n(arguments)))}}),o.send=function(t,n,i){if(2===arguments.length&&"function"==typeof n&&(i=n,n=null),c.open(t,e,!0),null==r||"accept"in l||(l.accept=r+",*/*"),c.setRequestHeader)for(var a in l)c.setRequestHeader(a,l[a]);return null!=r&&c.overrideMimeType&&c.overrideMimeType(r),null!=u&&(c.responseType=u),null!=i&&o.on("error",i).on("load",function(t){i(null,t)}),s.beforesend.call(o,c),c.send(null==n?null:n),o},o.abort=function(){return c.abort(),o},t.rebind(o,s,"on"),null==a?o:o.get(function(t){return 1===t.length?function(e,r){t(null==e?r:null)}:t}(a))}ge.forEach(function(t,e){ge.set(t,oe(e))}),t.functor=me,t.xhr=ve(z),t.dsv=function(t,e){var r=new RegExp('["'+t+"\n]"),n=t.charCodeAt(0);function i(t,r,n){arguments.length<3&&(n=r,r=null);var i=ye(t,e,null==r?a:o(r),n);return i.row=function(t){return arguments.length?i.response(null==(r=t)?a:o(t)):r},i}function a(t){return i.parse(t.responseText)}function o(t){return function(e){return i.parse(e.responseText,t)}}function s(e){return e.map(l).join(t)}function l(t){return r.test(t)?'"'+t.replace(/\"/g,'""')+'"':t}return i.parse=function(t,e){var r;return i.parseRows(t,function(t,n){if(r)return r(t,n-1);var i=new Function("d","return {"+t.map(function(t,e){return JSON.stringify(t)+": d["+e+"]"}).join(",")+"}");r=e?function(t,r){return e(i(t),r)}:i})},i.parseRows=function(t,e){var r,i,a={},o={},s=[],l=t.length,c=0,u=0;function f(){if(c>=l)return o;if(i)return i=!1,a;var e=c;if(34===t.charCodeAt(e)){for(var r=e;r++24?(isFinite(e)&&(clearTimeout(we),we=setTimeout(Ae,e)),_e=0):(_e=1,ke(Ae))}function Te(){for(var t=Date.now(),e=xe;e;)t>=e.t&&e.c(t-e.t)&&(e.c=null),e=e.n;return t}function Se(){for(var t,e=xe,r=1/0;e;)e.c?(e.t8?function(t){return t/r}:function(t){return t*r},symbol:t}});t.formatPrefix=function(e,r){var n=0;return(e=+e)&&(e<0&&(e*=-1),r&&(e=t.round(e,Ce(e,r))),n=1+Math.floor(1e-12+Math.log(e)/Math.LN10),n=Math.max(-24,Math.min(24,3*Math.floor((n-1)/3)))),Ee[8+n/3]};var Le=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,ze=t.map({b:function(t){return t.toString(2)},c:function(t){return String.fromCharCode(t)},o:function(t){return t.toString(8)},x:function(t){return t.toString(16)},X:function(t){return t.toString(16).toUpperCase()},g:function(t,e){return t.toPrecision(e)},e:function(t,e){return t.toExponential(e)},f:function(t,e){return t.toFixed(e)},r:function(e,r){return(e=t.round(e,Ce(e,r))).toFixed(Math.max(0,Math.min(20,Ce(e*(1+1e-15),r))))}});function Pe(t){return t+""}var De=t.time={},Oe=Date;function Ie(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}Ie.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){Re.setUTCDate.apply(this._,arguments)},setDay:function(){Re.setUTCDay.apply(this._,arguments)},setFullYear:function(){Re.setUTCFullYear.apply(this._,arguments)},setHours:function(){Re.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){Re.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){Re.setUTCMinutes.apply(this._,arguments)},setMonth:function(){Re.setUTCMonth.apply(this._,arguments)},setSeconds:function(){Re.setUTCSeconds.apply(this._,arguments)},setTime:function(){Re.setTime.apply(this._,arguments)}};var Re=Date.prototype;function Be(t,e,r){function n(e){var r=t(e),n=a(r,1);return e-r1)for(;o68?1900:2e3),r+i[0].length):-1}function Je(t,e,r){return/^[+-]\d{4}$/.test(e=e.slice(r,r+5))?(t.Z=-e,r+5):-1}function Ke(t,e,r){je.lastIndex=0;var n=je.exec(e.slice(r,r+2));return n?(t.m=n[0]-1,r+n[0].length):-1}function Qe(t,e,r){je.lastIndex=0;var n=je.exec(e.slice(r,r+2));return n?(t.d=+n[0],r+n[0].length):-1}function $e(t,e,r){je.lastIndex=0;var n=je.exec(e.slice(r,r+3));return n?(t.j=+n[0],r+n[0].length):-1}function tr(t,e,r){je.lastIndex=0;var n=je.exec(e.slice(r,r+2));return n?(t.H=+n[0],r+n[0].length):-1}function er(t,e,r){je.lastIndex=0;var n=je.exec(e.slice(r,r+2));return n?(t.M=+n[0],r+n[0].length):-1}function rr(t,e,r){je.lastIndex=0;var n=je.exec(e.slice(r,r+2));return n?(t.S=+n[0],r+n[0].length):-1}function nr(t,e,r){je.lastIndex=0;var n=je.exec(e.slice(r,r+3));return n?(t.L=+n[0],r+n[0].length):-1}function ir(t){var e=t.getTimezoneOffset(),r=e>0?"-":"+",n=y(e)/60|0,i=y(e)%60;return r+Ue(n,"0",2)+Ue(i,"0",2)}function ar(t,e,r){Ve.lastIndex=0;var n=Ve.exec(e.slice(r,r+1));return n?r+n[0].length:-1}function or(t){for(var e=t.length,r=-1;++r0&&s>0&&(l+s+1>e&&(s=Math.max(1,e-l)),a.push(t.substring(r-=s,r+s)),!((l+=s+1)>e));)s=i[o=(o+1)%i.length];return a.reverse().join(n)}:z;return function(e){var n=Le.exec(e),i=n[1]||" ",s=n[2]||">",l=n[3]||"-",c=n[4]||"",u=n[5],f=+n[6],h=n[7],p=n[8],d=n[9],g=1,m="",v="",y=!1,x=!0;switch(p&&(p=+p.substring(1)),(u||"0"===i&&"="===s)&&(u=i="0",s="="),d){case"n":h=!0,d="g";break;case"%":g=100,v="%",d="f";break;case"p":g=100,v="%",d="r";break;case"b":case"o":case"x":case"X":"#"===c&&(m="0"+d.toLowerCase());case"c":x=!1;case"d":y=!0,p=0;break;case"s":g=-1,d="r"}"$"===c&&(m=a[0],v=a[1]),"r"!=d||p||(d="g"),null!=p&&("g"==d?p=Math.max(1,Math.min(21,p)):"e"!=d&&"f"!=d||(p=Math.max(0,Math.min(20,p)))),d=ze.get(d)||Pe;var b=u&&h;return function(e){var n=v;if(y&&e%1)return"";var a=e<0||0===e&&1/e<0?(e=-e,"-"):"-"===l?"":l;if(g<0){var c=t.formatPrefix(e,p);e=c.scale(e),n=c.symbol+v}else e*=g;var _,w,k=(e=d(e,p)).lastIndexOf(".");if(k<0){var M=x?e.lastIndexOf("e"):-1;M<0?(_=e,w=""):(_=e.substring(0,M),w=e.substring(M))}else _=e.substring(0,k),w=r+e.substring(k+1);!u&&h&&(_=o(_,1/0));var A=m.length+_.length+w.length+(b?0:a.length),T=A"===s?T+a+e:"^"===s?T.substring(0,A>>=1)+a+e+T.substring(A):a+(b?e:T+e))+n}}}(e),timeFormat:function(e){var r=e.dateTime,n=e.date,i=e.time,a=e.periods,o=e.days,s=e.shortDays,l=e.months,c=e.shortMonths;function u(t){var e=t.length;function r(r){for(var n,i,a,o=[],s=-1,l=0;++s=c)return-1;if(37===(i=e.charCodeAt(s++))){if(o=e.charAt(s++),!(a=w[o in Ne?e.charAt(s++):o])||(n=a(t,r,n))<0)return-1}else if(i!=r.charCodeAt(n++))return-1}return n}u.utc=function(t){var e=u(t);function r(t){try{var r=new(Oe=Ie);return r._=t,e(r)}finally{Oe=Date}}return r.parse=function(t){try{Oe=Ie;var r=e.parse(t);return r&&r._}finally{Oe=Date}},r.toString=e.toString,r},u.multi=u.utc.multi=or;var h=t.map(),p=qe(o),d=He(o),g=qe(s),m=He(s),v=qe(l),y=He(l),x=qe(c),b=He(c);a.forEach(function(t,e){h.set(t.toLowerCase(),e)});var _={a:function(t){return s[t.getDay()]},A:function(t){return o[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return l[t.getMonth()]},c:u(r),d:function(t,e){return Ue(t.getDate(),e,2)},e:function(t,e){return Ue(t.getDate(),e,2)},H:function(t,e){return Ue(t.getHours(),e,2)},I:function(t,e){return Ue(t.getHours()%12||12,e,2)},j:function(t,e){return Ue(1+De.dayOfYear(t),e,3)},L:function(t,e){return Ue(t.getMilliseconds(),e,3)},m:function(t,e){return Ue(t.getMonth()+1,e,2)},M:function(t,e){return Ue(t.getMinutes(),e,2)},p:function(t){return a[+(t.getHours()>=12)]},S:function(t,e){return Ue(t.getSeconds(),e,2)},U:function(t,e){return Ue(De.sundayOfYear(t),e,2)},w:function(t){return t.getDay()},W:function(t,e){return Ue(De.mondayOfYear(t),e,2)},x:u(n),X:u(i),y:function(t,e){return Ue(t.getFullYear()%100,e,2)},Y:function(t,e){return Ue(t.getFullYear()%1e4,e,4)},Z:ir,"%":function(){return"%"}},w={a:function(t,e,r){g.lastIndex=0;var n=g.exec(e.slice(r));return n?(t.w=m.get(n[0].toLowerCase()),r+n[0].length):-1},A:function(t,e,r){p.lastIndex=0;var n=p.exec(e.slice(r));return n?(t.w=d.get(n[0].toLowerCase()),r+n[0].length):-1},b:function(t,e,r){x.lastIndex=0;var n=x.exec(e.slice(r));return n?(t.m=b.get(n[0].toLowerCase()),r+n[0].length):-1},B:function(t,e,r){v.lastIndex=0;var n=v.exec(e.slice(r));return n?(t.m=y.get(n[0].toLowerCase()),r+n[0].length):-1},c:function(t,e,r){return f(t,_.c.toString(),e,r)},d:Qe,e:Qe,H:tr,I:tr,j:$e,L:nr,m:Ke,M:er,p:function(t,e,r){var n=h.get(e.slice(r,r+=2).toLowerCase());return null==n?-1:(t.p=n,r)},S:rr,U:We,w:Ge,W:Ye,x:function(t,e,r){return f(t,_.x.toString(),e,r)},X:function(t,e,r){return f(t,_.X.toString(),e,r)},y:Ze,Y:Xe,Z:Je,"%":ar};return u}(e)}};var sr=t.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function lr(){}t.format=sr.numberFormat,t.geo={},lr.prototype={s:0,t:0,add:function(t){ur(t,this.t,cr),ur(cr.s,this.s,this),this.s?this.t+=cr.t:this.s=cr.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cr=new lr;function ur(t,e,r){var n=r.s=t+e,i=n-t,a=n-i;r.t=t-a+(e-i)}function fr(t,e){t&&pr.hasOwnProperty(t.type)&&pr[t.type](t,e)}t.geo.stream=function(t,e){t&&hr.hasOwnProperty(t.type)?hr[t.type](t,e):fr(t,e)};var hr={Feature:function(t,e){fr(t.geometry,e)},FeatureCollection:function(t,e){for(var r=t.features,n=-1,i=r.length;++n=0?1:-1,s=o*a,l=Math.cos(e),c=Math.sin(e),u=i*c,f=n*l+u*Math.cos(s),h=u*o*Math.sin(s);Cr.add(Math.atan2(h,f)),r=t,n=l,i=c}Er.point=function(o,s){Er.point=a,r=(t=o)*Et,n=Math.cos(s=(e=s)*Et/2+At/4),i=Math.sin(s)},Er.lineEnd=function(){a(t,e)}}function zr(t){var e=t[0],r=t[1],n=Math.cos(r);return[n*Math.cos(e),n*Math.sin(e),Math.sin(r)]}function Pr(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function Dr(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function Or(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}function Ir(t,e){return[t[0]*e,t[1]*e,t[2]*e]}function Rr(t){var e=Math.sqrt(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t[2]/=e}function Br(t){return[Math.atan2(t[1],t[0]),Ot(t[2])]}function Fr(t,e){return y(t[0]-e[0])kt?i=90:c<-kt&&(r=-90),f[0]=e,f[1]=n}};function p(t,a){u.push(f=[e=t,n=t]),ai&&(i=a)}function d(t,o){var s=zr([t*Et,o*Et]);if(l){var c=Dr(l,s),u=Dr([c[1],-c[0],0],c);Rr(u),u=Br(u);var f=t-a,h=f>0?1:-1,d=u[0]*Lt*h,g=y(f)>180;if(g^(h*ai&&(i=m);else if(g^(h*a<(d=(d+360)%360-180)&&di&&(i=o);g?t_(e,n)&&(n=t):_(t,n)>_(e,n)&&(e=t):n>=e?(tn&&(n=t)):t>a?_(e,t)>_(e,n)&&(n=t):_(t,n)>_(e,n)&&(e=t)}else p(t,o);l=s,a=t}function g(){h.point=d}function m(){f[0]=e,f[1]=n,h.point=p,l=null}function v(t,e){if(l){var r=t-a;c+=y(r)>180?r+(r>0?360:-360):r}else o=t,s=e;Er.point(t,e),d(t,e)}function x(){Er.lineStart()}function b(){v(o,s),Er.lineEnd(),y(c)>kt&&(e=-(n=180)),f[0]=e,f[1]=n,l=null}function _(t,e){return(e-=t)<0?e+360:e}function w(t,e){return t[0]-e[0]}function k(t,e){return e[0]<=e[1]?e[0]<=t&&t<=e[1]:t_(g[0],g[1])&&(g[1]=p[1]),_(p[0],g[1])>_(g[0],g[1])&&(g[0]=p[0])):s.push(g=p);for(var l,c,p,d=-1/0,g=(o=0,s[c=s.length-1]);o<=c;g=p,++o)p=s[o],(l=_(g[1],p[0]))>d&&(d=l,e=p[0],n=g[1])}return u=f=null,e===1/0||r===1/0?[[NaN,NaN],[NaN,NaN]]:[[e,r],[n,i]]}}(),t.geo.centroid=function(e){vr=yr=xr=br=_r=wr=kr=Mr=Ar=Tr=Sr=0,t.geo.stream(e,Nr);var r=Ar,n=Tr,i=Sr,a=r*r+n*n+i*i;return a=0;--s)i.point((f=u[s])[0],f[1]);else n(p.x,p.p.x,-1,i);p=p.p}u=(p=p.o).z,d=!d}while(!p.v);i.lineEnd()}}}function Xr(t){if(e=t.length){for(var e,r,n=0,i=t[0];++n=0?1:-1,k=w*_,M=k>At,A=d*x;if(Cr.add(Math.atan2(A*w*Math.sin(k),g*b+A*Math.cos(k))),a+=M?_+w*Tt:_,M^h>=r^v>=r){var T=Dr(zr(f),zr(t));Rr(T);var S=Dr(i,T);Rr(S);var C=(M^_>=0?-1:1)*Ot(S[2]);(n>C||n===C&&(T[0]||T[1]))&&(o+=M^_>=0?1:-1)}if(!m++)break;h=v,d=x,g=b,f=t}}return(a<-kt||a0){for(x||(o.polygonStart(),x=!0),o.lineStart();++a1&&2&e&&r.push(r.pop().concat(r.shift())),s.push(r.filter(Kr))}return u}}function Kr(t){return t.length>1}function Qr(){var t,e=[];return{lineStart:function(){e.push(t=[])},point:function(e,r){t.push([e,r])},lineEnd:I,buffer:function(){var r=e;return e=[],t=null,r},rejoin:function(){e.length>1&&e.push(e.pop().concat(e.shift()))}}}function $r(t,e){return((t=t.x)[0]<0?t[1]-Ct-kt:Ct-t[1])-((e=e.x)[0]<0?e[1]-Ct-kt:Ct-e[1])}var tn=Jr(Wr,function(t){var e,r=NaN,n=NaN,i=NaN;return{lineStart:function(){t.lineStart(),e=1},point:function(a,o){var s=a>0?At:-At,l=y(a-r);y(l-At)0?Ct:-Ct),t.point(i,n),t.lineEnd(),t.lineStart(),t.point(s,n),t.point(a,n),e=0):i!==s&&l>=At&&(y(r-i)kt?Math.atan((Math.sin(e)*(a=Math.cos(n))*Math.sin(r)-Math.sin(n)*(i=Math.cos(e))*Math.sin(t))/(i*a*o)):(e+n)/2}(r,n,a,o),t.point(i,n),t.lineEnd(),t.lineStart(),t.point(s,n),e=0),t.point(r=a,n=o),i=s},lineEnd:function(){t.lineEnd(),r=n=NaN},clean:function(){return 2-e}}},function(t,e,r,n){var i;if(null==t)i=r*Ct,n.point(-At,i),n.point(0,i),n.point(At,i),n.point(At,0),n.point(At,-i),n.point(0,-i),n.point(-At,-i),n.point(-At,0),n.point(-At,i);else if(y(t[0]-e[0])>kt){var a=t[0]0)){if(a/=h,h<0){if(a0){if(a>f)return;a>u&&(u=a)}if(a=r-l,h||!(a<0)){if(a/=h,h<0){if(a>f)return;a>u&&(u=a)}else if(h>0){if(a0)){if(a/=p,p<0){if(a0){if(a>f)return;a>u&&(u=a)}if(a=n-c,p||!(a<0)){if(a/=p,p<0){if(a>f)return;a>u&&(u=a)}else if(p>0){if(a0&&(i.a={x:l+u*h,y:c+u*p}),f<1&&(i.b={x:l+f*h,y:c+f*p}),i}}}}}}var rn=1e9;function nn(e,r,n,i){return function(l){var c,u,f,h,p,d,g,m,v,y,x,b=l,_=Qr(),w=en(e,r,n,i),k={point:T,lineStart:function(){k.point=S,u&&u.push(f=[]);y=!0,v=!1,g=m=NaN},lineEnd:function(){c&&(S(h,p),d&&v&&_.rejoin(),c.push(_.buffer()));k.point=T,v&&l.lineEnd()},polygonStart:function(){l=_,c=[],u=[],x=!0},polygonEnd:function(){l=b,c=t.merge(c);var r=function(t){for(var e=0,r=u.length,n=t[1],i=0;in&&Pt(c,a,t)>0&&++e:a[1]<=n&&Pt(c,a,t)<0&&--e,c=a;return 0!==e}([e,i]),n=x&&r,a=c.length;(n||a)&&(l.polygonStart(),n&&(l.lineStart(),M(null,null,1,l),l.lineEnd()),a&&Yr(c,o,r,M,l),l.polygonEnd()),c=u=f=null}};function M(t,o,l,c){var u=0,f=0;if(null==t||(u=a(t,l))!==(f=a(o,l))||s(t,o)<0^l>0)do{c.point(0===u||3===u?e:n,u>1?i:r)}while((u=(u+l+4)%4)!==f);else c.point(o[0],o[1])}function A(t,a){return e<=t&&t<=n&&r<=a&&a<=i}function T(t,e){A(t,e)&&l.point(t,e)}function S(t,e){var r=A(t=Math.max(-rn,Math.min(rn,t)),e=Math.max(-rn,Math.min(rn,e)));if(u&&f.push([t,e]),y)h=t,p=e,d=r,y=!1,r&&(l.lineStart(),l.point(t,e));else if(r&&v)l.point(t,e);else{var n={a:{x:g,y:m},b:{x:t,y:e}};w(n)?(v||(l.lineStart(),l.point(n.a.x,n.a.y)),l.point(n.b.x,n.b.y),r||l.lineEnd(),x=!1):r&&(l.lineStart(),l.point(t,e),x=!1)}g=t,m=e,v=r}return k};function a(t,i){return y(t[0]-e)0?0:3:y(t[0]-n)0?2:1:y(t[1]-r)0?1:0:i>0?3:2}function o(t,e){return s(t.x,e.x)}function s(t,e){var r=a(t,1),n=a(e,1);return r!==n?r-n:0===r?e[1]-t[1]:1===r?t[0]-e[0]:2===r?t[1]-e[1]:e[0]-t[0]}}function an(t){var e=0,r=At/3,n=En(t),i=n(e,r);return i.parallels=function(t){return arguments.length?n(e=t[0]*At/180,r=t[1]*At/180):[e/At*180,r/At*180]},i}function on(t,e){var r=Math.sin(t),n=(r+Math.sin(e))/2,i=1+r*(2*n-r),a=Math.sqrt(i)/n;function o(t,e){var r=Math.sqrt(i-2*n*Math.sin(e))/n;return[r*Math.sin(t*=n),a-r*Math.cos(t)]}return o.invert=function(t,e){var r=a-e;return[Math.atan2(t,r)/n,Ot((i-(t*t+r*r)*n*n)/(2*n))]},o}t.geo.clipExtent=function(){var t,e,r,n,i,a,o={stream:function(t){return i&&(i.valid=!1),(i=a(t)).valid=!0,i},extent:function(s){return arguments.length?(a=nn(t=+s[0][0],e=+s[0][1],r=+s[1][0],n=+s[1][1]),i&&(i.valid=!1,i=null),o):[[t,e],[r,n]]}};return o.extent([[0,0],[960,500]])},(t.geo.conicEqualArea=function(){return an(on)}).raw=on,t.geo.albers=function(){return t.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},t.geo.albersUsa=function(){var e,r,n,i,a=t.geo.albers(),o=t.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),s=t.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(t,r){e=[t,r]}};function c(t){var a=t[0],o=t[1];return e=null,r(a,o),e||(n(a,o),e)||i(a,o),e}return c.invert=function(t){var e=a.scale(),r=a.translate(),n=(t[0]-r[0])/e,i=(t[1]-r[1])/e;return(i>=.12&&i<.234&&n>=-.425&&n<-.214?o:i>=.166&&i<.234&&n>=-.214&&n<-.115?s:a).invert(t)},c.stream=function(t){var e=a.stream(t),r=o.stream(t),n=s.stream(t);return{point:function(t,i){e.point(t,i),r.point(t,i),n.point(t,i)},sphere:function(){e.sphere(),r.sphere(),n.sphere()},lineStart:function(){e.lineStart(),r.lineStart(),n.lineStart()},lineEnd:function(){e.lineEnd(),r.lineEnd(),n.lineEnd()},polygonStart:function(){e.polygonStart(),r.polygonStart(),n.polygonStart()},polygonEnd:function(){e.polygonEnd(),r.polygonEnd(),n.polygonEnd()}}},c.precision=function(t){return arguments.length?(a.precision(t),o.precision(t),s.precision(t),c):a.precision()},c.scale=function(t){return arguments.length?(a.scale(t),o.scale(.35*t),s.scale(t),c.translate(a.translate())):a.scale()},c.translate=function(t){if(!arguments.length)return a.translate();var e=a.scale(),u=+t[0],f=+t[1];return r=a.translate(t).clipExtent([[u-.455*e,f-.238*e],[u+.455*e,f+.238*e]]).stream(l).point,n=o.translate([u-.307*e,f+.201*e]).clipExtent([[u-.425*e+kt,f+.12*e+kt],[u-.214*e-kt,f+.234*e-kt]]).stream(l).point,i=s.translate([u-.205*e,f+.212*e]).clipExtent([[u-.214*e+kt,f+.166*e+kt],[u-.115*e-kt,f+.234*e-kt]]).stream(l).point,c},c.scale(1070)};var sn,ln,cn,un,fn,hn,pn={point:I,lineStart:I,lineEnd:I,polygonStart:function(){ln=0,pn.lineStart=dn},polygonEnd:function(){pn.lineStart=pn.lineEnd=pn.point=I,sn+=y(ln/2)}};function dn(){var t,e,r,n;function i(t,e){ln+=n*t-r*e,r=t,n=e}pn.point=function(a,o){pn.point=i,t=r=a,e=n=o},pn.lineEnd=function(){i(t,e)}}var gn={point:function(t,e){tfn&&(fn=t);ehn&&(hn=e)},lineStart:I,lineEnd:I,polygonStart:I,polygonEnd:I};function mn(){var t=vn(4.5),e=[],r={point:n,lineStart:function(){r.point=i},lineEnd:o,polygonStart:function(){r.lineEnd=s},polygonEnd:function(){r.lineEnd=o,r.point=n},pointRadius:function(e){return t=vn(e),r},result:function(){if(e.length){var t=e.join("");return e=[],t}}};function n(r,n){e.push("M",r,",",n,t)}function i(t,n){e.push("M",t,",",n),r.point=a}function a(t,r){e.push("L",t,",",r)}function o(){r.point=n}function s(){e.push("Z")}return r}function vn(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}var yn,xn={point:bn,lineStart:_n,lineEnd:wn,polygonStart:function(){xn.lineStart=kn},polygonEnd:function(){xn.point=bn,xn.lineStart=_n,xn.lineEnd=wn}};function bn(t,e){xr+=t,br+=e,++_r}function _n(){var t,e;function r(r,n){var i=r-t,a=n-e,o=Math.sqrt(i*i+a*a);wr+=o*(t+r)/2,kr+=o*(e+n)/2,Mr+=o,bn(t=r,e=n)}xn.point=function(n,i){xn.point=r,bn(t=n,e=i)}}function wn(){xn.point=bn}function kn(){var t,e,r,n;function i(t,e){var i=t-r,a=e-n,o=Math.sqrt(i*i+a*a);wr+=o*(r+t)/2,kr+=o*(n+e)/2,Mr+=o,Ar+=(o=n*t-r*e)*(r+t),Tr+=o*(n+e),Sr+=3*o,bn(r=t,n=e)}xn.point=function(a,o){xn.point=i,bn(t=r=a,e=n=o)},xn.lineEnd=function(){i(t,e)}}function Mn(t){var e=4.5,r={point:n,lineStart:function(){r.point=i},lineEnd:o,polygonStart:function(){r.lineEnd=s},polygonEnd:function(){r.lineEnd=o,r.point=n},pointRadius:function(t){return e=t,r},result:I};function n(r,n){t.moveTo(r+e,n),t.arc(r,n,e,0,Tt)}function i(e,n){t.moveTo(e,n),r.point=a}function a(e,r){t.lineTo(e,r)}function o(){r.point=n}function s(){t.closePath()}return r}function An(t){var e=.5,r=Math.cos(30*Et),n=16;function i(e){return(n?function(e){var r,i,o,s,l,c,u,f,h,p,d,g,m={point:v,lineStart:y,lineEnd:b,polygonStart:function(){e.polygonStart(),m.lineStart=_},polygonEnd:function(){e.polygonEnd(),m.lineStart=y}};function v(r,n){r=t(r,n),e.point(r[0],r[1])}function y(){f=NaN,m.point=x,e.lineStart()}function x(r,i){var o=zr([r,i]),s=t(r,i);a(f,h,u,p,d,g,f=s[0],h=s[1],u=r,p=o[0],d=o[1],g=o[2],n,e),e.point(f,h)}function b(){m.point=v,e.lineEnd()}function _(){y(),m.point=w,m.lineEnd=k}function w(t,e){x(r=t,e),i=f,o=h,s=p,l=d,c=g,m.point=x}function k(){a(f,h,u,p,d,g,i,o,r,s,l,c,n,e),m.lineEnd=b,b()}return m}:function(e){return Sn(e,function(r,n){r=t(r,n),e.point(r[0],r[1])})})(e)}function a(n,i,o,s,l,c,u,f,h,p,d,g,m,v){var x=u-n,b=f-i,_=x*x+b*b;if(_>4*e&&m--){var w=s+p,k=l+d,M=c+g,A=Math.sqrt(w*w+k*k+M*M),T=Math.asin(M/=A),S=y(y(M)-1)e||y((x*z+b*P)/_-.5)>.3||s*p+l*d+c*g0&&16,i):Math.sqrt(e)},i}function Tn(t){this.stream=t}function Sn(t,e){return{point:e,sphere:function(){t.sphere()},lineStart:function(){t.lineStart()},lineEnd:function(){t.lineEnd()},polygonStart:function(){t.polygonStart()},polygonEnd:function(){t.polygonEnd()}}}function Cn(t){return En(function(){return t})()}function En(e){var r,n,i,a,o,s,l=An(function(t,e){return[(t=r(t,e))[0]*c+a,o-t[1]*c]}),c=150,u=480,f=250,h=0,p=0,d=0,g=0,m=0,v=tn,x=z,b=null,_=null;function w(t){return[(t=i(t[0]*Et,t[1]*Et))[0]*c+a,o-t[1]*c]}function k(t){return(t=i.invert((t[0]-a)/c,(o-t[1])/c))&&[t[0]*Lt,t[1]*Lt]}function M(){i=Gr(n=Dn(d,g,m),r);var t=r(h,p);return a=u-t[0]*c,o=f+t[1]*c,A()}function A(){return s&&(s.valid=!1,s=null),w}return w.stream=function(t){return s&&(s.valid=!1),(s=Ln(v(n,l(x(t))))).valid=!0,s},w.clipAngle=function(t){return arguments.length?(v=null==t?(b=t,tn):function(t){var e=Math.cos(t),r=e>0,n=y(e)>kt;return Jr(i,function(t){var e,s,l,c,u;return{lineStart:function(){c=l=!1,u=1},point:function(f,h){var p,d=[f,h],g=i(f,h),m=r?g?0:o(f,h):g?o(f+(f<0?At:-At),h):0;if(!e&&(c=l=g)&&t.lineStart(),g!==l&&(p=a(e,d),(Fr(e,p)||Fr(d,p))&&(d[0]+=kt,d[1]+=kt,g=i(d[0],d[1]))),g!==l)u=0,g?(t.lineStart(),p=a(d,e),t.point(p[0],p[1])):(p=a(e,d),t.point(p[0],p[1]),t.lineEnd()),e=p;else if(n&&e&&r^g){var v;m&s||!(v=a(d,e,!0))||(u=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1])))}!g||e&&Fr(e,d)||t.point(d[0],d[1]),e=d,l=g,s=m},lineEnd:function(){l&&t.lineEnd(),e=null},clean:function(){return u|(c&&l)<<1}}},Bn(t,6*Et),r?[0,-t]:[-At,t-At]);function i(t,r){return Math.cos(t)*Math.cos(r)>e}function a(t,r,n){var i=[1,0,0],a=Dr(zr(t),zr(r)),o=Pr(a,a),s=a[0],l=o-s*s;if(!l)return!n&&t;var c=e*o/l,u=-e*s/l,f=Dr(i,a),h=Ir(i,c);Or(h,Ir(a,u));var p=f,d=Pr(h,p),g=Pr(p,p),m=d*d-g*(Pr(h,h)-1);if(!(m<0)){var v=Math.sqrt(m),x=Ir(p,(-d-v)/g);if(Or(x,h),x=Br(x),!n)return x;var b,_=t[0],w=r[0],k=t[1],M=r[1];w<_&&(b=_,_=w,w=b);var A=w-_,T=y(A-At)0^x[1]<(y(x[0]-_)At^(_<=x[0]&&x[0]<=w)){var S=Ir(p,(-d+v)/g);return Or(S,h),[x,Br(S)]}}}function o(e,n){var i=r?t:At-t,a=0;return e<-i?a|=1:e>i&&(a|=2),n<-i?a|=4:n>i&&(a|=8),a}}((b=+t)*Et),A()):b},w.clipExtent=function(t){return arguments.length?(_=t,x=t?nn(t[0][0],t[0][1],t[1][0],t[1][1]):z,A()):_},w.scale=function(t){return arguments.length?(c=+t,M()):c},w.translate=function(t){return arguments.length?(u=+t[0],f=+t[1],M()):[u,f]},w.center=function(t){return arguments.length?(h=t[0]%360*Et,p=t[1]%360*Et,M()):[h*Lt,p*Lt]},w.rotate=function(t){return arguments.length?(d=t[0]%360*Et,g=t[1]%360*Et,m=t.length>2?t[2]%360*Et:0,M()):[d*Lt,g*Lt,m*Lt]},t.rebind(w,l,"precision"),function(){return r=e.apply(this,arguments),w.invert=r.invert&&k,M()}}function Ln(t){return Sn(t,function(e,r){t.point(e*Et,r*Et)})}function zn(t,e){return[t,e]}function Pn(t,e){return[t>At?t-Tt:t<-At?t+Tt:t,e]}function Dn(t,e,r){return t?e||r?Gr(In(t),Rn(e,r)):In(t):e||r?Rn(e,r):Pn}function On(t){return function(e,r){return[(e+=t)>At?e-Tt:e<-At?e+Tt:e,r]}}function In(t){var e=On(t);return e.invert=On(-t),e}function Rn(t,e){var r=Math.cos(t),n=Math.sin(t),i=Math.cos(e),a=Math.sin(e);function o(t,e){var o=Math.cos(e),s=Math.cos(t)*o,l=Math.sin(t)*o,c=Math.sin(e),u=c*r+s*n;return[Math.atan2(l*i-u*a,s*r-c*n),Ot(u*i+l*a)]}return o.invert=function(t,e){var o=Math.cos(e),s=Math.cos(t)*o,l=Math.sin(t)*o,c=Math.sin(e),u=c*i-l*a;return[Math.atan2(l*i+c*a,s*r+u*n),Ot(u*r-s*n)]},o}function Bn(t,e){var r=Math.cos(t),n=Math.sin(t);return function(i,a,o,s){var l=o*e;null!=i?(i=Fn(r,i),a=Fn(r,a),(o>0?ia)&&(i+=o*Tt)):(i=t+o*Tt,a=t-.5*l);for(var c,u=i;o>0?u>a:u2?t[2]*Et:0),e.invert=function(e){return(e=t.invert(e[0]*Et,e[1]*Et))[0]*=Lt,e[1]*=Lt,e},e},Pn.invert=zn,t.geo.circle=function(){var t,e,r=[0,0],n=6;function i(){var t="function"==typeof r?r.apply(this,arguments):r,n=Dn(-t[0]*Et,-t[1]*Et,0).invert,i=[];return e(null,null,1,{point:function(t,e){i.push(t=n(t,e)),t[0]*=Lt,t[1]*=Lt}}),{type:"Polygon",coordinates:[i]}}return i.origin=function(t){return arguments.length?(r=t,i):r},i.angle=function(r){return arguments.length?(e=Bn((t=+r)*Et,n*Et),i):t},i.precision=function(r){return arguments.length?(e=Bn(t*Et,(n=+r)*Et),i):n},i.angle(90)},t.geo.distance=function(t,e){var r,n=(e[0]-t[0])*Et,i=t[1]*Et,a=e[1]*Et,o=Math.sin(n),s=Math.cos(n),l=Math.sin(i),c=Math.cos(i),u=Math.sin(a),f=Math.cos(a);return Math.atan2(Math.sqrt((r=f*o)*r+(r=c*u-l*f*s)*r),l*u+c*f*s)},t.geo.graticule=function(){var e,r,n,i,a,o,s,l,c,u,f,h,p=10,d=p,g=90,m=360,v=2.5;function x(){return{type:"MultiLineString",coordinates:b()}}function b(){return t.range(Math.ceil(i/g)*g,n,g).map(f).concat(t.range(Math.ceil(l/m)*m,s,m).map(h)).concat(t.range(Math.ceil(r/p)*p,e,p).filter(function(t){return y(t%g)>kt}).map(c)).concat(t.range(Math.ceil(o/d)*d,a,d).filter(function(t){return y(t%m)>kt}).map(u))}return x.lines=function(){return b().map(function(t){return{type:"LineString",coordinates:t}})},x.outline=function(){return{type:"Polygon",coordinates:[f(i).concat(h(s).slice(1),f(n).reverse().slice(1),h(l).reverse().slice(1))]}},x.extent=function(t){return arguments.length?x.majorExtent(t).minorExtent(t):x.minorExtent()},x.majorExtent=function(t){return arguments.length?(i=+t[0][0],n=+t[1][0],l=+t[0][1],s=+t[1][1],i>n&&(t=i,i=n,n=t),l>s&&(t=l,l=s,s=t),x.precision(v)):[[i,l],[n,s]]},x.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],o=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),o>a&&(t=o,o=a,a=t),x.precision(v)):[[r,o],[e,a]]},x.step=function(t){return arguments.length?x.majorStep(t).minorStep(t):x.minorStep()},x.majorStep=function(t){return arguments.length?(g=+t[0],m=+t[1],x):[g,m]},x.minorStep=function(t){return arguments.length?(p=+t[0],d=+t[1],x):[p,d]},x.precision=function(t){return arguments.length?(v=+t,c=Nn(o,a,90),u=jn(r,e,v),f=Nn(l,s,90),h=jn(i,n,v),x):v},x.majorExtent([[-180,-90+kt],[180,90-kt]]).minorExtent([[-180,-80-kt],[180,80+kt]])},t.geo.greatArc=function(){var e,r,n=Vn,i=Un;function a(){return{type:"LineString",coordinates:[e||n.apply(this,arguments),r||i.apply(this,arguments)]}}return a.distance=function(){return t.geo.distance(e||n.apply(this,arguments),r||i.apply(this,arguments))},a.source=function(t){return arguments.length?(n=t,e="function"==typeof t?null:t,a):n},a.target=function(t){return arguments.length?(i=t,r="function"==typeof t?null:t,a):i},a.precision=function(){return arguments.length?a:0},a},t.geo.interpolate=function(t,e){return r=t[0]*Et,n=t[1]*Et,i=e[0]*Et,a=e[1]*Et,o=Math.cos(n),s=Math.sin(n),l=Math.cos(a),c=Math.sin(a),u=o*Math.cos(r),f=o*Math.sin(r),h=l*Math.cos(i),p=l*Math.sin(i),d=2*Math.asin(Math.sqrt(Rt(a-n)+o*l*Rt(i-r))),g=1/Math.sin(d),(m=d?function(t){var e=Math.sin(t*=d)*g,r=Math.sin(d-t)*g,n=r*u+e*h,i=r*f+e*p,a=r*s+e*c;return[Math.atan2(i,n)*Lt,Math.atan2(a,Math.sqrt(n*n+i*i))*Lt]}:function(){return[r*Lt,n*Lt]}).distance=d,m;var r,n,i,a,o,s,l,c,u,f,h,p,d,g,m},t.geo.length=function(e){return yn=0,t.geo.stream(e,qn),yn};var qn={sphere:I,point:I,lineStart:function(){var t,e,r;function n(n,i){var a=Math.sin(i*=Et),o=Math.cos(i),s=y((n*=Et)-t),l=Math.cos(s);yn+=Math.atan2(Math.sqrt((s=o*Math.sin(s))*s+(s=r*a-e*o*l)*s),e*a+r*o*l),t=n,e=a,r=o}qn.point=function(i,a){t=i*Et,e=Math.sin(a*=Et),r=Math.cos(a),qn.point=n},qn.lineEnd=function(){qn.point=qn.lineEnd=I}},lineEnd:I,polygonStart:I,polygonEnd:I};function Hn(t,e){function r(e,r){var n=Math.cos(e),i=Math.cos(r),a=t(n*i);return[a*i*Math.sin(e),a*Math.sin(r)]}return r.invert=function(t,r){var n=Math.sqrt(t*t+r*r),i=e(n),a=Math.sin(i),o=Math.cos(i);return[Math.atan2(t*a,n*o),Math.asin(n&&r*a/n)]},r}var Gn=Hn(function(t){return Math.sqrt(2/(1+t))},function(t){return 2*Math.asin(t/2)});(t.geo.azimuthalEqualArea=function(){return Cn(Gn)}).raw=Gn;var Wn=Hn(function(t){var e=Math.acos(t);return e&&e/Math.sin(e)},z);function Yn(t,e){var r=Math.cos(t),n=function(t){return Math.tan(At/4+t/2)},i=t===e?Math.sin(t):Math.log(r/Math.cos(e))/Math.log(n(e)/n(t)),a=r*Math.pow(n(t),i)/i;if(!i)return Jn;function o(t,e){a>0?e<-Ct+kt&&(e=-Ct+kt):e>Ct-kt&&(e=Ct-kt);var r=a/Math.pow(n(e),i);return[r*Math.sin(i*t),a-r*Math.cos(i*t)]}return o.invert=function(t,e){var r=a-e,n=zt(i)*Math.sqrt(t*t+r*r);return[Math.atan2(t,r)/i,2*Math.atan(Math.pow(a/n,1/i))-Ct]},o}function Xn(t,e){var r=Math.cos(t),n=t===e?Math.sin(t):(r-Math.cos(e))/(e-t),i=r/n+t;if(y(n)1&&Pt(t[r[n-2]],t[r[n-1]],t[i])<=0;)--n;r[n++]=i}return r.slice(0,n)}function ii(t,e){return t[0]-e[0]||t[1]-e[1]}(t.geo.stereographic=function(){return Cn($n)}).raw=$n,ti.invert=function(t,e){return[-e,2*Math.atan(Math.exp(t))-Ct]},(t.geo.transverseMercator=function(){var t=Kn(ti),e=t.center,r=t.rotate;return t.center=function(t){return t?e([-t[1],t[0]]):[(t=e())[1],-t[0]]},t.rotate=function(t){return t?r([t[0],t[1],t.length>2?t[2]+90:90]):[(t=r())[0],t[1],t[2]-90]},r([0,0,90])}).raw=ti,t.geom={},t.geom.hull=function(t){var e=ei,r=ri;if(arguments.length)return n(t);function n(t){if(t.length<3)return[];var n,i=me(e),a=me(r),o=t.length,s=[],l=[];for(n=0;n=0;--n)p.push(t[s[c[n]][2]]);for(n=+f;nkt)s=s.L;else{if(!((i=a-wi(s,o))>kt)){n>-kt?(e=s.P,r=s):i>-kt?(e=s,r=s.N):e=r=s;break}if(!s.R){e=s;break}s=s.R}var l=vi(t);if(fi.insert(e,l),e||r){if(e===r)return Si(e),r=vi(e.site),fi.insert(l,r),l.edge=r.edge=Li(e.site,l.site),Ti(e),void Ti(r);if(r){Si(e),Si(r);var c=e.site,u=c.x,f=c.y,h=t.x-u,p=t.y-f,d=r.site,g=d.x-u,m=d.y-f,v=2*(h*m-p*g),y=h*h+p*p,x=g*g+m*m,b={x:(m*y-p*x)/v+u,y:(h*x-g*y)/v+f};zi(r.edge,c,d,b),l.edge=Li(c,t,null,b),r.edge=Li(t,d,null,b),Ti(e),Ti(r)}else l.edge=Li(e.site,l.site)}}function _i(t,e){var r=t.site,n=r.x,i=r.y,a=i-e;if(!a)return n;var o=t.P;if(!o)return-1/0;var s=(r=o.site).x,l=r.y,c=l-e;if(!c)return s;var u=s-n,f=1/a-1/c,h=u/c;return f?(-h+Math.sqrt(h*h-2*f*(u*u/(-2*c)-l+c/2+i-a/2)))/f+n:(n+s)/2}function wi(t,e){var r=t.N;if(r)return _i(r,e);var n=t.site;return n.y===e?n.x:1/0}function ki(t){this.site=t,this.edges=[]}function Mi(t,e){return e.angle-t.angle}function Ai(){Oi(this),this.x=this.y=this.arc=this.site=this.cy=null}function Ti(t){var e=t.P,r=t.N;if(e&&r){var n=e.site,i=t.site,a=r.site;if(n!==a){var o=i.x,s=i.y,l=n.x-o,c=n.y-s,u=a.x-o,f=2*(l*(m=a.y-s)-c*u);if(!(f>=-Mt)){var h=l*l+c*c,p=u*u+m*m,d=(m*h-c*p)/f,g=(l*p-u*h)/f,m=g+s,v=gi.pop()||new Ai;v.arc=t,v.site=i,v.x=d+o,v.y=m+Math.sqrt(d*d+g*g),v.cy=m,t.circle=v;for(var y=null,x=pi._;x;)if(v.y=s)return;if(h>d){if(a){if(a.y>=c)return}else a={x:m,y:l};r={x:m,y:c}}else{if(a){if(a.y1)if(h>d){if(a){if(a.y>=c)return}else a={x:(l-i)/n,y:l};r={x:(c-i)/n,y:c}}else{if(a){if(a.y=s)return}else a={x:o,y:n*o+i};r={x:s,y:n*s+i}}else{if(a){if(a.xkt||y(i-r)>kt)&&(s.splice(o,0,new Pi((v=a.site,x=u,b=y(n-f)kt?{x:f,y:y(e-f)kt?{x:y(r-d)kt?{x:h,y:y(e-h)kt?{x:y(r-p)=r&&c.x<=i&&c.y>=n&&c.y<=o?[[r,o],[i,o],[i,n],[r,n]]:[]).point=t[s]}),e}function s(t){return t.map(function(t,e){return{x:Math.round(n(t,e)/kt)*kt,y:Math.round(i(t,e)/kt)*kt,i:e}})}return o.links=function(t){return Fi(s(t)).edges.filter(function(t){return t.l&&t.r}).map(function(e){return{source:t[e.l.i],target:t[e.r.i]}})},o.triangles=function(t){var e=[];return Fi(s(t)).cells.forEach(function(r,n){for(var i,a,o,s,l=r.site,c=r.edges.sort(Mi),u=-1,f=c.length,h=c[f-1].edge,p=h.l===l?h.r:h.l;++ua&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(r=r[0])===(n=n[0])?s[o]?s[o]+=n:s[++o]=n:(s[++o]=null,l.push({i:o,x:Gi(r,n)})),a=Xi.lastIndex;return ag&&(g=l.x),l.y>m&&(m=l.y),c.push(l.x),u.push(l.y);else for(f=0;fg&&(g=b),_>m&&(m=_),c.push(b),u.push(_)}var w=g-p,k=m-d;function M(t,e,r,n,i,a,o,s){if(!isNaN(r)&&!isNaN(n))if(t.leaf){var l=t.x,c=t.y;if(null!=l)if(y(l-r)+y(c-n)<.01)A(t,e,r,n,i,a,o,s);else{var u=t.point;t.x=t.y=t.point=null,A(t,u,l,c,i,a,o,s),A(t,e,r,n,i,a,o,s)}else t.x=r,t.y=n,t.point=e}else A(t,e,r,n,i,a,o,s)}function A(t,e,r,n,i,a,o,s){var l=.5*(i+o),c=.5*(a+s),u=r>=l,f=n>=c,h=f<<1|u;t.leaf=!1,u?i=l:o=l,f?a=c:s=c,M(t=t.nodes[h]||(t.nodes[h]={leaf:!0,nodes:[],point:null,x:null,y:null,add:function(t){M(T,t,+v(t,++f),+x(t,f),p,d,g,m)}}),e,r,n,i,a,o,s)}w>k?m=d+w:g=p+k;var T={leaf:!0,nodes:[],point:null,x:null,y:null,add:function(t){M(T,t,+v(t,++f),+x(t,f),p,d,g,m)}};if(T.visit=function(t){!function t(e,r,n,i,a,o){if(!e(r,n,i,a,o)){var s=.5*(n+a),l=.5*(i+o),c=r.nodes;c[0]&&t(e,c[0],n,i,s,l),c[1]&&t(e,c[1],s,i,a,l),c[2]&&t(e,c[2],n,l,s,o),c[3]&&t(e,c[3],s,l,a,o)}}(t,T,p,d,g,m)},T.find=function(t){return function(t,e,r,n,i,a,o){var s,l=1/0;return function t(c,u,f,h,p){if(!(u>a||f>o||h=_)<<1|e>=b,k=w+4;w=0&&!(n=t.interpolators[i](e,r)););return n}function Ji(t,e){var r,n=[],i=[],a=t.length,o=e.length,s=Math.min(t.length,e.length);for(r=0;r=1)return 1;var e=t*t,r=e*t;return 4*(t<.5?r:3*(t-e)+r-.75)}function aa(t){return 1-Math.cos(t*Ct)}function oa(t){return Math.pow(2,10*(t-1))}function sa(t){return 1-Math.sqrt(1-t*t)}function la(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}function ca(t,e){return e-=t,function(r){return Math.round(t+e*r)}}function ua(t){var e,r,n,i=[t.a,t.b],a=[t.c,t.d],o=ha(i),s=fa(i,a),l=ha(((e=a)[0]+=(n=-s)*(r=i)[0],e[1]+=n*r[1],e))||0;i[0]*a[1]=0?t.slice(0,n):t,a=n>=0?t.slice(n+1):"in";return i=Qi.get(i)||Ki,a=$i.get(a)||z,e=a(i.apply(null,r.call(arguments,1))),function(t){return t<=0?0:t>=1?1:e(t)}},t.interpolateHcl=function(e,r){e=t.hcl(e),r=t.hcl(r);var n=e.h,i=e.c,a=e.l,o=r.h-n,s=r.c-i,l=r.l-a;isNaN(s)&&(s=0,i=isNaN(i)?r.c:i);isNaN(o)?(o=0,n=isNaN(n)?r.h:n):o>180?o-=360:o<-180&&(o+=360);return function(t){return Yt(n+o*t,i+s*t,a+l*t)+""}},t.interpolateHsl=function(e,r){e=t.hsl(e),r=t.hsl(r);var n=e.h,i=e.s,a=e.l,o=r.h-n,s=r.s-i,l=r.l-a;isNaN(s)&&(s=0,i=isNaN(i)?r.s:i);isNaN(o)?(o=0,n=isNaN(n)?r.h:n):o>180?o-=360:o<-180&&(o+=360);return function(t){return Ht(n+o*t,i+s*t,a+l*t)+""}},t.interpolateLab=function(e,r){e=t.lab(e),r=t.lab(r);var n=e.l,i=e.a,a=e.b,o=r.l-n,s=r.a-i,l=r.b-a;return function(t){return te(n+o*t,i+s*t,a+l*t)+""}},t.interpolateRound=ca,t.transform=function(e){var r=i.createElementNS(t.ns.prefix.svg,"g");return(t.transform=function(t){if(null!=t){r.setAttribute("transform",t);var e=r.transform.baseVal.consolidate()}return new ua(e?e.matrix:pa)})(e)},ua.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var pa={a:1,b:0,c:0,d:1,e:0,f:0};function da(t){return t.length?t.pop()+",":""}function ga(e,r){var n=[],i=[];return e=t.transform(e),r=t.transform(r),function(t,e,r,n){if(t[0]!==e[0]||t[1]!==e[1]){var i=r.push("translate(",null,",",null,")");n.push({i:i-4,x:Gi(t[0],e[0])},{i:i-2,x:Gi(t[1],e[1])})}else(e[0]||e[1])&&r.push("translate("+e+")")}(e.translate,r.translate,n,i),function(t,e,r,n){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),n.push({i:r.push(da(r)+"rotate(",null,")")-2,x:Gi(t,e)})):e&&r.push(da(r)+"rotate("+e+")")}(e.rotate,r.rotate,n,i),function(t,e,r,n){t!==e?n.push({i:r.push(da(r)+"skewX(",null,")")-2,x:Gi(t,e)}):e&&r.push(da(r)+"skewX("+e+")")}(e.skew,r.skew,n,i),function(t,e,r,n){if(t[0]!==e[0]||t[1]!==e[1]){var i=r.push(da(r)+"scale(",null,",",null,")");n.push({i:i-4,x:Gi(t[0],e[0])},{i:i-2,x:Gi(t[1],e[1])})}else 1===e[0]&&1===e[1]||r.push(da(r)+"scale("+e+")")}(e.scale,r.scale,n,i),e=r=null,function(t){for(var e,r=-1,a=i.length;++r0?n=t:(e.c=null,e.t=NaN,e=null,l.end({type:"end",alpha:n=0})):t>0&&(l.start({type:"start",alpha:n=t}),e=Me(s.tick)),s):n},s.start=function(){var t,e,r,n=v.length,l=y.length,u=c[0],d=c[1];for(t=0;t=0;)r.push(i[n])}function Ea(t,e){for(var r=[t],n=[];null!=(t=r.pop());)if(n.push(t),(a=t.children)&&(i=a.length))for(var i,a,o=-1;++o=0;)o.push(u=c[l]),u.parent=a,u.depth=a.depth+1;r&&(a.value=0),a.children=c}else r&&(a.value=+r.call(n,a,a.depth)||0),delete a.children;return Ea(i,function(e){var n,i;t&&(n=e.children)&&n.sort(t),r&&(i=e.parent)&&(i.value+=e.value)}),s}return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(Ca(t,function(t){t.children&&(t.value=0)}),Ea(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},t.layout.partition=function(){var e=t.layout.hierarchy(),r=[1,1];function n(t,n){var i=e.call(this,t,n);return function t(e,r,n,i){var a=e.children;if(e.x=r,e.y=e.depth*i,e.dx=n,e.dy=i,a&&(o=a.length)){var o,s,l,c=-1;for(n=e.value?n/e.value:0;++cs&&(s=n),o.push(n)}for(r=0;ri&&(n=r,i=e);return n}function qa(t){return t.reduce(Ha,0)}function Ha(t,e){return t+e[1]}function Ga(t,e){return Wa(t,Math.ceil(Math.log(e.length)/Math.LN2+1))}function Wa(t,e){for(var r=-1,n=+t[0],i=(t[1]-n)/e,a=[];++r<=e;)a[r]=i*r+n;return a}function Ya(e){return[t.min(e),t.max(e)]}function Xa(t,e){return t.value-e.value}function Za(t,e){var r=t._pack_next;t._pack_next=e,e._pack_prev=t,e._pack_next=r,r._pack_prev=e}function Ja(t,e){t._pack_next=e,e._pack_prev=t}function Ka(t,e){var r=e.x-t.x,n=e.y-t.y,i=t.r+e.r;return.999*i*i>r*r+n*n}function Qa(t){if((e=t.children)&&(l=e.length)){var e,r,n,i,a,o,s,l,c=1/0,u=-1/0,f=1/0,h=-1/0;if(e.forEach($a),(r=e[0]).x=-r.r,r.y=0,x(r),l>1&&((n=e[1]).x=n.r,n.y=0,x(n),l>2))for(eo(r,n,i=e[2]),x(i),Za(r,i),r._pack_prev=i,Za(i,n),n=r._pack_next,a=3;a0)for(o=-1;++o=f[0]&&l<=f[1]&&((s=c[t.bisect(h,l,1,d)-1]).y+=g,s.push(a[o]));return c}return a.value=function(t){return arguments.length?(r=t,a):r},a.range=function(t){return arguments.length?(n=me(t),a):n},a.bins=function(t){return arguments.length?(i="number"==typeof t?function(e){return Wa(e,t)}:me(t),a):i},a.frequency=function(t){return arguments.length?(e=!!t,a):e},a},t.layout.pack=function(){var e,r=t.layout.hierarchy().sort(Xa),n=0,i=[1,1];function a(t,a){var o=r.call(this,t,a),s=o[0],l=i[0],c=i[1],u=null==e?Math.sqrt:"function"==typeof e?e:function(){return e};if(s.x=s.y=0,Ea(s,function(t){t.r=+u(t.value)}),Ea(s,Qa),n){var f=n*(e?1:Math.max(2*s.r/l,2*s.r/c))/2;Ea(s,function(t){t.r+=f}),Ea(s,Qa),Ea(s,function(t){t.r-=f})}return function t(e,r,n,i){var a=e.children;e.x=r+=i*e.x;e.y=n+=i*e.y;e.r*=i;if(a)for(var o=-1,s=a.length;++op.x&&(p=t),t.depth>d.depth&&(d=t)});var g=r(h,p)/2-h.x,m=n[0]/(p.x+r(p,h)/2+g),v=n[1]/(d.depth||1);Ca(u,function(t){t.x=(t.x+g)*m,t.y=t.depth*v})}return c}function o(t){var e=t.children,n=t.parent.children,i=t.i?n[t.i-1]:null;if(e.length){!function(t){var e,r=0,n=0,i=t.children,a=i.length;for(;--a>=0;)(e=i[a]).z+=r,e.m+=r,r+=e.s+(n+=e.c)}(t);var a=(e[0].z+e[e.length-1].z)/2;i?(t.z=i.z+r(t._,i._),t.m=t.z-a):t.z=a}else i&&(t.z=i.z+r(t._,i._));t.parent.A=function(t,e,n){if(e){for(var i,a=t,o=t,s=e,l=a.parent.children[0],c=a.m,u=o.m,f=s.m,h=l.m;s=io(s),a=no(a),s&&a;)l=no(l),(o=io(o)).a=t,(i=s.z+f-a.z-c+r(s._,a._))>0&&(ao(oo(s,t,n),t,i),c+=i,u+=i),f+=s.m,c+=a.m,h+=l.m,u+=o.m;s&&!io(o)&&(o.t=s,o.m+=f-u),a&&!no(l)&&(l.t=a,l.m+=c-h,n=t)}return n}(t,i,t.parent.A||n[0])}function s(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function l(t){t.x*=n[0],t.y=t.depth*n[1]}return a.separation=function(t){return arguments.length?(r=t,a):r},a.size=function(t){return arguments.length?(i=null==(n=t)?l:null,a):i?null:n},a.nodeSize=function(t){return arguments.length?(i=null==(n=t)?null:l,a):i?n:null},Sa(a,e)},t.layout.cluster=function(){var e=t.layout.hierarchy().sort(null).value(null),r=ro,n=[1,1],i=!1;function a(a,o){var s,l=e.call(this,a,o),c=l[0],u=0;Ea(c,function(e){var n=e.children;n&&n.length?(e.x=function(t){return t.reduce(function(t,e){return t+e.x},0)/t.length}(n),e.y=function(e){return 1+t.max(e,function(t){return t.y})}(n)):(e.x=s?u+=r(e,s):0,e.y=0,s=e)});var f=function t(e){var r=e.children;return r&&r.length?t(r[0]):e}(c),h=function t(e){var r,n=e.children;return n&&(r=n.length)?t(n[r-1]):e}(c),p=f.x-r(f,h)/2,d=h.x+r(h,f)/2;return Ea(c,i?function(t){t.x=(t.x-c.x)*n[0],t.y=(c.y-t.y)*n[1]}:function(t){t.x=(t.x-p)/(d-p)*n[0],t.y=(1-(c.y?t.y/c.y:1))*n[1]}),l}return a.separation=function(t){return arguments.length?(r=t,a):r},a.size=function(t){return arguments.length?(i=null==(n=t),a):i?null:n},a.nodeSize=function(t){return arguments.length?(i=null!=(n=t),a):i?n:null},Sa(a,e)},t.layout.treemap=function(){var e,r=t.layout.hierarchy(),n=Math.round,i=[1,1],a=null,o=so,s=!1,l="squarify",c=.5*(1+Math.sqrt(5));function u(t,e){for(var r,n,i=-1,a=t.length;++i0;)s.push(r=c[i-1]),s.area+=r.area,"squarify"!==l||(n=p(s,g))<=h?(c.pop(),h=n):(s.area-=s.pop().area,d(s,g,a,!1),g=Math.min(a.dx,a.dy),s.length=s.area=0,h=1/0);s.length&&(d(s,g,a,!0),s.length=s.area=0),e.forEach(f)}}function h(t){var e=t.children;if(e&&e.length){var r,n=o(t),i=e.slice(),a=[];for(u(i,n.dx*n.dy/t.value),a.area=0;r=i.pop();)a.push(r),a.area+=r.area,null!=r.z&&(d(a,r.z?n.dx:n.dy,n,!i.length),a.length=a.area=0);e.forEach(h)}}function p(t,e){for(var r,n=t.area,i=0,a=1/0,o=-1,s=t.length;++oi&&(i=r));return e*=e,(n*=n)?Math.max(e*i*c/n,n/(e*a*c)):1/0}function d(t,e,r,i){var a,o=-1,s=t.length,l=r.x,c=r.y,u=e?n(t.area/e):0;if(e==r.dx){for((i||u>r.dy)&&(u=r.dy);++or.dx)&&(u=r.dx);++o1);return t+e*r*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var e=t.random.normal.apply(t,arguments);return function(){return Math.exp(e())}},bates:function(e){var r=t.random.irwinHall(e);return function(){return r()/e}},irwinHall:function(t){return function(){for(var e=0,r=0;r2?mo:fo,s=i?va:ma;return a=t(e,r,s,n),o=t(r,e,s,Zi),l}function l(t){return a(t)}l.invert=function(t){return o(t)};l.domain=function(t){return arguments.length?(e=t.map(Number),s()):e};l.range=function(t){return arguments.length?(r=t,s()):r};l.rangeRound=function(t){return l.range(t).interpolate(ca)};l.clamp=function(t){return arguments.length?(i=t,s()):i};l.interpolate=function(t){return arguments.length?(n=t,s()):n};l.ticks=function(t){return bo(e,t)};l.tickFormat=function(t,r){return _o(e,t,r)};l.nice=function(t){return yo(e,t),s()};l.copy=function(){return t(e,r,n,i)};return s()}([0,1],[0,1],Zi,!1)};var wo={s:1,g:1,p:1,r:1,e:1};function ko(t){return-Math.floor(Math.log(t)/Math.LN10+.01)}t.scale.log=function(){return function e(r,n,i,a){function o(t){return(i?Math.log(t<0?0:t):-Math.log(t>0?0:-t))/Math.log(n)}function s(t){return i?Math.pow(n,t):-Math.pow(n,-t)}function l(t){return r(o(t))}l.invert=function(t){return s(r.invert(t))};l.domain=function(t){return arguments.length?(i=t[0]>=0,r.domain((a=t.map(Number)).map(o)),l):a};l.base=function(t){return arguments.length?(n=+t,r.domain(a.map(o)),l):n};l.nice=function(){var t=ho(a.map(o),i?Math:Ao);return r.domain(t),a=t.map(s),l};l.ticks=function(){var t=co(a),e=[],r=t[0],l=t[1],c=Math.floor(o(r)),u=Math.ceil(o(l)),f=n%1?2:n;if(isFinite(u-c)){if(i){for(;c0;h--)e.push(s(c)*h);for(c=0;e[c]l;u--);e=e.slice(c,u)}return e};l.tickFormat=function(e,r){if(!arguments.length)return Mo;arguments.length<2?r=Mo:"function"!=typeof r&&(r=t.format(r));var i=Math.max(1,n*e/l.ticks().length);return function(t){var e=t/s(Math.round(o(t)));return e*n0?i[t-1]:r[0],tf?0:1;if(c=St)return l(c,p)+(s?l(s,1-p):"")+"Z";var d,g,m,v,y,x,b,_,w,k,M,A,T=0,S=0,C=[];if((v=(+o.apply(this,arguments)||0)/2)&&(m=n===Po?Math.sqrt(s*s+c*c):+n.apply(this,arguments),p||(S*=-1),c&&(S=Ot(m/c*Math.sin(v))),s&&(T=Ot(m/s*Math.sin(v)))),c){y=c*Math.cos(u+S),x=c*Math.sin(u+S),b=c*Math.cos(f-S),_=c*Math.sin(f-S);var E=Math.abs(f-u-2*S)<=At?0:1;if(S&&Fo(y,x,b,_)===p^E){var L=(u+f)/2;y=c*Math.cos(L),x=c*Math.sin(L),b=_=null}}else y=x=0;if(s){w=s*Math.cos(f-T),k=s*Math.sin(f-T),M=s*Math.cos(u+T),A=s*Math.sin(u+T);var z=Math.abs(u-f+2*T)<=At?0:1;if(T&&Fo(w,k,M,A)===1-p^z){var P=(u+f)/2;w=s*Math.cos(P),k=s*Math.sin(P),M=A=null}}else w=k=0;if(h>kt&&(d=Math.min(Math.abs(c-s)/2,+r.apply(this,arguments)))>.001){g=s0?0:1}function No(t,e,r,n,i){var a=t[0]-e[0],o=t[1]-e[1],s=(i?n:-n)/Math.sqrt(a*a+o*o),l=s*o,c=-s*a,u=t[0]+l,f=t[1]+c,h=e[0]+l,p=e[1]+c,d=(u+h)/2,g=(f+p)/2,m=h-u,v=p-f,y=m*m+v*v,x=r-n,b=u*p-h*f,_=(v<0?-1:1)*Math.sqrt(Math.max(0,x*x*y-b*b)),w=(b*v-m*_)/y,k=(-b*m-v*_)/y,M=(b*v+m*_)/y,A=(-b*m+v*_)/y,T=w-d,S=k-g,C=M-d,E=A-g;return T*T+S*S>C*C+E*E&&(w=M,k=A),[[w-l,k-c],[w*r/x,k*r/x]]}function jo(t){var e=ei,r=ri,n=Wr,i=Uo,a=i.key,o=.7;function s(a){var s,l=[],c=[],u=-1,f=a.length,h=me(e),p=me(r);function d(){l.push("M",i(t(c),o))}for(;++u1&&i.push("H",n[0]);return i.join("")},"step-before":Ho,"step-after":Go,basis:Xo,"basis-open":function(t){if(t.length<4)return Uo(t);var e,r=[],n=-1,i=t.length,a=[0],o=[0];for(;++n<3;)e=t[n],a.push(e[0]),o.push(e[1]);r.push(Zo(Qo,a)+","+Zo(Qo,o)),--n;for(;++n9&&(i=3*e/Math.sqrt(i),o[s]=i*r,o[s+1]=i*n));s=-1;for(;++s<=l;)i=(t[Math.min(l,s+1)][0]-t[Math.max(0,s-1)][0])/(6*(1+o[s]*o[s])),a.push([i||0,o[s]*i||0]);return a}(t))}});function Uo(t){return t.length>1?t.join("L"):t+"Z"}function qo(t){return t.join("L")+"Z"}function Ho(t){for(var e=0,r=t.length,n=t[0],i=[n[0],",",n[1]];++e1){s=e[1],a=t[l],l++,n+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(a[0]-s[0])+","+(a[1]-s[1])+","+a[0]+","+a[1];for(var c=2;cAt)+",1 "+e}function l(t,e,r,n){return"Q 0,0 "+n}return a.radius=function(t){return arguments.length?(r=me(t),a):r},a.source=function(e){return arguments.length?(t=me(e),a):t},a.target=function(t){return arguments.length?(e=me(t),a):e},a.startAngle=function(t){return arguments.length?(n=me(t),a):n},a.endAngle=function(t){return arguments.length?(i=me(t),a):i},a},t.svg.diagonal=function(){var t=Vn,e=Un,r=is;function n(n,i){var a=t.call(this,n,i),o=e.call(this,n,i),s=(a.y+o.y)/2,l=[a,{x:a.x,y:s},{x:o.x,y:s},o];return"M"+(l=l.map(r))[0]+"C"+l[1]+" "+l[2]+" "+l[3]}return n.source=function(e){return arguments.length?(t=me(e),n):t},n.target=function(t){return arguments.length?(e=me(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},t.svg.diagonal.radial=function(){var e=t.svg.diagonal(),r=is,n=e.projection;return e.projection=function(t){return arguments.length?n(function(t){return function(){var e=t.apply(this,arguments),r=e[0],n=e[1]-Ct;return[r*Math.cos(n),r*Math.sin(n)]}}(r=t)):r},e},t.svg.symbol=function(){var t=os,e=as;function r(r,n){return(ls.get(t.call(this,r,n))||ss)(e.call(this,r,n))}return r.type=function(e){return arguments.length?(t=me(e),r):t},r.size=function(t){return arguments.length?(e=me(t),r):e},r};var ls=t.map({circle:ss,cross:function(t){var e=Math.sqrt(t/5)/2;return"M"+-3*e+","+-e+"H"+-e+"V"+-3*e+"H"+e+"V"+-e+"H"+3*e+"V"+e+"H"+e+"V"+3*e+"H"+-e+"V"+e+"H"+-3*e+"Z"},diamond:function(t){var e=Math.sqrt(t/(2*us)),r=e*us;return"M0,"+-e+"L"+r+",0 0,"+e+" "+-r+",0Z"},square:function(t){var e=Math.sqrt(t)/2;return"M"+-e+","+-e+"L"+e+","+-e+" "+e+","+e+" "+-e+","+e+"Z"},"triangle-down":function(t){var e=Math.sqrt(t/cs),r=e*cs/2;return"M0,"+r+"L"+e+","+-r+" "+-e+","+-r+"Z"},"triangle-up":function(t){var e=Math.sqrt(t/cs),r=e*cs/2;return"M0,"+-r+"L"+e+","+r+" "+-e+","+r+"Z"}});t.svg.symbolTypes=ls.keys();var cs=Math.sqrt(3),us=Math.tan(30*Et);Y.transition=function(t){for(var e,r,n=ds||++vs,i=bs(t),a=[],o=gs||{time:Date.now(),ease:ia,delay:0,duration:250},s=-1,l=this.length;++s0;)c[--h].call(t,o);if(a>=1)return f.event&&f.event.end.call(t,t.__data__,e),--u.count?delete u[n]:delete t[r],1}f||(a=i.time,o=Me(function(t){var e=f.delay;if(o.t=e+a,e<=t)return h(t-e);o.c=h},0,a),f=u[n]={tween:new b,time:a,timer:o,delay:i.delay,duration:i.duration,ease:i.ease,index:e},i=null,++u.count)}ms.call=Y.call,ms.empty=Y.empty,ms.node=Y.node,ms.size=Y.size,t.transition=function(e,r){return e&&e.transition?ds?e.transition(r):e:t.selection().transition(e)},t.transition.prototype=ms,ms.select=function(t){var e,r,n,i=this.id,a=this.namespace,o=[];t=X(t);for(var s=-1,l=this.length;++srect,.s>rect").attr("width",s[1]-s[0])}function g(t){t.select(".extent").attr("y",l[0]),t.selectAll(".extent,.e>rect,.w>rect").attr("height",l[1]-l[0])}function m(){var f,m,v=this,y=t.select(t.event.target),x=n.of(v,arguments),b=t.select(v),_=y.datum(),w=!/^(n|s)$/.test(_)&&i,k=!/^(e|w)$/.test(_)&&a,M=y.classed("extent"),A=xt(v),T=t.mouse(v),S=t.select(o(v)).on("keydown.brush",function(){32==t.event.keyCode&&(M||(f=null,T[0]-=s[1],T[1]-=l[1],M=2),F())}).on("keyup.brush",function(){32==t.event.keyCode&&2==M&&(T[0]+=s[1],T[1]+=l[1],M=0,F())});if(t.event.changedTouches?S.on("touchmove.brush",L).on("touchend.brush",P):S.on("mousemove.brush",L).on("mouseup.brush",P),b.interrupt().selectAll("*").interrupt(),M)T[0]=s[0]-T[0],T[1]=l[0]-T[1];else if(_){var C=+/w$/.test(_),E=+/^n/.test(_);m=[s[1-C]-T[0],l[1-E]-T[1]],T[0]=s[C],T[1]=l[E]}else t.event.altKey&&(f=T.slice());function L(){var e=t.mouse(v),r=!1;m&&(e[0]+=m[0],e[1]+=m[1]),M||(t.event.altKey?(f||(f=[(s[0]+s[1])/2,(l[0]+l[1])/2]),T[0]=s[+(e[0]1?{floor:function(e){for(;s(e=t.floor(e));)e=Ds(e-1);return e},ceil:function(e){for(;s(e=t.ceil(e));)e=Ds(+e+1);return e}}:t))},i.ticks=function(t,e){var r=co(i.domain()),n=null==t?a(r,10):"number"==typeof t?a(r,t):!t.range&&[{range:t},e];return n&&(t=n[0],e=n[1]),t.range(r[0],Ds(+r[1]+1),e<1?1:e)},i.tickFormat=function(){return n},i.copy=function(){return Ps(e.copy(),r,n)},vo(i,e)}function Ds(t){return new Date(t)}Cs.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?zs:Ls,zs.parse=function(t){var e=new Date(t);return isNaN(e)?null:e},zs.toString=Ls.toString,De.second=Be(function(t){return new Oe(1e3*Math.floor(t/1e3))},function(t,e){t.setTime(t.getTime()+1e3*Math.floor(e))},function(t){return t.getSeconds()}),De.seconds=De.second.range,De.seconds.utc=De.second.utc.range,De.minute=Be(function(t){return new Oe(6e4*Math.floor(t/6e4))},function(t,e){t.setTime(t.getTime()+6e4*Math.floor(e))},function(t){return t.getMinutes()}),De.minutes=De.minute.range,De.minutes.utc=De.minute.utc.range,De.hour=Be(function(t){var e=t.getTimezoneOffset()/60;return new Oe(36e5*(Math.floor(t/36e5-e)+e))},function(t,e){t.setTime(t.getTime()+36e5*Math.floor(e))},function(t){return t.getHours()}),De.hours=De.hour.range,De.hours.utc=De.hour.utc.range,De.month=Be(function(t){return(t=De.day(t)).setDate(1),t},function(t,e){t.setMonth(t.getMonth()+e)},function(t){return t.getMonth()}),De.months=De.month.range,De.months.utc=De.month.utc.range;var Os=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Is=[[De.second,1],[De.second,5],[De.second,15],[De.second,30],[De.minute,1],[De.minute,5],[De.minute,15],[De.minute,30],[De.hour,1],[De.hour,3],[De.hour,6],[De.hour,12],[De.day,1],[De.day,2],[De.week,1],[De.month,1],[De.month,3],[De.year,1]],Rs=Cs.multi([[".%L",function(t){return t.getMilliseconds()}],[":%S",function(t){return t.getSeconds()}],["%I:%M",function(t){return t.getMinutes()}],["%I %p",function(t){return t.getHours()}],["%a %d",function(t){return t.getDay()&&1!=t.getDate()}],["%b %d",function(t){return 1!=t.getDate()}],["%B",function(t){return t.getMonth()}],["%Y",Wr]]),Bs={range:function(e,r,n){return t.range(Math.ceil(e/n)*n,+r,n).map(Ds)},floor:z,ceil:z};Is.year=De.year,De.scale=function(){return Ps(t.scale.linear(),Is,Rs)};var Fs=Is.map(function(t){return[t[0].utc,t[1]]}),Ns=Es.multi([[".%L",function(t){return t.getUTCMilliseconds()}],[":%S",function(t){return t.getUTCSeconds()}],["%I:%M",function(t){return t.getUTCMinutes()}],["%I %p",function(t){return t.getUTCHours()}],["%a %d",function(t){return t.getUTCDay()&&1!=t.getUTCDate()}],["%b %d",function(t){return 1!=t.getUTCDate()}],["%B",function(t){return t.getUTCMonth()}],["%Y",Wr]]);function js(t){return JSON.parse(t.responseText)}function Vs(t){var e=i.createRange();return e.selectNode(i.body),e.createContextualFragment(t.responseText)}Fs.year=De.year.utc,De.scale.utc=function(){return Ps(t.scale.linear(),Fs,Ns)},t.text=ve(function(t){return t.responseText}),t.json=function(t,e){return ye(t,"application/json",js,e)},t.html=function(t,e){return ye(t,"text/html",Vs,e)},t.xml=ve(function(t){return t.responseXML}),"object"==typeof e&&e.exports?e.exports=t:this.d3=t}()},{}],132:[function(t,e,r){e.exports=function(){for(var t=0;t=2)return!1;t[r]=n}return!0}):_.filter(function(t){for(var e=0;e<=s;++e){var r=v[t[e]];if(r<0)return!1;t[e]=r}return!0});if(1&s)for(var u=0;u<_.length;++u){var b=_[u],h=b[0];b[0]=b[1],b[1]=h}return _}},{"incremental-convex-hull":357,uniq:483}],134:[function(t,e,r){(function(t){var r=!1;if("undefined"!=typeof Float64Array){var n=new Float64Array(1),i=new Uint32Array(n.buffer);if(n[0]=1,r=!0,1072693248===i[1]){e.exports=function(t){return n[0]=t,[i[0],i[1]]},e.exports.pack=function(t,e){return i[0]=t,i[1]=e,n[0]},e.exports.lo=function(t){return n[0]=t,i[0]},e.exports.hi=function(t){return n[0]=t,i[1]}}else if(1072693248===i[0]){e.exports=function(t){return n[0]=t,[i[1],i[0]]},e.exports.pack=function(t,e){return i[1]=t,i[0]=e,n[0]},e.exports.lo=function(t){return n[0]=t,i[1]},e.exports.hi=function(t){return n[0]=t,i[0]}}else r=!1}if(!r){var a=new t(8);e.exports=function(t){return a.writeDoubleLE(t,0,!0),[a.readUInt32LE(0,!0),a.readUInt32LE(4,!0)]},e.exports.pack=function(t,e){return a.writeUInt32LE(t,0,!0),a.writeUInt32LE(e,4,!0),a.readDoubleLE(0,!0)},e.exports.lo=function(t){return a.writeDoubleLE(t,0,!0),a.readUInt32LE(0,!0)},e.exports.hi=function(t){return a.writeDoubleLE(t,0,!0),a.readUInt32LE(4,!0)}}e.exports.sign=function(t){return e.exports.hi(t)>>>31},e.exports.exponent=function(t){return(e.exports.hi(t)<<1>>>21)-1023},e.exports.fraction=function(t){var r=e.exports.lo(t),n=e.exports.hi(t),i=1048575&n;return 2146435072&n&&(i+=1<<20),[r,i]},e.exports.denormalized=function(t){return!(2146435072&e.exports.hi(t))}}).call(this,t("buffer").Buffer)},{buffer:86}],135:[function(t,e,r){var n=t("abs-svg-path"),i=t("normalize-svg-path"),a={M:"moveTo",C:"bezierCurveTo"};e.exports=function(t,e){t.beginPath(),i(n(e)).forEach(function(e){var r=e[0],n=e.slice(1);t[a[r]].apply(t,n)}),t.closePath()}},{"abs-svg-path":45,"normalize-svg-path":395}],136:[function(t,e,r){e.exports=function(t){switch(t){case"int8":return Int8Array;case"int16":return Int16Array;case"int32":return Int32Array;case"uint8":return Uint8Array;case"uint16":return Uint16Array;case"uint32":return Uint32Array;case"float32":return Float32Array;case"float64":return Float64Array;case"array":return Array;case"uint8_clamped":return Uint8ClampedArray}}},{}],137:[function(t,e,r){"use strict";e.exports=function(t,e){switch(void 0===e&&(e=0),typeof t){case"number":if(t>0)return function(t,e){var r,n;for(r=new Array(t),n=0;n80*r){n=l=t[0],s=c=t[1];for(var b=r;bl&&(l=u),p>c&&(c=p);g=0!==(g=Math.max(l-n,c-s))?1/g:0}return o(y,x,r,n,s,g),x}function i(t,e,r,n,i){var a,o;if(i===A(t,e,r,n)>0)for(a=e;a=e;a-=n)o=w(a,t[a],t[a+1],o);return o&&y(o,o.next)&&(k(o),o=o.next),o}function a(t,e){if(!t)return t;e||(e=t);var r,n=t;do{if(r=!1,n.steiner||!y(n,n.next)&&0!==v(n.prev,n,n.next))n=n.next;else{if(k(n),(n=e=n.prev)===n.next)break;r=!0}}while(r||n!==e);return e}function o(t,e,r,n,i,f,h){if(t){!h&&f&&function(t,e,r,n){var i=t;do{null===i.z&&(i.z=p(i.x,i.y,e,r,n)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next}while(i!==t);i.prevZ.nextZ=null,i.prevZ=null,function(t){var e,r,n,i,a,o,s,l,c=1;do{for(r=t,t=null,a=null,o=0;r;){for(o++,n=r,s=0,e=0;e0||l>0&&n;)0!==s&&(0===l||!n||r.z<=n.z)?(i=r,r=r.nextZ,s--):(i=n,n=n.nextZ,l--),a?a.nextZ=i:t=i,i.prevZ=a,a=i;r=n}a.nextZ=null,c*=2}while(o>1)}(i)}(t,n,i,f);for(var d,g,m=t;t.prev!==t.next;)if(d=t.prev,g=t.next,f?l(t,n,i,f):s(t))e.push(d.i/r),e.push(t.i/r),e.push(g.i/r),k(t),t=g.next,m=g.next;else if((t=g)===m){h?1===h?o(t=c(t,e,r),e,r,n,i,f,2):2===h&&u(t,e,r,n,i,f):o(a(t),e,r,n,i,f,1);break}}}function s(t){var e=t.prev,r=t,n=t.next;if(v(e,r,n)>=0)return!1;for(var i=t.next.next;i!==t.prev;){if(g(e.x,e.y,r.x,r.y,n.x,n.y,i.x,i.y)&&v(i.prev,i,i.next)>=0)return!1;i=i.next}return!0}function l(t,e,r,n){var i=t.prev,a=t,o=t.next;if(v(i,a,o)>=0)return!1;for(var s=i.xa.x?i.x>o.x?i.x:o.x:a.x>o.x?a.x:o.x,u=i.y>a.y?i.y>o.y?i.y:o.y:a.y>o.y?a.y:o.y,f=p(s,l,e,r,n),h=p(c,u,e,r,n),d=t.prevZ,m=t.nextZ;d&&d.z>=f&&m&&m.z<=h;){if(d!==t.prev&&d!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,d.x,d.y)&&v(d.prev,d,d.next)>=0)return!1;if(d=d.prevZ,m!==t.prev&&m!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,m.x,m.y)&&v(m.prev,m,m.next)>=0)return!1;m=m.nextZ}for(;d&&d.z>=f;){if(d!==t.prev&&d!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,d.x,d.y)&&v(d.prev,d,d.next)>=0)return!1;d=d.prevZ}for(;m&&m.z<=h;){if(m!==t.prev&&m!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,m.x,m.y)&&v(m.prev,m,m.next)>=0)return!1;m=m.nextZ}return!0}function c(t,e,r){var n=t;do{var i=n.prev,a=n.next.next;!y(i,a)&&x(i,n,n.next,a)&&b(i,a)&&b(a,i)&&(e.push(i.i/r),e.push(n.i/r),e.push(a.i/r),k(n),k(n.next),n=t=a),n=n.next}while(n!==t);return n}function u(t,e,r,n,i,s){var l=t;do{for(var c=l.next.next;c!==l.prev;){if(l.i!==c.i&&m(l,c)){var u=_(l,c);return l=a(l,l.next),u=a(u,u.next),o(l,e,r,n,i,s),void o(u,e,r,n,i,s)}c=c.next}l=l.next}while(l!==t)}function f(t,e){return t.x-e.x}function h(t,e){if(e=function(t,e){var r,n=e,i=t.x,a=t.y,o=-1/0;do{if(a<=n.y&&a>=n.next.y&&n.next.y!==n.y){var s=n.x+(a-n.y)*(n.next.x-n.x)/(n.next.y-n.y);if(s<=i&&s>o){if(o=s,s===i){if(a===n.y)return n;if(a===n.next.y)return n.next}r=n.x=n.x&&n.x>=u&&i!==n.x&&g(ar.x)&&b(n,t)&&(r=n,h=l),n=n.next;return r}(t,e)){var r=_(e,t);a(r,r.next)}}function p(t,e,r,n,i){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-r)*i)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-n)*i)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function d(t){var e=t,r=t;do{e.x=0&&(t-o)*(n-s)-(r-o)*(e-s)>=0&&(r-o)*(a-s)-(i-o)*(n-s)>=0}function m(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){var r=t;do{if(r.i!==t.i&&r.next.i!==t.i&&r.i!==e.i&&r.next.i!==e.i&&x(r,r.next,t,e))return!0;r=r.next}while(r!==t);return!1}(t,e)&&b(t,e)&&b(e,t)&&function(t,e){var r=t,n=!1,i=(t.x+e.x)/2,a=(t.y+e.y)/2;do{r.y>a!=r.next.y>a&&r.next.y!==r.y&&i<(r.next.x-r.x)*(a-r.y)/(r.next.y-r.y)+r.x&&(n=!n),r=r.next}while(r!==t);return n}(t,e)}function v(t,e,r){return(e.y-t.y)*(r.x-e.x)-(e.x-t.x)*(r.y-e.y)}function y(t,e){return t.x===e.x&&t.y===e.y}function x(t,e,r,n){return!!(y(t,e)&&y(r,n)||y(t,n)&&y(r,e))||v(t,e,r)>0!=v(t,e,n)>0&&v(r,n,t)>0!=v(r,n,e)>0}function b(t,e){return v(t.prev,t,t.next)<0?v(t,e,t.next)>=0&&v(t,t.prev,e)>=0:v(t,e,t.prev)<0||v(t,t.next,e)<0}function _(t,e){var r=new M(t.i,t.x,t.y),n=new M(e.i,e.x,e.y),i=t.next,a=e.prev;return t.next=e,e.prev=t,r.next=i,i.prev=r,n.next=r,r.prev=n,a.next=n,n.prev=a,n}function w(t,e,r,n){var i=new M(t,e,r);return n?(i.next=n.next,i.prev=n,n.next.prev=i,n.next=i):(i.prev=i,i.next=i),i}function k(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function M(t,e,r){this.i=t,this.x=e,this.y=r,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function A(t,e,r,n){for(var i=0,a=e,o=r-n;a0&&(n+=t[i-1].length,r.holes.push(n))}return r}},{}],139:[function(t,e,r){"use strict";e.exports=function(t,e){var r=t.length;if("number"!=typeof e){e=0;for(var i=0;i=55296&&y<=56319&&(w+=t[++r]),w=k?h.call(k,M,w,g):w,e?(p.value=w,d(m,g,p)):m[g]=w,++g;v=g}if(void 0===v)for(v=o(t.length),e&&(m=new e(v)),r=0;r0?1:-1}},{}],150:[function(t,e,r){"use strict";var n=t("../math/sign"),i=Math.abs,a=Math.floor;e.exports=function(t){return isNaN(t)?0:0!==(t=Number(t))&&isFinite(t)?n(t)*a(i(t)):t}},{"../math/sign":147}],151:[function(t,e,r){"use strict";var n=t("./to-integer"),i=Math.max;e.exports=function(t){return i(0,n(t))}},{"./to-integer":150}],152:[function(t,e,r){"use strict";var n=t("./valid-callable"),i=t("./valid-value"),a=Function.prototype.bind,o=Function.prototype.call,s=Object.keys,l=Object.prototype.propertyIsEnumerable;e.exports=function(t,e){return function(r,c){var u,f=arguments[2],h=arguments[3];return r=Object(i(r)),n(c),u=s(r),h&&u.sort("function"==typeof h?a.call(h,r):void 0),"function"!=typeof t&&(t=u[t]),o.call(t,u,function(t,n){return l.call(r,t)?o.call(c,f,r[t],t,r,n):e})}}},{"./valid-callable":170,"./valid-value":172}],153:[function(t,e,r){"use strict";e.exports=t("./is-implemented")()?Object.assign:t("./shim")},{"./is-implemented":154,"./shim":155}],154:[function(t,e,r){"use strict";e.exports=function(){var t,e=Object.assign;return"function"==typeof e&&(e(t={foo:"raz"},{bar:"dwa"},{trzy:"trzy"}),t.foo+t.bar+t.trzy==="razdwatrzy")}},{}],155:[function(t,e,r){"use strict";var n=t("../keys"),i=t("../valid-value"),a=Math.max;e.exports=function(t,e){var r,o,s,l=a(arguments.length,2);for(t=Object(i(t)),s=function(n){try{t[n]=e[n]}catch(t){r||(r=t)}},o=1;o-1}},{}],176:[function(t,e,r){"use strict";var n=Object.prototype.toString,i=n.call("");e.exports=function(t){return"string"==typeof t||t&&"object"==typeof t&&(t instanceof String||n.call(t)===i)||!1}},{}],177:[function(t,e,r){"use strict";var n=Object.create(null),i=Math.random;e.exports=function(){var t;do{t=i().toString(36).slice(2)}while(n[t]);return t}},{}],178:[function(t,e,r){"use strict";var n,i=t("es5-ext/object/set-prototype-of"),a=t("es5-ext/string/#/contains"),o=t("d"),s=t("es6-symbol"),l=t("./"),c=Object.defineProperty;n=e.exports=function(t,e){if(!(this instanceof n))throw new TypeError("Constructor requires 'new'");l.call(this,t),e=e?a.call(e,"key+value")?"key+value":a.call(e,"key")?"key":"value":"value",c(this,"__kind__",o("",e))},i&&i(n,l),delete n.prototype.constructor,n.prototype=Object.create(l.prototype,{_resolve:o(function(t){return"value"===this.__kind__?this.__list__[t]:"key+value"===this.__kind__?[t,this.__list__[t]]:t})}),c(n.prototype,s.toStringTag,o("c","Array Iterator"))},{"./":181,d:122,"es5-ext/object/set-prototype-of":167,"es5-ext/string/#/contains":173,"es6-symbol":186}],179:[function(t,e,r){"use strict";var n=t("es5-ext/function/is-arguments"),i=t("es5-ext/object/valid-callable"),a=t("es5-ext/string/is-string"),o=t("./get"),s=Array.isArray,l=Function.prototype.call,c=Array.prototype.some;e.exports=function(t,e){var r,u,f,h,p,d,g,m,v=arguments[2];if(s(t)||n(t)?r="array":a(t)?r="string":t=o(t),i(e),f=function(){h=!0},"array"!==r)if("string"!==r)for(u=t.next();!u.done;){if(l.call(e,v,u.value,f),h)return;u=t.next()}else for(d=t.length,p=0;p=55296&&m<=56319&&(g+=t[++p]),l.call(e,v,g,f),!h);++p);else c.call(t,function(t){return l.call(e,v,t,f),h})}},{"./get":180,"es5-ext/function/is-arguments":144,"es5-ext/object/valid-callable":170,"es5-ext/string/is-string":176}],180:[function(t,e,r){"use strict";var n=t("es5-ext/function/is-arguments"),i=t("es5-ext/string/is-string"),a=t("./array"),o=t("./string"),s=t("./valid-iterable"),l=t("es6-symbol").iterator;e.exports=function(t){return"function"==typeof s(t)[l]?t[l]():n(t)?new a(t):i(t)?new o(t):new a(t)}},{"./array":178,"./string":183,"./valid-iterable":184,"es5-ext/function/is-arguments":144,"es5-ext/string/is-string":176,"es6-symbol":186}],181:[function(t,e,r){"use strict";var n,i=t("es5-ext/array/#/clear"),a=t("es5-ext/object/assign"),o=t("es5-ext/object/valid-callable"),s=t("es5-ext/object/valid-value"),l=t("d"),c=t("d/auto-bind"),u=t("es6-symbol"),f=Object.defineProperty,h=Object.defineProperties;e.exports=n=function(t,e){if(!(this instanceof n))throw new TypeError("Constructor requires 'new'");h(this,{__list__:l("w",s(t)),__context__:l("w",e),__nextIndex__:l("w",0)}),e&&(o(e.on),e.on("_add",this._onAdd),e.on("_delete",this._onDelete),e.on("_clear",this._onClear))},delete n.prototype.constructor,h(n.prototype,a({_next:l(function(){var t;if(this.__list__)return this.__redo__&&void 0!==(t=this.__redo__.shift())?t:this.__nextIndex__=this.__nextIndex__||(++this.__nextIndex__,this.__redo__?(this.__redo__.forEach(function(e,r){e>=t&&(this.__redo__[r]=++e)},this),this.__redo__.push(t)):f(this,"__redo__",l("c",[t])))}),_onDelete:l(function(t){var e;t>=this.__nextIndex__||(--this.__nextIndex__,this.__redo__&&(-1!==(e=this.__redo__.indexOf(t))&&this.__redo__.splice(e,1),this.__redo__.forEach(function(e,r){e>t&&(this.__redo__[r]=--e)},this)))}),_onClear:l(function(){this.__redo__&&i.call(this.__redo__),this.__nextIndex__=0})}))),f(n.prototype,u.iterator,l(function(){return this}))},{d:122,"d/auto-bind":121,"es5-ext/array/#/clear":140,"es5-ext/object/assign":153,"es5-ext/object/valid-callable":170,"es5-ext/object/valid-value":172,"es6-symbol":186}],182:[function(t,e,r){"use strict";var n=t("es5-ext/function/is-arguments"),i=t("es5-ext/object/is-value"),a=t("es5-ext/string/is-string"),o=t("es6-symbol").iterator,s=Array.isArray;e.exports=function(t){return!!i(t)&&(!!s(t)||(!!a(t)||(!!n(t)||"function"==typeof t[o])))}},{"es5-ext/function/is-arguments":144,"es5-ext/object/is-value":161,"es5-ext/string/is-string":176,"es6-symbol":186}],183:[function(t,e,r){"use strict";var n,i=t("es5-ext/object/set-prototype-of"),a=t("d"),o=t("es6-symbol"),s=t("./"),l=Object.defineProperty;n=e.exports=function(t){if(!(this instanceof n))throw new TypeError("Constructor requires 'new'");t=String(t),s.call(this,t),l(this,"__length__",a("",t.length))},i&&i(n,s),delete n.prototype.constructor,n.prototype=Object.create(s.prototype,{_next:a(function(){if(this.__list__)return this.__nextIndex__=55296&&e<=56319?r+this.__list__[this.__nextIndex__++]:r})}),l(n.prototype,o.toStringTag,a("c","String Iterator"))},{"./":181,d:122,"es5-ext/object/set-prototype-of":167,"es6-symbol":186}],184:[function(t,e,r){"use strict";var n=t("./is-iterable");e.exports=function(t){if(!n(t))throw new TypeError(t+" is not iterable");return t}},{"./is-iterable":182}],185:[function(t,e,r){(function(n,i){!function(t,n){"object"==typeof r&&void 0!==e?e.exports=n():t.ES6Promise=n()}(this,function(){"use strict";function e(t){return"function"==typeof t}var r=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},a=0,o=void 0,s=void 0,l=function(t,e){g[a]=t,g[a+1]=e,2===(a+=2)&&(s?s(m):_())};var c="undefined"!=typeof window?window:void 0,u=c||{},f=u.MutationObserver||u.WebKitMutationObserver,h="undefined"==typeof self&&void 0!==n&&"[object process]"==={}.toString.call(n),p="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function d(){var t=setTimeout;return function(){return t(m,1)}}var g=new Array(1e3);function m(){for(var t=0;t0&&this._events[t].length>r&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),"function"==typeof console.trace&&console.trace()),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(t,e){if(!i(e))throw TypeError("listener must be a function");var r=!1;function n(){this.removeListener(t,n),r||(r=!0,e.apply(this,arguments))}return n.listener=e,this.on(t,n),this},n.prototype.removeListener=function(t,e){var r,n,o,s;if(!i(e))throw TypeError("listener must be a function");if(!this._events||!this._events[t])return this;if(o=(r=this._events[t]).length,n=-1,r===e||i(r.listener)&&r.listener===e)delete this._events[t],this._events.removeListener&&this.emit("removeListener",t,e);else if(a(r)){for(s=o;s-- >0;)if(r[s]===e||r[s].listener&&r[s].listener===e){n=s;break}if(n<0)return this;1===r.length?(r.length=0,delete this._events[t]):r.splice(n,1),this._events.removeListener&&this.emit("removeListener",t,e)}return this},n.prototype.removeAllListeners=function(t){var e,r;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[t]&&delete this._events[t],this;if(0===arguments.length){for(e in this._events)"removeListener"!==e&&this.removeAllListeners(e);return this.removeAllListeners("removeListener"),this._events={},this}if(i(r=this._events[t]))this.removeListener(t,r);else if(r)for(;r.length;)this.removeListener(t,r[r.length-1]);return delete this._events[t],this},n.prototype.listeners=function(t){return this._events&&this._events[t]?i(this._events[t])?[this._events[t]]:this._events[t].slice():[]},n.prototype.listenerCount=function(t){if(this._events){var e=this._events[t];if(i(e))return 1;if(e)return e.length}return 0},n.listenerCount=function(t,e){return t.listenerCount(e)}},{}],196:[function(t,e,r){"use strict";e.exports=function(t,e,r){var n=e||0,i=r||1;return[[t[12]+t[0],t[13]+t[1],t[14]+t[2],t[15]+t[3]],[t[12]-t[0],t[13]-t[1],t[14]-t[2],t[15]-t[3]],[t[12]+t[4],t[13]+t[5],t[14]+t[6],t[15]+t[7]],[t[12]-t[4],t[13]-t[5],t[14]-t[6],t[15]-t[7]],[n*t[12]+t[8],n*t[13]+t[9],n*t[14]+t[10],n*t[15]+t[11]],[i*t[12]-t[8],i*t[13]-t[9],i*t[14]-t[10],i*t[15]-t[11]]]}},{}],197:[function(t,e,r){"use strict";e.exports=function(t){var e=typeof t;if("string"===e){var r=t;if(0===(t=+t)&&function(t){for(var e,r=t.length,n=0;n13)&&32!==e&&133!==e&&160!==e&&5760!==e&&6158!==e&&(e<8192||e>8205)&&8232!==e&&8233!==e&&8239!==e&&8287!==e&&8288!==e&&12288!==e&&65279!==e)return!1;return!0}(r))return!1}else if("number"!==e)return!1;return t-t<1}},{}],198:[function(t,e,r){"use strict";e.exports=function(t,e,r){switch(arguments.length){case 0:return new o([0],[0],0);case 1:if("number"==typeof t){var n=l(t);return new o(n,n,0)}return new o(t,l(t.length),0);case 2:if("number"==typeof e){var n=l(t.length);return new o(t,n,+e)}r=0;case 3:if(t.length!==e.length)throw new Error("state and velocity lengths must match");return new o(t,e,r)}};var n=t("cubic-hermite"),i=t("binary-search-bounds");function a(t,e,r){return Math.min(e,Math.max(t,r))}function o(t,e,r){this.dimension=t.length,this.bounds=[new Array(this.dimension),new Array(this.dimension)];for(var n=0;n=r-1){h=l.length-1;var d=t-e[r-1];for(p=0;p=r-1)for(var u=s.length-1,f=(e[r-1],0);f=0;--r)if(t[--e])return!1;return!0},s.jump=function(t){var e=this.lastT(),r=this.dimension;if(!(t0;--f)n.push(a(l[f-1],c[f-1],arguments[f])),i.push(0)}},s.push=function(t){var e=this.lastT(),r=this.dimension;if(!(t1e-6?1/s:0;this._time.push(t);for(var h=r;h>0;--h){var p=a(c[h-1],u[h-1],arguments[h]);n.push(p),i.push((p-n[o++])*f)}}},s.set=function(t){var e=this.dimension;if(!(t0;--l)r.push(a(o[l-1],s[l-1],arguments[l])),n.push(0)}},s.move=function(t){var e=this.lastT(),r=this.dimension;if(!(t<=e||arguments.length!==r+1)){var n=this._state,i=this._velocity,o=n.length-this.dimension,s=this.bounds,l=s[0],c=s[1],u=t-e,f=u>1e-6?1/u:0;this._time.push(t);for(var h=r;h>0;--h){var p=arguments[h];n.push(a(l[h-1],c[h-1],n[o++]+p)),i.push(p*f)}}},s.idle=function(t){var e=this.lastT();if(!(t=0;--f)n.push(a(l[f],c[f],n[o]+u*i[o])),i.push(0),o+=1}}},{"binary-search-bounds":73,"cubic-hermite":116}],199:[function(t,e,r){var n=t("dtype");e.exports=function(t,e,r){if(!t)throw new TypeError("must specify data as first parameter");if(r=0|+(r||0),Array.isArray(t)&&Array.isArray(t[0])){var i=t[0].length,a=t.length*i;e&&"string"!=typeof e||(e=new(n(e||"float32"))(a+r));var o=e.length-r;if(a!==o)throw new Error("source length "+a+" ("+i+"x"+t.length+") does not match destination length "+o);for(var s=0,l=r;s=0;--p){o=u[p];f[p]<=0?u[p]=new a(o._color,o.key,o.value,u[p+1],o.right,o._count+1):u[p]=new a(o._color,o.key,o.value,o.left,u[p+1],o._count+1)}for(p=u.length-1;p>1;--p){var d=u[p-1];o=u[p];if(d._color===i||o._color===i)break;var g=u[p-2];if(g.left===d)if(d.left===o){if(!(m=g.right)||m._color!==n){if(g._color=n,g.left=d.right,d._color=i,d.right=g,u[p-2]=d,u[p-1]=o,l(g),l(d),p>=3)(v=u[p-3]).left===g?v.left=d:v.right=d;break}d._color=i,g.right=s(i,m),g._color=n,p-=1}else{if(!(m=g.right)||m._color!==n){if(d.right=o.left,g._color=n,g.left=o.right,o._color=i,o.left=d,o.right=g,u[p-2]=o,u[p-1]=d,l(g),l(d),l(o),p>=3)(v=u[p-3]).left===g?v.left=o:v.right=o;break}d._color=i,g.right=s(i,m),g._color=n,p-=1}else if(d.right===o){if(!(m=g.left)||m._color!==n){if(g._color=n,g.right=d.left,d._color=i,d.left=g,u[p-2]=d,u[p-1]=o,l(g),l(d),p>=3)(v=u[p-3]).right===g?v.right=d:v.left=d;break}d._color=i,g.left=s(i,m),g._color=n,p-=1}else{var m;if(!(m=g.left)||m._color!==n){var v;if(d.left=o.right,g._color=n,g.right=o.left,o._color=i,o.right=d,o.left=g,u[p-2]=o,u[p-1]=d,l(g),l(d),l(o),p>=3)(v=u[p-3]).right===g?v.right=o:v.left=o;break}d._color=i,g.left=s(i,m),g._color=n,p-=1}}return u[0]._color=i,new c(r,u[0])},u.forEach=function(t,e,r){if(this.root)switch(arguments.length){case 1:return function t(e,r){var n;if(r.left&&(n=t(e,r.left)))return n;return(n=e(r.key,r.value))||(r.right?t(e,r.right):void 0)}(t,this.root);case 2:return function t(e,r,n,i){if(r(e,i.key)<=0){var a;if(i.left&&(a=t(e,r,n,i.left)))return a;if(a=n(i.key,i.value))return a}if(i.right)return t(e,r,n,i.right)}(e,this._compare,t,this.root);case 3:if(this._compare(e,r)>=0)return;return function t(e,r,n,i,a){var o,s=n(e,a.key),l=n(r,a.key);if(s<=0){if(a.left&&(o=t(e,r,n,i,a.left)))return o;if(l>0&&(o=i(a.key,a.value)))return o}if(l>0&&a.right)return t(e,r,n,i,a.right)}(e,r,this._compare,t,this.root)}},Object.defineProperty(u,"begin",{get:function(){for(var t=[],e=this.root;e;)t.push(e),e=e.left;return new f(this,t)}}),Object.defineProperty(u,"end",{get:function(){for(var t=[],e=this.root;e;)t.push(e),e=e.right;return new f(this,t)}}),u.at=function(t){if(t<0)return new f(this,[]);for(var e=this.root,r=[];;){if(r.push(e),e.left){if(t=e.right._count)break;e=e.right}return new f(this,[])},u.ge=function(t){for(var e=this._compare,r=this.root,n=[],i=0;r;){var a=e(t,r.key);n.push(r),a<=0&&(i=n.length),r=a<=0?r.left:r.right}return n.length=i,new f(this,n)},u.gt=function(t){for(var e=this._compare,r=this.root,n=[],i=0;r;){var a=e(t,r.key);n.push(r),a<0&&(i=n.length),r=a<0?r.left:r.right}return n.length=i,new f(this,n)},u.lt=function(t){for(var e=this._compare,r=this.root,n=[],i=0;r;){var a=e(t,r.key);n.push(r),a>0&&(i=n.length),r=a<=0?r.left:r.right}return n.length=i,new f(this,n)},u.le=function(t){for(var e=this._compare,r=this.root,n=[],i=0;r;){var a=e(t,r.key);n.push(r),a>=0&&(i=n.length),r=a<0?r.left:r.right}return n.length=i,new f(this,n)},u.find=function(t){for(var e=this._compare,r=this.root,n=[];r;){var i=e(t,r.key);if(n.push(r),0===i)return new f(this,n);r=i<=0?r.left:r.right}return new f(this,[])},u.remove=function(t){var e=this.find(t);return e?e.remove():this},u.get=function(t){for(var e=this._compare,r=this.root;r;){var n=e(t,r.key);if(0===n)return r.value;r=n<=0?r.left:r.right}};var h=f.prototype;function p(t,e){t.key=e.key,t.value=e.value,t.left=e.left,t.right=e.right,t._color=e._color,t._count=e._count}function d(t,e){return te?1:0}Object.defineProperty(h,"valid",{get:function(){return this._stack.length>0}}),Object.defineProperty(h,"node",{get:function(){return this._stack.length>0?this._stack[this._stack.length-1]:null},enumerable:!0}),h.clone=function(){return new f(this.tree,this._stack.slice())},h.remove=function(){var t=this._stack;if(0===t.length)return this.tree;var e=new Array(t.length),r=t[t.length-1];e[e.length-1]=new a(r._color,r.key,r.value,r.left,r.right,r._count);for(var u=t.length-2;u>=0;--u){(r=t[u]).left===t[u+1]?e[u]=new a(r._color,r.key,r.value,e[u+1],r.right,r._count):e[u]=new a(r._color,r.key,r.value,r.left,e[u+1],r._count)}if((r=e[e.length-1]).left&&r.right){var f=e.length;for(r=r.left;r.right;)e.push(r),r=r.right;var h=e[f-1];e.push(new a(r._color,h.key,h.value,r.left,r.right,r._count)),e[f-1].key=r.key,e[f-1].value=r.value;for(u=e.length-2;u>=f;--u)r=e[u],e[u]=new a(r._color,r.key,r.value,r.left,e[u+1],r._count);e[f-1].left=e[f]}if((r=e[e.length-1])._color===n){var d=e[e.length-2];d.left===r?d.left=null:d.right===r&&(d.right=null),e.pop();for(u=0;u=0;--u){if(e=t[u],0===u)return void(e._color=i);if((r=t[u-1]).left===e){if((a=r.right).right&&a.right._color===n)return c=(a=r.right=o(a)).right=o(a.right),r.right=a.left,a.left=r,a.right=c,a._color=r._color,e._color=i,r._color=i,c._color=i,l(r),l(a),u>1&&((f=t[u-2]).left===r?f.left=a:f.right=a),void(t[u-1]=a);if(a.left&&a.left._color===n)return c=(a=r.right=o(a)).left=o(a.left),r.right=c.left,a.left=c.right,c.left=r,c.right=a,c._color=r._color,r._color=i,a._color=i,e._color=i,l(r),l(a),l(c),u>1&&((f=t[u-2]).left===r?f.left=c:f.right=c),void(t[u-1]=c);if(a._color===i){if(r._color===n)return r._color=i,void(r.right=s(n,a));r.right=s(n,a);continue}a=o(a),r.right=a.left,a.left=r,a._color=r._color,r._color=n,l(r),l(a),u>1&&((f=t[u-2]).left===r?f.left=a:f.right=a),t[u-1]=a,t[u]=r,u+11&&((f=t[u-2]).right===r?f.right=a:f.left=a),void(t[u-1]=a);if(a.right&&a.right._color===n)return c=(a=r.left=o(a)).right=o(a.right),r.left=c.right,a.right=c.left,c.right=r,c.left=a,c._color=r._color,r._color=i,a._color=i,e._color=i,l(r),l(a),l(c),u>1&&((f=t[u-2]).right===r?f.right=c:f.left=c),void(t[u-1]=c);if(a._color===i){if(r._color===n)return r._color=i,void(r.left=s(n,a));r.left=s(n,a);continue}var f;a=o(a),r.left=a.right,a.right=r,a._color=r._color,r._color=n,l(r),l(a),u>1&&((f=t[u-2]).right===r?f.right=a:f.left=a),t[u-1]=a,t[u]=r,u+10)return this._stack[this._stack.length-1].key},enumerable:!0}),Object.defineProperty(h,"value",{get:function(){if(this._stack.length>0)return this._stack[this._stack.length-1].value},enumerable:!0}),Object.defineProperty(h,"index",{get:function(){var t=0,e=this._stack;if(0===e.length){var r=this.tree.root;return r?r._count:0}e[e.length-1].left&&(t=e[e.length-1].left._count);for(var n=e.length-2;n>=0;--n)e[n+1]===e[n].right&&(++t,e[n].left&&(t+=e[n].left._count));return t},enumerable:!0}),h.next=function(){var t=this._stack;if(0!==t.length){var e=t[t.length-1];if(e.right)for(e=e.right;e;)t.push(e),e=e.left;else for(t.pop();t.length>0&&t[t.length-1].right===e;)e=t[t.length-1],t.pop()}},Object.defineProperty(h,"hasNext",{get:function(){var t=this._stack;if(0===t.length)return!1;if(t[t.length-1].right)return!0;for(var e=t.length-1;e>0;--e)if(t[e-1].left===t[e])return!0;return!1}}),h.update=function(t){var e=this._stack;if(0===e.length)throw new Error("Can't update empty node!");var r=new Array(e.length),n=e[e.length-1];r[r.length-1]=new a(n._color,n.key,t,n.left,n.right,n._count);for(var i=e.length-2;i>=0;--i)(n=e[i]).left===e[i+1]?r[i]=new a(n._color,n.key,n.value,r[i+1],n.right,n._count):r[i]=new a(n._color,n.key,n.value,n.left,r[i+1],n._count);return new c(this.tree._compare,r[0])},h.prev=function(){var t=this._stack;if(0!==t.length){var e=t[t.length-1];if(e.left)for(e=e.left;e;)t.push(e),e=e.right;else for(t.pop();t.length>0&&t[t.length-1].left===e;)e=t[t.length-1],t.pop()}},Object.defineProperty(h,"hasPrev",{get:function(){var t=this._stack;if(0===t.length)return!1;if(t[t.length-1].left)return!0;for(var e=t.length-1;e>0;--e)if(t[e-1].right===t[e])return!0;return!1}})},{}],201:[function(t,e,r){var n=[.9999999999998099,676.5203681218851,-1259.1392167224028,771.3234287776531,-176.6150291621406,12.507343278686905,-.13857109526572012,9984369578019572e-21,1.5056327351493116e-7],i=607/128,a=[.9999999999999971,57.15623566586292,-59.59796035547549,14.136097974741746,-.4919138160976202,3399464998481189e-20,4652362892704858e-20,-9837447530487956e-20,.0001580887032249125,-.00021026444172410488,.00021743961811521265,-.0001643181065367639,8441822398385275e-20,-26190838401581408e-21,36899182659531625e-22];function o(t){if(t<0)return Number("0/0");for(var e=a[0],r=a.length-1;r>0;--r)e+=a[r]/(t+r);var n=t+i+.5;return.5*Math.log(2*Math.PI)+(t+.5)*Math.log(n)-n+Math.log(e)-Math.log(t)}e.exports=function t(e){if(e<.5)return Math.PI/(Math.sin(Math.PI*e)*t(1-e));if(e>100)return Math.exp(o(e));e-=1;for(var r=n[0],i=1;i<9;i++)r+=n[i]/(e+i);var a=e+7+.5;return Math.sqrt(2*Math.PI)*Math.pow(a,e+.5)*Math.exp(-a)*r},e.exports.log=o},{}],202:[function(t,e,r){e.exports=function(t,e){if("string"!=typeof t)throw new TypeError("must specify type string");if(e=e||{},"undefined"==typeof document&&!e.canvas)return null;var r=e.canvas||document.createElement("canvas");"number"==typeof e.width&&(r.width=e.width);"number"==typeof e.height&&(r.height=e.height);var n,i=e;try{var a=[t];0===t.indexOf("webgl")&&a.push("experimental-"+t);for(var o=0;o0?(p[u]=-1,d[u]=0):(p[u]=0,d[u]=1)}}var g=[0,0,0],m={model:l,view:l,projection:l};f.isOpaque=function(){return!0},f.isTransparent=function(){return!1},f.drawTransparent=function(t){};var v=[0,0,0],y=[0,0,0],x=[0,0,0];f.draw=function(t){t=t||m;for(var e=this.gl,r=t.model||l,n=t.view||l,i=t.projection||l,a=this.bounds,s=o(r,n,i,a),u=s.cubeEdges,f=s.axis,h=n[12],b=n[13],_=n[14],w=n[15],k=this.pixelRatio*(i[3]*h+i[7]*b+i[11]*_+i[15]*w)/e.drawingBufferHeight,M=0;M<3;++M)this.lastCubeProps.cubeEdges[M]=u[M],this.lastCubeProps.axis[M]=f[M];var A=p;for(M=0;M<3;++M)d(p[M],M,this.bounds,u,f);e=this.gl;var T=g;for(M=0;M<3;++M)this.backgroundEnable[M]?T[M]=f[M]:T[M]=0;this._background.draw(r,n,i,a,T,this.backgroundColor),this._lines.bind(r,n,i,this);for(M=0;M<3;++M){var S=[0,0,0];f[M]>0?S[M]=a[1][M]:S[M]=a[0][M];for(var C=0;C<2;++C){var E=(M+1+C)%3,L=(M+1+(1^C))%3;this.gridEnable[E]&&this._lines.drawGrid(E,L,this.bounds,S,this.gridColor[E],this.gridWidth[E]*this.pixelRatio)}for(C=0;C<2;++C){E=(M+1+C)%3,L=(M+1+(1^C))%3;this.zeroEnable[L]&&a[0][L]<=0&&a[1][L]>=0&&this._lines.drawZero(E,L,this.bounds,S,this.zeroLineColor[L],this.zeroLineWidth[L]*this.pixelRatio)}}for(M=0;M<3;++M){this.lineEnable[M]&&this._lines.drawAxisLine(M,this.bounds,A[M].primalOffset,this.lineColor[M],this.lineWidth[M]*this.pixelRatio),this.lineMirror[M]&&this._lines.drawAxisLine(M,this.bounds,A[M].mirrorOffset,this.lineColor[M],this.lineWidth[M]*this.pixelRatio);var z=c(v,A[M].primalMinor),P=c(y,A[M].mirrorMinor),D=this.lineTickLength;for(C=0;C<3;++C){var O=k/r[5*C];z[C]*=D[C]*O,P[C]*=D[C]*O}this.lineTickEnable[M]&&this._lines.drawAxisTicks(M,A[M].primalOffset,z,this.lineTickColor[M],this.lineTickWidth[M]*this.pixelRatio),this.lineTickMirror[M]&&this._lines.drawAxisTicks(M,A[M].mirrorOffset,P,this.lineTickColor[M],this.lineTickWidth[M]*this.pixelRatio)}this._lines.unbind(),this._text.bind(r,n,i,this.pixelRatio);for(M=0;M<3;++M){var I=A[M].primalMinor,R=c(x,A[M].primalOffset);for(C=0;C<3;++C)this.lineTickEnable[M]&&(R[C]+=k*I[C]*Math.max(this.lineTickLength[C],0)/r[5*C]);if(this.tickEnable[M]){for(C=0;C<3;++C)R[C]+=k*I[C]*this.tickPad[C]/r[5*C];this._text.drawTicks(M,this.tickSize[M],this.tickAngle[M],R,this.tickColor[M])}if(this.labelEnable[M]){for(C=0;C<3;++C)R[C]+=k*I[C]*this.labelPad[C]/r[5*C];R[M]+=.5*(a[0][M]+a[1][M]),this._text.drawLabel(M,this.labelSize[M],this.labelAngle[M],R,this.labelColor[M])}}this._text.unbind()},f.dispose=function(){this._text.dispose(),this._lines.dispose(),this._background.dispose(),this._lines=null,this._text=null,this._background=null,this.gl=null}},{"./lib/background.js":204,"./lib/cube.js":205,"./lib/lines.js":206,"./lib/text.js":208,"./lib/ticks.js":209}],204:[function(t,e,r){"use strict";e.exports=function(t){for(var e=[],r=[],s=0,l=0;l<3;++l)for(var c=(l+1)%3,u=(l+2)%3,f=[0,0,0],h=[0,0,0],p=-1;p<=1;p+=2){r.push(s,s+2,s+1,s+1,s+2,s+3),f[l]=p,h[l]=p;for(var d=-1;d<=1;d+=2){f[c]=d;for(var g=-1;g<=1;g+=2)f[u]=g,e.push(f[0],f[1],f[2],h[0],h[1],h[2]),s+=1}var m=c;c=u,u=m}var v=n(t,new Float32Array(e)),y=n(t,new Uint16Array(r),t.ELEMENT_ARRAY_BUFFER),x=i(t,[{buffer:v,type:t.FLOAT,size:3,offset:0,stride:24},{buffer:v,type:t.FLOAT,size:3,offset:12,stride:24}],y),b=a(t);return b.attributes.position.location=0,b.attributes.normal.location=1,new o(t,v,x,b)};var n=t("gl-buffer"),i=t("gl-vao"),a=t("./shaders").bg;function o(t,e,r,n){this.gl=t,this.buffer=e,this.vao=r,this.shader=n}var s=o.prototype;s.draw=function(t,e,r,n,i,a){for(var o=!1,s=0;s<3;++s)o=o||i[s];if(o){var l=this.gl;l.enable(l.POLYGON_OFFSET_FILL),l.polygonOffset(1,2),this.shader.bind(),this.shader.uniforms={model:t,view:e,projection:r,bounds:n,enable:i,colors:a},this.vao.bind(),this.vao.draw(this.gl.TRIANGLES,36),this.vao.unbind(),l.disable(l.POLYGON_OFFSET_FILL)}},s.dispose=function(){this.vao.dispose(),this.buffer.dispose(),this.shader.dispose()}},{"./shaders":207,"gl-buffer":211,"gl-vao":284}],205:[function(t,e,r){"use strict";e.exports=function(t,e,r,a){i(s,e,t),i(s,r,s);for(var p=0,y=0;y<2;++y){u[2]=a[y][2];for(var x=0;x<2;++x){u[1]=a[x][1];for(var b=0;b<2;++b)u[0]=a[b][0],h(l[p],u,s),p+=1}}for(var _=-1,y=0;y<8;++y){for(var w=l[y][3],k=0;k<3;++k)c[y][k]=l[y][k]/w;w<0&&(_<0?_=y:c[y][2]S&&(_|=1<S&&(_|=1<c[y][1]&&(I=y));for(var R=-1,y=0;y<3;++y){var B=I^1<c[F][0]&&(F=B)}}var N=g;N[0]=N[1]=N[2]=0,N[n.log2(R^I)]=I&R,N[n.log2(I^F)]=I&F;var j=7^F;j===_||j===O?(j=7^R,N[n.log2(F^j)]=j&F):N[n.log2(R^j)]=j&R;for(var V=m,U=_,M=0;M<3;++M)V[M]=U&1< 0.0) {\n vec3 nPosition = mix(bounds[0], bounds[1], 0.5 * (position + 1.0));\n gl_Position = projection * view * model * vec4(nPosition, 1.0);\n } else {\n gl_Position = vec4(0,0,0,0);\n }\n colorChannel = abs(normal);\n}"]),u=n(["precision mediump float;\n#define GLSLIFY 1\n\nuniform vec4 colors[3];\n\nvarying vec3 colorChannel;\n\nvoid main() {\n gl_FragColor = colorChannel.x * colors[0] + \n colorChannel.y * colors[1] +\n colorChannel.z * colors[2];\n}"]);r.bg=function(t){return i(t,c,u,null,[{name:"position",type:"vec3"},{name:"normal",type:"vec3"}])}},{"gl-shader":268,glslify:353}],208:[function(t,e,r){(function(r){"use strict";e.exports=function(t,e,r,a,s,l){var u=n(t),f=i(t,[{buffer:u,size:3}]),h=o(t);h.attributes.position.location=0;var p=new c(t,h,u,f);return p.update(e,r,a,s,l),p};var n=t("gl-buffer"),i=t("gl-vao"),a=t("vectorize-text"),o=t("./shaders").text,s=window||r.global||{},l=s.__TEXT_CACHE||{};s.__TEXT_CACHE={};function c(t,e,r,n){this.gl=t,this.shader=e,this.buffer=r,this.vao=n,this.tickOffset=this.tickCount=this.labelOffset=this.labelCount=null}var u=c.prototype,f=[0,0];u.bind=function(t,e,r,n){this.vao.bind(),this.shader.bind();var i=this.shader.uniforms;i.model=t,i.view=e,i.projection=r,i.pixelScale=n,f[0]=this.gl.drawingBufferWidth,f[1]=this.gl.drawingBufferHeight,this.shader.uniforms.resolution=f},u.unbind=function(){this.vao.unbind()},u.update=function(t,e,r,n,i){this.gl;var o=[];function s(t,e,r,n){var i=l[r];i||(i=l[r]={});var s=i[e];s||(s=i[e]=function(t,e){try{return a(t,e)}catch(t){return console.warn("error vectorizing text:",t),{cells:[],positions:[]}}}(e,{triangles:!0,font:r,textAlign:"center",textBaseline:"middle"}));for(var c=(n||12)/12,u=s.positions,f=s.cells,h=0,p=f.length;h=0;--g){var m=u[d[g]];o.push(c*m[0],-c*m[1],t)}}for(var c=[0,0,0],u=[0,0,0],f=[0,0,0],h=[0,0,0],p=0;p<3;++p){f[p]=o.length/3|0,s(.5*(t[0][p]+t[1][p]),e[p],r),h[p]=(o.length/3|0)-f[p],c[p]=o.length/3|0;for(var d=0;d=0&&(i=r.length-n-1);var a=Math.pow(10,i),o=Math.round(t*e*a),s=o+"";if(s.indexOf("e")>=0)return s;var l=o/a,c=o%a;o<0?(l=0|-Math.ceil(l),c=0|-c):(l=0|Math.floor(l),c|=0);var u=""+l;if(o<0&&(u="-"+u),i){for(var f=""+c;f.length=t[0][i];--o)a.push({x:o*e[i],text:n(e[i],o)});r.push(a)}return r},r.equal=function(t,e){for(var r=0;r<3;++r){if(t[r].length!==e[r].length)return!1;for(var n=0;nr)throw new Error("gl-buffer: If resizing buffer, must not specify offset");return t.bufferSubData(e,a,i),r}function u(t,e){for(var r=n.malloc(t.length,e),i=t.length,a=0;a=0;--n){if(e[n]!==r)return!1;r*=t[n]}return!0}(t.shape,t.stride))0===t.offset&&t.data.length===t.shape[0]?this.length=c(this.gl,this.type,this.length,this.usage,t.data,e):this.length=c(this.gl,this.type,this.length,this.usage,t.data.subarray(t.offset,t.shape[0]),e);else{var s=n.malloc(t.size,r),l=a(s,t.shape);i.assign(l,t),this.length=c(this.gl,this.type,this.length,this.usage,e<0?s:s.subarray(0,t.size),e),n.free(s)}}else if(Array.isArray(t)){var f;f=this.type===this.gl.ELEMENT_ARRAY_BUFFER?u(t,"uint16"):u(t,"float32"),this.length=c(this.gl,this.type,this.length,this.usage,e<0?f:f.subarray(0,t.length),e),n.free(f)}else if("object"==typeof t&&"number"==typeof t.length)this.length=c(this.gl,this.type,this.length,this.usage,t,e);else{if("number"!=typeof t&&void 0!==t)throw new Error("gl-buffer: Invalid data type");if(e>=0)throw new Error("gl-buffer: Cannot specify offset when resizing buffer");(t|=0)<=0&&(t=1),this.gl.bufferData(this.type,0|t,this.usage),this.length=t}},e.exports=function(t,e,r,n){if(r=r||t.ARRAY_BUFFER,n=n||t.DYNAMIC_DRAW,r!==t.ARRAY_BUFFER&&r!==t.ELEMENT_ARRAY_BUFFER)throw new Error("gl-buffer: Invalid type for webgl buffer, must be either gl.ARRAY_BUFFER or gl.ELEMENT_ARRAY_BUFFER");if(n!==t.DYNAMIC_DRAW&&n!==t.STATIC_DRAW&&n!==t.STREAM_DRAW)throw new Error("gl-buffer: Invalid usage for buffer, must be either gl.DYNAMIC_DRAW, gl.STATIC_DRAW or gl.STREAM_DRAW");var i=t.createBuffer(),a=new s(t,r,i,0,n);return a.update(e),a}},{ndarray:393,"ndarray-ops":387,"typedarray-pool":481}],212:[function(t,e,r){"use strict";var n=t("gl-vec3"),i=(t("gl-vec4"),function(t,e){for(var r=0;r=e)return r-1;return r}),a=n.create(),o=n.create(),s=function(t,e,r){return tr?r:t},l=function(t,e,r,l){var c=t[0],u=t[1],f=t[2],h=r[0].length,p=r[1].length,d=r[2].length,g=i(r[0],c),m=i(r[1],u),v=i(r[2],f),y=g+1,x=m+1,b=v+1;if(l&&(g=s(g,0,h-1),y=s(y,0,h-1),m=s(m,0,p-1),x=s(x,0,p-1),v=s(v,0,d-1),b=s(b,0,d-1)),g<0||m<0||v<0||y>=h||x>=p||b>=d)return n.create();var _=(c-r[0][g])/(r[0][y]-r[0][g]),w=(u-r[1][m])/(r[1][x]-r[1][m]),k=(f-r[2][v])/(r[2][b]-r[2][v]);(_<0||_>1||isNaN(_))&&(_=0),(w<0||w>1||isNaN(w))&&(w=0),(k<0||k>1||isNaN(k))&&(k=0);var M=v*h*p,A=b*h*p,T=m*h,S=x*h,C=g,E=y,L=e[T+M+C],z=e[T+M+E],P=e[S+M+C],D=e[S+M+E],O=e[T+A+C],I=e[T+A+E],R=e[S+A+C],B=e[S+A+E],F=n.create();return n.lerp(F,L,z,_),n.lerp(a,P,D,_),n.lerp(F,F,a,w),n.lerp(a,O,I,_),n.lerp(o,R,B,_),n.lerp(a,a,o,w),n.lerp(F,F,a,k),F};e.exports=function(t,e){var r;r=t.positions?t.positions:function(t){for(var e=t[0],r=t[1],n=t[2],i=[],a=0;as&&(s=n.length(b)),x&&(y=Math.min(y,2*n.distance(g,_)/(n.length(m)+n.length(b)))),g=_,m=b,v.push(b)}var w=[c,f,p],k=[u,h,d];e&&(e[0]=w,e[1]=k),0===s&&(s=1);var M=1/s;isFinite(y)&&!isNaN(y)||(y=1),o.vectorScale=y;var A=function(t,e,r){var i=n.create();return void 0!==t&&n.set(i,t,e,r),i}(0,1,0),T=t.coneSize||.5;t.absoluteConeSize&&(T=t.absoluteConeSize*M),o.coneScale=T;x=0;for(var S=0;x1.0001)return null;m+=g[u]}if(Math.abs(m-1)>.001)return null;return[f,function(t,e){for(var r=[0,0,0],n=0;n=1},x.isTransparent=function(){return this.opacity<1},x.pickSlots=1,x.setPickBase=function(t){this.pickId=t},x.highlight=function(t){if(t&&this.contourEnable){for(var e=h(this.cells,this.intensity,t.intensity),r=e.cells,n=e.vertexIds,i=e.vertexWeights,a=r.length,o=p.mallocFloat32(6*a),s=0,l=0;l0&&((f=this.triShader).bind(),f.uniforms=s,this.triangleVAO.bind(),e.drawArrays(e.TRIANGLES,0,3*this.triangleCount),this.triangleVAO.unbind());this.edgeCount>0&&this.lineWidth>0&&((f=this.lineShader).bind(),f.uniforms=s,this.edgeVAO.bind(),e.lineWidth(this.lineWidth),e.drawArrays(e.LINES,0,2*this.edgeCount),this.edgeVAO.unbind());this.pointCount>0&&((f=this.pointShader).bind(),f.uniforms=s,this.pointVAO.bind(),e.drawArrays(e.POINTS,0,this.pointCount),this.pointVAO.unbind());this.contourEnable&&this.contourCount>0&&this.contourLineWidth>0&&((f=this.contourShader).bind(),f.uniforms=s,this.contourVAO.bind(),e.drawArrays(e.LINES,0,this.contourCount),this.contourVAO.unbind())},x.drawPick=function(t){t=t||{};for(var e=this.gl,r=t.model||v,n=t.view||v,i=t.projection||v,a=[[-1e6,-1e6,-1e6],[1e6,1e6,1e6]],o=0;o<3;++o)a[0][o]=Math.max(a[0][o],this.clipBounds[0][o]),a[1][o]=Math.min(a[1][o],this.clipBounds[1][o]);this._model=[].slice.call(r),this._view=[].slice.call(n),this._projection=[].slice.call(i),this._resolution=[e.drawingBufferWidth,e.drawingBufferHeight];var s,l={model:r,view:n,projection:i,clipBounds:a,vectorScale:this.vectorScale,coneScale:this.coneScale,coneOffset:this.coneOffset,pickId:this.pickId/255};((s=this.pickShader).bind(),s.uniforms=l,this.triangleCount>0&&(this.triangleVAO.bind(),e.drawArrays(e.TRIANGLES,0,3*this.triangleCount),this.triangleVAO.unbind()),this.edgeCount>0&&(this.edgeVAO.bind(),e.lineWidth(this.lineWidth),e.drawArrays(e.LINES,0,2*this.edgeCount),this.edgeVAO.unbind()),this.pointCount>0)&&((s=this.pointPickShader).bind(),s.uniforms=l,this.pointVAO.bind(),e.drawArrays(e.POINTS,0,this.pointCount),this.pointVAO.unbind())},x.pick=function(t){if(!t)return null;if(t.id!==this.pickId)return null;var e=t.value[0]+256*t.value[1]+65536*t.value[2],r=this.cells[e],n=this.positions[r[1]].slice(0,3);return{index:Math.floor(r[1]/48),position:n,dataCoordinate:n}},x.dispose=function(){this.texture.dispose(),this.triShader.dispose(),this.pickShader.dispose(),this.triangleVAO.dispose(),this.trianglePositions.dispose(),this.triangleVectors.dispose(),this.triangleColors.dispose(),this.triangleUVs.dispose(),this.triangleNormals.dispose(),this.triangleIds.dispose(),this.edgeVAO.dispose(),this.edgePositions.dispose(),this.edgeColors.dispose(),this.edgeUVs.dispose(),this.edgeIds.dispose(),this.pointVAO.dispose(),this.pointPositions.dispose(),this.pointColors.dispose(),this.pointUVs.dispose(),this.pointSizes.dispose(),this.pointIds.dispose(),this.contourVAO.dispose(),this.contourPositions.dispose()},e.exports=function(t,e){1===arguments.length&&(t=(e=t).gl);var r=e.triShader||function(t){var e=n(t,g.vertex,g.fragment,null,g.attributes);return e.attributes.position.location=0,e.attributes.color.location=2,e.attributes.uv.location=3,e.attributes.vector.location=5,e}(t),s=b(t),l=o(t,u(new Uint8Array([255,255,255,255]),[1,1,4]));l.generateMipmap(),l.minFilter=t.LINEAR_MIPMAP_LINEAR,l.magFilter=t.LINEAR;var c=i(t),f=i(t),h=i(t),p=i(t),d=i(t),m=i(t),v=a(t,[{buffer:c,type:t.FLOAT,size:4},{buffer:m,type:t.UNSIGNED_BYTE,size:4,normalized:!0},{buffer:h,type:t.FLOAT,size:4},{buffer:p,type:t.FLOAT,size:2},{buffer:d,type:t.FLOAT,size:3},{buffer:f,type:t.FLOAT,size:3}]),x=i(t),_=i(t),w=i(t),k=i(t),M=a(t,[{buffer:x,type:t.FLOAT,size:3},{buffer:k,type:t.UNSIGNED_BYTE,size:4,normalized:!0},{buffer:_,type:t.FLOAT,size:4},{buffer:w,type:t.FLOAT,size:2}]),A=i(t),T=i(t),S=i(t),C=i(t),E=i(t),L=a(t,[{buffer:A,type:t.FLOAT,size:3},{buffer:E,type:t.UNSIGNED_BYTE,size:4,normalized:!0},{buffer:T,type:t.FLOAT,size:4},{buffer:S,type:t.FLOAT,size:2},{buffer:C,type:t.FLOAT,size:1}]),z=i(t),P=new y(t,l,r,null,null,s,null,null,c,f,m,h,p,d,v,x,k,_,w,M,A,E,T,S,C,L,z,a(t,[{buffer:z,type:t.FLOAT,size:3}]));return P.update(e),P}},{"./closest-point":213,"./shaders":215,colormap:107,"gl-buffer":211,"gl-mat4/invert":235,"gl-mat4/multiply":237,"gl-shader":268,"gl-texture2d":280,"gl-vao":284,ndarray:393,normals:396,"simplicial-complex-contour":454,"typedarray-pool":481}],215:[function(t,e,r){var n=t("glslify"),i=n(["precision mediump float;\n#define GLSLIFY 1\n\nfloat inverse(float m) {\n return 1.0 / m;\n}\n\nmat2 inverse(mat2 m) {\n return mat2(m[1][1],-m[0][1],\n -m[1][0], m[0][0]) / (m[0][0]*m[1][1] - m[0][1]*m[1][0]);\n}\n\nmat3 inverse(mat3 m) {\n float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];\n float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];\n float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];\n\n float b01 = a22 * a11 - a12 * a21;\n float b11 = -a22 * a10 + a12 * a20;\n float b21 = a21 * a10 - a11 * a20;\n\n float det = a00 * b01 + a01 * b11 + a02 * b21;\n\n return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11),\n b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10),\n b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det;\n}\n\nmat4 inverse(mat4 m) {\n float\n a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3],\n a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3],\n a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3],\n a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3],\n\n b00 = a00 * a11 - a01 * a10,\n b01 = a00 * a12 - a02 * a10,\n b02 = a00 * a13 - a03 * a10,\n b03 = a01 * a12 - a02 * a11,\n b04 = a01 * a13 - a03 * a11,\n b05 = a02 * a13 - a03 * a12,\n b06 = a20 * a31 - a21 * a30,\n b07 = a20 * a32 - a22 * a30,\n b08 = a20 * a33 - a23 * a30,\n b09 = a21 * a32 - a22 * a31,\n b10 = a21 * a33 - a23 * a31,\n b11 = a22 * a33 - a23 * a32,\n\n det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n\n return mat4(\n a11 * b11 - a12 * b10 + a13 * b09,\n a02 * b10 - a01 * b11 - a03 * b09,\n a31 * b05 - a32 * b04 + a33 * b03,\n a22 * b04 - a21 * b05 - a23 * b03,\n a12 * b08 - a10 * b11 - a13 * b07,\n a00 * b11 - a02 * b08 + a03 * b07,\n a32 * b02 - a30 * b05 - a33 * b01,\n a20 * b05 - a22 * b02 + a23 * b01,\n a10 * b10 - a11 * b08 + a13 * b06,\n a01 * b08 - a00 * b10 - a03 * b06,\n a30 * b04 - a31 * b02 + a33 * b00,\n a21 * b02 - a20 * b04 - a23 * b00,\n a11 * b07 - a10 * b09 - a12 * b06,\n a00 * b09 - a01 * b07 + a02 * b06,\n a31 * b01 - a30 * b03 - a32 * b00,\n a20 * b03 - a21 * b01 + a22 * b00) / det;\n}\n\nvec3 getOrthogonalVector(vec3 v) {\n // Return up-vector for only-z vector.\n // Return ax + by + cz = 0, a point that lies on the plane that has v as a normal and that isn't (0,0,0).\n // From the above if-statement we have ||a|| > 0 U ||b|| > 0.\n // Assign z = 0, x = -b, y = a:\n // a*-b + b*a + c*0 = -ba + ba + 0 = 0\n if (v.x*v.x > v.z*v.z || v.y*v.y > v.z*v.z) {\n return normalize(vec3(-v.y, v.x, 0.0));\n } else {\n return normalize(vec3(0.0, v.z, -v.y));\n }\n}\n\n// Calculate the cone vertex and normal at the given index.\n//\n// The returned vertex is for a cone with its top at origin and height of 1.0,\n// pointing in the direction of the vector attribute.\n//\n// Each cone is made up of a top vertex, a center base vertex and base perimeter vertices.\n// These vertices are used to make up the triangles of the cone by the following:\n// segment + 0 top vertex\n// segment + 1 perimeter vertex a+1\n// segment + 2 perimeter vertex a\n// segment + 3 center base vertex\n// segment + 4 perimeter vertex a\n// segment + 5 perimeter vertex a+1\n// Where segment is the number of the radial segment * 6 and a is the angle at that radial segment.\n// To go from index to segment, floor(index / 6)\n// To go from segment to angle, 2*pi * (segment/segmentCount)\n// To go from index to segment index, index - (segment*6)\n//\nvec3 getConePosition(vec3 d, float index, float coneOffset, out vec3 normal) {\n\n const float segmentCount = 8.0;\n\n index = mod(index, segmentCount * 6.0);\n\n float segment = floor(index/6.0);\n float segmentIndex = index - (segment*6.0);\n\n normal = -normalize(d);\n\n if (segmentIndex == 3.0) {\n return mix(vec3(0.0), -d, coneOffset);\n }\n\n // angle = 2pi * ((segment + ((segmentIndex == 1.0 || segmentIndex == 5.0) ? 1.0 : 0.0)) / segmentCount)\n float nextAngle = float(segmentIndex == 1.0 || segmentIndex == 5.0);\n float angle = 2.0 * 3.14159 * ((segment + nextAngle) / segmentCount);\n\n vec3 v1 = mix(d, vec3(0.0), coneOffset);\n vec3 v2 = v1 - d;\n\n vec3 u = getOrthogonalVector(d);\n vec3 v = normalize(cross(u, d));\n\n vec3 x = u * cos(angle) * length(d)*0.25;\n vec3 y = v * sin(angle) * length(d)*0.25;\n vec3 v3 = v2 + x + y;\n if (segmentIndex <= 2.0) {\n vec3 tx = u * sin(angle);\n vec3 ty = v * -cos(angle);\n vec3 tangent = tx + ty;\n normal = normalize(cross(v3 - v1, tangent));\n }\n\n if (segmentIndex == 0.0) {\n return mix(d, vec3(0.0), coneOffset);\n }\n return v3;\n}\n\nattribute vec3 vector;\nattribute vec4 color, position;\nattribute vec2 uv;\nuniform float vectorScale;\nuniform float coneScale;\n\nuniform float coneOffset;\n\nuniform mat4 model\n , view\n , projection;\nuniform vec3 eyePosition\n , lightPosition;\n\nvarying vec3 f_normal\n , f_lightDirection\n , f_eyeDirection\n , f_data;\nvarying vec4 f_color;\nvarying vec2 f_uv;\n\nvoid main() {\n // Scale the vector magnitude to stay constant with\n // model & view changes.\n vec3 normal;\n vec4 conePosition = model * vec4(position.xyz, 1.0) + vec4(getConePosition(mat3(model) * ((vectorScale * coneScale) * vector), position.w, coneOffset, normal), 0.0);\n normal = normalize(normal * inverse(mat3(model)));\n\n // vec4 m_position = model * vec4(conePosition, 1.0);\n vec4 t_position = view * conePosition;\n gl_Position = projection * t_position;\n f_color = color; //vec4(position.w, color.r, 0, 0);\n f_normal = normal;\n f_data = conePosition.xyz;\n f_eyeDirection = eyePosition - conePosition.xyz;\n f_lightDirection = lightPosition - conePosition.xyz;\n f_uv = uv;\n}\n"]),a=n(["precision mediump float;\n#define GLSLIFY 1\n\nfloat beckmannDistribution(float x, float roughness) {\n float NdotH = max(x, 0.0001);\n float cos2Alpha = NdotH * NdotH;\n float tan2Alpha = (cos2Alpha - 1.0) / cos2Alpha;\n float roughness2 = roughness * roughness;\n float denom = 3.141592653589793 * roughness2 * cos2Alpha * cos2Alpha;\n return exp(tan2Alpha / roughness2) / denom;\n}\n\nfloat cookTorranceSpecular(\n vec3 lightDirection,\n vec3 viewDirection,\n vec3 surfaceNormal,\n float roughness,\n float fresnel) {\n\n float VdotN = max(dot(viewDirection, surfaceNormal), 0.0);\n float LdotN = max(dot(lightDirection, surfaceNormal), 0.0);\n\n //Half angle vector\n vec3 H = normalize(lightDirection + viewDirection);\n\n //Geometric term\n float NdotH = max(dot(surfaceNormal, H), 0.0);\n float VdotH = max(dot(viewDirection, H), 0.000001);\n float LdotH = max(dot(lightDirection, H), 0.000001);\n float G1 = (2.0 * NdotH * VdotN) / VdotH;\n float G2 = (2.0 * NdotH * LdotN) / LdotH;\n float G = min(1.0, min(G1, G2));\n \n //Distribution term\n float D = beckmannDistribution(NdotH, roughness);\n\n //Fresnel term\n float F = pow(1.0 - VdotN, fresnel);\n\n //Multiply terms and done\n return G * F * D / max(3.14159265 * VdotN, 0.000001);\n}\n\nuniform vec3 clipBounds[2];\nuniform float roughness\n , fresnel\n , kambient\n , kdiffuse\n , kspecular\n , opacity;\nuniform sampler2D texture;\n\nvarying vec3 f_normal\n , f_lightDirection\n , f_eyeDirection\n , f_data;\nvarying vec4 f_color;\nvarying vec2 f_uv;\n\nvoid main() {\n //if(any(lessThan(f_data, clipBounds[0])) || \n // any(greaterThan(f_data, clipBounds[1]))) {\n // discard;\n //}\n\n vec3 N = normalize(f_normal);\n vec3 L = normalize(f_lightDirection);\n vec3 V = normalize(f_eyeDirection);\n \n if(!gl_FrontFacing) {\n N = -N;\n }\n\n float specular = cookTorranceSpecular(L, V, N, roughness, fresnel);\n float diffuse = min(kambient + kdiffuse * max(dot(N, L), 0.0), 1.0);\n\n vec4 surfaceColor = texture2D(texture, f_uv);\n vec4 litColor = surfaceColor.a * vec4(diffuse * surfaceColor.rgb + kspecular * vec3(1,1,1) * specular, 1.0);\n\n gl_FragColor = litColor * opacity;\n}"]),o=n(["precision mediump float;\n#define GLSLIFY 1\n\nvec3 getOrthogonalVector(vec3 v) {\n // Return up-vector for only-z vector.\n // Return ax + by + cz = 0, a point that lies on the plane that has v as a normal and that isn't (0,0,0).\n // From the above if-statement we have ||a|| > 0 U ||b|| > 0.\n // Assign z = 0, x = -b, y = a:\n // a*-b + b*a + c*0 = -ba + ba + 0 = 0\n if (v.x*v.x > v.z*v.z || v.y*v.y > v.z*v.z) {\n return normalize(vec3(-v.y, v.x, 0.0));\n } else {\n return normalize(vec3(0.0, v.z, -v.y));\n }\n}\n\n// Calculate the cone vertex and normal at the given index.\n//\n// The returned vertex is for a cone with its top at origin and height of 1.0,\n// pointing in the direction of the vector attribute.\n//\n// Each cone is made up of a top vertex, a center base vertex and base perimeter vertices.\n// These vertices are used to make up the triangles of the cone by the following:\n// segment + 0 top vertex\n// segment + 1 perimeter vertex a+1\n// segment + 2 perimeter vertex a\n// segment + 3 center base vertex\n// segment + 4 perimeter vertex a\n// segment + 5 perimeter vertex a+1\n// Where segment is the number of the radial segment * 6 and a is the angle at that radial segment.\n// To go from index to segment, floor(index / 6)\n// To go from segment to angle, 2*pi * (segment/segmentCount)\n// To go from index to segment index, index - (segment*6)\n//\nvec3 getConePosition(vec3 d, float index, float coneOffset, out vec3 normal) {\n\n const float segmentCount = 8.0;\n\n index = mod(index, segmentCount * 6.0);\n\n float segment = floor(index/6.0);\n float segmentIndex = index - (segment*6.0);\n\n normal = -normalize(d);\n\n if (segmentIndex == 3.0) {\n return mix(vec3(0.0), -d, coneOffset);\n }\n\n // angle = 2pi * ((segment + ((segmentIndex == 1.0 || segmentIndex == 5.0) ? 1.0 : 0.0)) / segmentCount)\n float nextAngle = float(segmentIndex == 1.0 || segmentIndex == 5.0);\n float angle = 2.0 * 3.14159 * ((segment + nextAngle) / segmentCount);\n\n vec3 v1 = mix(d, vec3(0.0), coneOffset);\n vec3 v2 = v1 - d;\n\n vec3 u = getOrthogonalVector(d);\n vec3 v = normalize(cross(u, d));\n\n vec3 x = u * cos(angle) * length(d)*0.25;\n vec3 y = v * sin(angle) * length(d)*0.25;\n vec3 v3 = v2 + x + y;\n if (segmentIndex <= 2.0) {\n vec3 tx = u * sin(angle);\n vec3 ty = v * -cos(angle);\n vec3 tangent = tx + ty;\n normal = normalize(cross(v3 - v1, tangent));\n }\n\n if (segmentIndex == 0.0) {\n return mix(d, vec3(0.0), coneOffset);\n }\n return v3;\n}\n\nattribute vec3 vector;\nattribute vec4 position;\nattribute vec4 id;\n\nuniform mat4 model, view, projection;\n\nuniform float vectorScale;\nuniform float coneScale;\nuniform float coneOffset;\n\nvarying vec3 f_position;\nvarying vec4 f_id;\n\nvoid main() {\n vec3 normal;\n vec4 conePosition = model * vec4(position.xyz, 1.0) + vec4(getConePosition(mat3(model) * ((vectorScale * coneScale) * vector), position.w, coneOffset, normal), 0.0);\n gl_Position = projection * view * conePosition;\n f_id = id;\n f_position = position.xyz;\n}\n"]),s=n(["precision mediump float;\n#define GLSLIFY 1\n\nuniform vec3 clipBounds[2];\nuniform float pickId;\n\nvarying vec3 f_position;\nvarying vec4 f_id;\n\nvoid main() {\n if(any(lessThan(f_position, clipBounds[0])) || \n any(greaterThan(f_position, clipBounds[1]))) {\n discard;\n }\n gl_FragColor = vec4(pickId, f_id.xyz);\n}"]);r.meshShader={vertex:i,fragment:a,attributes:[{name:"position",type:"vec4"},{name:"normal",type:"vec3"},{name:"color",type:"vec4"},{name:"uv",type:"vec2"},{name:"vector",type:"vec3"}]},r.pickShader={vertex:o,fragment:s,attributes:[{name:"position",type:"vec4"},{name:"id",type:"vec4"},{name:"vector",type:"vec3"}]}},{glslify:353}],216:[function(t,e,r){e.exports={0:"NONE",1:"ONE",2:"LINE_LOOP",3:"LINE_STRIP",4:"TRIANGLES",5:"TRIANGLE_STRIP",6:"TRIANGLE_FAN",256:"DEPTH_BUFFER_BIT",512:"NEVER",513:"LESS",514:"EQUAL",515:"LEQUAL",516:"GREATER",517:"NOTEQUAL",518:"GEQUAL",519:"ALWAYS",768:"SRC_COLOR",769:"ONE_MINUS_SRC_COLOR",770:"SRC_ALPHA",771:"ONE_MINUS_SRC_ALPHA",772:"DST_ALPHA",773:"ONE_MINUS_DST_ALPHA",774:"DST_COLOR",775:"ONE_MINUS_DST_COLOR",776:"SRC_ALPHA_SATURATE",1024:"STENCIL_BUFFER_BIT",1028:"FRONT",1029:"BACK",1032:"FRONT_AND_BACK",1280:"INVALID_ENUM",1281:"INVALID_VALUE",1282:"INVALID_OPERATION",1285:"OUT_OF_MEMORY",1286:"INVALID_FRAMEBUFFER_OPERATION",2304:"CW",2305:"CCW",2849:"LINE_WIDTH",2884:"CULL_FACE",2885:"CULL_FACE_MODE",2886:"FRONT_FACE",2928:"DEPTH_RANGE",2929:"DEPTH_TEST",2930:"DEPTH_WRITEMASK",2931:"DEPTH_CLEAR_VALUE",2932:"DEPTH_FUNC",2960:"STENCIL_TEST",2961:"STENCIL_CLEAR_VALUE",2962:"STENCIL_FUNC",2963:"STENCIL_VALUE_MASK",2964:"STENCIL_FAIL",2965:"STENCIL_PASS_DEPTH_FAIL",2966:"STENCIL_PASS_DEPTH_PASS",2967:"STENCIL_REF",2968:"STENCIL_WRITEMASK",2978:"VIEWPORT",3024:"DITHER",3042:"BLEND",3088:"SCISSOR_BOX",3089:"SCISSOR_TEST",3106:"COLOR_CLEAR_VALUE",3107:"COLOR_WRITEMASK",3317:"UNPACK_ALIGNMENT",3333:"PACK_ALIGNMENT",3379:"MAX_TEXTURE_SIZE",3386:"MAX_VIEWPORT_DIMS",3408:"SUBPIXEL_BITS",3410:"RED_BITS",3411:"GREEN_BITS",3412:"BLUE_BITS",3413:"ALPHA_BITS",3414:"DEPTH_BITS",3415:"STENCIL_BITS",3553:"TEXTURE_2D",4352:"DONT_CARE",4353:"FASTEST",4354:"NICEST",5120:"BYTE",5121:"UNSIGNED_BYTE",5122:"SHORT",5123:"UNSIGNED_SHORT",5124:"INT",5125:"UNSIGNED_INT",5126:"FLOAT",5386:"INVERT",5890:"TEXTURE",6401:"STENCIL_INDEX",6402:"DEPTH_COMPONENT",6406:"ALPHA",6407:"RGB",6408:"RGBA",6409:"LUMINANCE",6410:"LUMINANCE_ALPHA",7680:"KEEP",7681:"REPLACE",7682:"INCR",7683:"DECR",7936:"VENDOR",7937:"RENDERER",7938:"VERSION",9728:"NEAREST",9729:"LINEAR",9984:"NEAREST_MIPMAP_NEAREST",9985:"LINEAR_MIPMAP_NEAREST",9986:"NEAREST_MIPMAP_LINEAR",9987:"LINEAR_MIPMAP_LINEAR",10240:"TEXTURE_MAG_FILTER",10241:"TEXTURE_MIN_FILTER",10242:"TEXTURE_WRAP_S",10243:"TEXTURE_WRAP_T",10497:"REPEAT",10752:"POLYGON_OFFSET_UNITS",16384:"COLOR_BUFFER_BIT",32769:"CONSTANT_COLOR",32770:"ONE_MINUS_CONSTANT_COLOR",32771:"CONSTANT_ALPHA",32772:"ONE_MINUS_CONSTANT_ALPHA",32773:"BLEND_COLOR",32774:"FUNC_ADD",32777:"BLEND_EQUATION_RGB",32778:"FUNC_SUBTRACT",32779:"FUNC_REVERSE_SUBTRACT",32819:"UNSIGNED_SHORT_4_4_4_4",32820:"UNSIGNED_SHORT_5_5_5_1",32823:"POLYGON_OFFSET_FILL",32824:"POLYGON_OFFSET_FACTOR",32854:"RGBA4",32855:"RGB5_A1",32873:"TEXTURE_BINDING_2D",32926:"SAMPLE_ALPHA_TO_COVERAGE",32928:"SAMPLE_COVERAGE",32936:"SAMPLE_BUFFERS",32937:"SAMPLES",32938:"SAMPLE_COVERAGE_VALUE",32939:"SAMPLE_COVERAGE_INVERT",32968:"BLEND_DST_RGB",32969:"BLEND_SRC_RGB",32970:"BLEND_DST_ALPHA",32971:"BLEND_SRC_ALPHA",33071:"CLAMP_TO_EDGE",33170:"GENERATE_MIPMAP_HINT",33189:"DEPTH_COMPONENT16",33306:"DEPTH_STENCIL_ATTACHMENT",33635:"UNSIGNED_SHORT_5_6_5",33648:"MIRRORED_REPEAT",33901:"ALIASED_POINT_SIZE_RANGE",33902:"ALIASED_LINE_WIDTH_RANGE",33984:"TEXTURE0",33985:"TEXTURE1",33986:"TEXTURE2",33987:"TEXTURE3",33988:"TEXTURE4",33989:"TEXTURE5",33990:"TEXTURE6",33991:"TEXTURE7",33992:"TEXTURE8",33993:"TEXTURE9",33994:"TEXTURE10",33995:"TEXTURE11",33996:"TEXTURE12",33997:"TEXTURE13",33998:"TEXTURE14",33999:"TEXTURE15",34000:"TEXTURE16",34001:"TEXTURE17",34002:"TEXTURE18",34003:"TEXTURE19",34004:"TEXTURE20",34005:"TEXTURE21",34006:"TEXTURE22",34007:"TEXTURE23",34008:"TEXTURE24",34009:"TEXTURE25",34010:"TEXTURE26",34011:"TEXTURE27",34012:"TEXTURE28",34013:"TEXTURE29",34014:"TEXTURE30",34015:"TEXTURE31",34016:"ACTIVE_TEXTURE",34024:"MAX_RENDERBUFFER_SIZE",34041:"DEPTH_STENCIL",34055:"INCR_WRAP",34056:"DECR_WRAP",34067:"TEXTURE_CUBE_MAP",34068:"TEXTURE_BINDING_CUBE_MAP",34069:"TEXTURE_CUBE_MAP_POSITIVE_X",34070:"TEXTURE_CUBE_MAP_NEGATIVE_X",34071:"TEXTURE_CUBE_MAP_POSITIVE_Y",34072:"TEXTURE_CUBE_MAP_NEGATIVE_Y",34073:"TEXTURE_CUBE_MAP_POSITIVE_Z",34074:"TEXTURE_CUBE_MAP_NEGATIVE_Z",34076:"MAX_CUBE_MAP_TEXTURE_SIZE",34338:"VERTEX_ATTRIB_ARRAY_ENABLED",34339:"VERTEX_ATTRIB_ARRAY_SIZE",34340:"VERTEX_ATTRIB_ARRAY_STRIDE",34341:"VERTEX_ATTRIB_ARRAY_TYPE",34342:"CURRENT_VERTEX_ATTRIB",34373:"VERTEX_ATTRIB_ARRAY_POINTER",34466:"NUM_COMPRESSED_TEXTURE_FORMATS",34467:"COMPRESSED_TEXTURE_FORMATS",34660:"BUFFER_SIZE",34661:"BUFFER_USAGE",34816:"STENCIL_BACK_FUNC",34817:"STENCIL_BACK_FAIL",34818:"STENCIL_BACK_PASS_DEPTH_FAIL",34819:"STENCIL_BACK_PASS_DEPTH_PASS",34877:"BLEND_EQUATION_ALPHA",34921:"MAX_VERTEX_ATTRIBS",34922:"VERTEX_ATTRIB_ARRAY_NORMALIZED",34930:"MAX_TEXTURE_IMAGE_UNITS",34962:"ARRAY_BUFFER",34963:"ELEMENT_ARRAY_BUFFER",34964:"ARRAY_BUFFER_BINDING",34965:"ELEMENT_ARRAY_BUFFER_BINDING",34975:"VERTEX_ATTRIB_ARRAY_BUFFER_BINDING",35040:"STREAM_DRAW",35044:"STATIC_DRAW",35048:"DYNAMIC_DRAW",35632:"FRAGMENT_SHADER",35633:"VERTEX_SHADER",35660:"MAX_VERTEX_TEXTURE_IMAGE_UNITS",35661:"MAX_COMBINED_TEXTURE_IMAGE_UNITS",35663:"SHADER_TYPE",35664:"FLOAT_VEC2",35665:"FLOAT_VEC3",35666:"FLOAT_VEC4",35667:"INT_VEC2",35668:"INT_VEC3",35669:"INT_VEC4",35670:"BOOL",35671:"BOOL_VEC2",35672:"BOOL_VEC3",35673:"BOOL_VEC4",35674:"FLOAT_MAT2",35675:"FLOAT_MAT3",35676:"FLOAT_MAT4",35678:"SAMPLER_2D",35680:"SAMPLER_CUBE",35712:"DELETE_STATUS",35713:"COMPILE_STATUS",35714:"LINK_STATUS",35715:"VALIDATE_STATUS",35716:"INFO_LOG_LENGTH",35717:"ATTACHED_SHADERS",35718:"ACTIVE_UNIFORMS",35719:"ACTIVE_UNIFORM_MAX_LENGTH",35720:"SHADER_SOURCE_LENGTH",35721:"ACTIVE_ATTRIBUTES",35722:"ACTIVE_ATTRIBUTE_MAX_LENGTH",35724:"SHADING_LANGUAGE_VERSION",35725:"CURRENT_PROGRAM",36003:"STENCIL_BACK_REF",36004:"STENCIL_BACK_VALUE_MASK",36005:"STENCIL_BACK_WRITEMASK",36006:"FRAMEBUFFER_BINDING",36007:"RENDERBUFFER_BINDING",36048:"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",36049:"FRAMEBUFFER_ATTACHMENT_OBJECT_NAME",36050:"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL",36051:"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE",36053:"FRAMEBUFFER_COMPLETE",36054:"FRAMEBUFFER_INCOMPLETE_ATTACHMENT",36055:"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",36057:"FRAMEBUFFER_INCOMPLETE_DIMENSIONS",36061:"FRAMEBUFFER_UNSUPPORTED",36064:"COLOR_ATTACHMENT0",36096:"DEPTH_ATTACHMENT",36128:"STENCIL_ATTACHMENT",36160:"FRAMEBUFFER",36161:"RENDERBUFFER",36162:"RENDERBUFFER_WIDTH",36163:"RENDERBUFFER_HEIGHT",36164:"RENDERBUFFER_INTERNAL_FORMAT",36168:"STENCIL_INDEX8",36176:"RENDERBUFFER_RED_SIZE",36177:"RENDERBUFFER_GREEN_SIZE",36178:"RENDERBUFFER_BLUE_SIZE",36179:"RENDERBUFFER_ALPHA_SIZE",36180:"RENDERBUFFER_DEPTH_SIZE",36181:"RENDERBUFFER_STENCIL_SIZE",36194:"RGB565",36336:"LOW_FLOAT",36337:"MEDIUM_FLOAT",36338:"HIGH_FLOAT",36339:"LOW_INT",36340:"MEDIUM_INT",36341:"HIGH_INT",36346:"SHADER_COMPILER",36347:"MAX_VERTEX_UNIFORM_VECTORS",36348:"MAX_VARYING_VECTORS",36349:"MAX_FRAGMENT_UNIFORM_VECTORS",37440:"UNPACK_FLIP_Y_WEBGL",37441:"UNPACK_PREMULTIPLY_ALPHA_WEBGL",37442:"CONTEXT_LOST_WEBGL",37443:"UNPACK_COLORSPACE_CONVERSION_WEBGL",37444:"BROWSER_DEFAULT_WEBGL"}},{}],217:[function(t,e,r){var n=t("./1.0/numbers");e.exports=function(t){return n[t]}},{"./1.0/numbers":216}],218:[function(t,e,r){"use strict";e.exports=function(t){var e=t.gl,r=n(e),o=i(e,[{buffer:r,type:e.FLOAT,size:3,offset:0,stride:40},{buffer:r,type:e.FLOAT,size:4,offset:12,stride:40},{buffer:r,type:e.FLOAT,size:3,offset:28,stride:40}]),l=a(e);l.attributes.position.location=0,l.attributes.color.location=1,l.attributes.offset.location=2;var c=new s(e,r,o,l);return c.update(t),c};var n=t("gl-buffer"),i=t("gl-vao"),a=t("./shaders/index"),o=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];function s(t,e,r,n){this.gl=t,this.shader=n,this.buffer=e,this.vao=r,this.pixelRatio=1,this.bounds=[[1/0,1/0,1/0],[-1/0,-1/0,-1/0]],this.clipBounds=[[-1/0,-1/0,-1/0],[1/0,1/0,1/0]],this.lineWidth=[1,1,1],this.capSize=[10,10,10],this.lineCount=[0,0,0],this.lineOffset=[0,0,0],this.opacity=1}var l=s.prototype;function c(t,e){for(var r=0;r<3;++r)t[0][r]=Math.min(t[0][r],e[r]),t[1][r]=Math.max(t[1][r],e[r])}l.isOpaque=function(){return this.opacity>=1},l.isTransparent=function(){return this.opacity<1},l.drawTransparent=l.draw=function(t){var e=this.gl,r=this.shader.uniforms;this.shader.bind();var n=r.view=t.view||o,i=r.projection=t.projection||o;r.model=t.model||o,r.clipBounds=this.clipBounds,r.opacity=this.opacity;var a=n[12],s=n[13],l=n[14],c=n[15],u=this.pixelRatio*(i[3]*a+i[7]*s+i[11]*l+i[15]*c)/e.drawingBufferHeight;this.vao.bind();for(var f=0;f<3;++f)e.lineWidth(this.lineWidth[f]),r.capSize=this.capSize[f]*u,this.lineCount[f]&&e.drawArrays(e.LINES,this.lineOffset[f],this.lineCount[f]);this.vao.unbind()};var u=function(){for(var t=new Array(3),e=0;e<3;++e){for(var r=[],n=1;n<=2;++n)for(var i=-1;i<=1;i+=2){var a=[0,0,0];a[(n+e)%3]=i,r.push(a)}t[e]=r}return t}();function f(t,e,r,n){for(var i=u[n],a=0;a0)(g=u.slice())[s]+=p[1][s],i.push(u[0],u[1],u[2],d[0],d[1],d[2],d[3],0,0,0,g[0],g[1],g[2],d[0],d[1],d[2],d[3],0,0,0),c(this.bounds,g),o+=2+f(i,g,d,s)}}this.lineCount[s]=o-this.lineOffset[s]}this.buffer.update(i)}},l.dispose=function(){this.shader.dispose(),this.buffer.dispose(),this.vao.dispose()}},{"./shaders/index":219,"gl-buffer":211,"gl-vao":284}],219:[function(t,e,r){"use strict";var n=t("glslify"),i=t("gl-shader"),a=n(["precision mediump float;\n#define GLSLIFY 1\n\nattribute vec3 position, offset;\nattribute vec4 color;\nuniform mat4 model, view, projection;\nuniform float capSize;\nvarying vec4 fragColor;\nvarying vec3 fragPosition;\n\nvoid main() {\n vec4 worldPosition = model * vec4(position, 1.0);\n worldPosition = (worldPosition / worldPosition.w) + vec4(capSize * offset, 0.0);\n gl_Position = projection * view * worldPosition;\n fragColor = color;\n fragPosition = position;\n}"]),o=n(["precision mediump float;\n#define GLSLIFY 1\nuniform vec3 clipBounds[2];\nuniform float opacity;\nvarying vec3 fragPosition;\nvarying vec4 fragColor;\n\nvoid main() {\n if(any(lessThan(fragPosition, clipBounds[0])) || any(greaterThan(fragPosition, clipBounds[1]))) {\n discard;\n }\n gl_FragColor = opacity * fragColor;\n}"]);e.exports=function(t){return i(t,a,o,null,[{name:"position",type:"vec3"},{name:"color",type:"vec4"},{name:"offset",type:"vec3"}])}},{"gl-shader":268,glslify:353}],220:[function(t,e,r){"use strict";var n=t("gl-texture2d");e.exports=function(t,e,r,n){i||(i=t.FRAMEBUFFER_UNSUPPORTED,a=t.FRAMEBUFFER_INCOMPLETE_ATTACHMENT,o=t.FRAMEBUFFER_INCOMPLETE_DIMENSIONS,s=t.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);var c=t.getExtension("WEBGL_draw_buffers");!l&&c&&function(t,e){var r=t.getParameter(e.MAX_COLOR_ATTACHMENTS_WEBGL);l=new Array(r+1);for(var n=0;n<=r;++n){for(var i=new Array(r),a=0;au||r<0||r>u)throw new Error("gl-fbo: Parameters are too large for FBO");var f=1;if("color"in(n=n||{})){if((f=Math.max(0|n.color,0))<0)throw new Error("gl-fbo: Must specify a nonnegative number of colors");if(f>1){if(!c)throw new Error("gl-fbo: Multiple draw buffer extension not supported");if(f>t.getParameter(c.MAX_COLOR_ATTACHMENTS_WEBGL))throw new Error("gl-fbo: Context does not support "+f+" draw buffers")}}var h=t.UNSIGNED_BYTE,p=t.getExtension("OES_texture_float");if(n.float&&f>0){if(!p)throw new Error("gl-fbo: Context does not support floating point textures");h=t.FLOAT}else n.preferFloat&&f>0&&p&&(h=t.FLOAT);var g=!0;"depth"in n&&(g=!!n.depth);var m=!1;"stencil"in n&&(m=!!n.stencil);return new d(t,e,r,h,f,g,m,c)};var i,a,o,s,l=null;function c(t){return[t.getParameter(t.FRAMEBUFFER_BINDING),t.getParameter(t.RENDERBUFFER_BINDING),t.getParameter(t.TEXTURE_BINDING_2D)]}function u(t,e){t.bindFramebuffer(t.FRAMEBUFFER,e[0]),t.bindRenderbuffer(t.RENDERBUFFER,e[1]),t.bindTexture(t.TEXTURE_2D,e[2])}function f(t){switch(t){case i:throw new Error("gl-fbo: Framebuffer unsupported");case a:throw new Error("gl-fbo: Framebuffer incomplete attachment");case o:throw new Error("gl-fbo: Framebuffer incomplete dimensions");case s:throw new Error("gl-fbo: Framebuffer incomplete missing attachment");default:throw new Error("gl-fbo: Framebuffer failed for unspecified reason")}}function h(t,e,r,i,a,o){if(!i)return null;var s=n(t,e,r,a,i);return s.magFilter=t.NEAREST,s.minFilter=t.NEAREST,s.mipSamples=1,s.bind(),t.framebufferTexture2D(t.FRAMEBUFFER,o,t.TEXTURE_2D,s.handle,0),s}function p(t,e,r,n,i){var a=t.createRenderbuffer();return t.bindRenderbuffer(t.RENDERBUFFER,a),t.renderbufferStorage(t.RENDERBUFFER,n,e,r),t.framebufferRenderbuffer(t.FRAMEBUFFER,i,t.RENDERBUFFER,a),a}function d(t,e,r,n,i,a,o,s){this.gl=t,this._shape=[0|e,0|r],this._destroyed=!1,this._ext=s,this.color=new Array(i);for(var d=0;d1&&s.drawBuffersWEBGL(l[o]);var y=r.getExtension("WEBGL_depth_texture");y?d?t.depth=h(r,i,a,y.UNSIGNED_INT_24_8_WEBGL,r.DEPTH_STENCIL,r.DEPTH_STENCIL_ATTACHMENT):g&&(t.depth=h(r,i,a,r.UNSIGNED_SHORT,r.DEPTH_COMPONENT,r.DEPTH_ATTACHMENT)):g&&d?t._depth_rb=p(r,i,a,r.DEPTH_STENCIL,r.DEPTH_STENCIL_ATTACHMENT):g?t._depth_rb=p(r,i,a,r.DEPTH_COMPONENT16,r.DEPTH_ATTACHMENT):d&&(t._depth_rb=p(r,i,a,r.STENCIL_INDEX,r.STENCIL_ATTACHMENT));var x=r.checkFramebufferStatus(r.FRAMEBUFFER);if(x!==r.FRAMEBUFFER_COMPLETE){for(t._destroyed=!0,r.bindFramebuffer(r.FRAMEBUFFER,null),r.deleteFramebuffer(t.handle),t.handle=null,t.depth&&(t.depth.dispose(),t.depth=null),t._depth_rb&&(r.deleteRenderbuffer(t._depth_rb),t._depth_rb=null),v=0;vi||r<0||r>i)throw new Error("gl-fbo: Can't resize FBO, invalid dimensions");t._shape[0]=e,t._shape[1]=r;for(var a=c(n),o=0;o>8*p&255;this.pickOffset=r,i.bind();var d=i.uniforms;d.viewTransform=t,d.pickOffset=e,d.shape=this.shape;var g=i.attributes;return this.positionBuffer.bind(),g.position.pointer(),this.weightBuffer.bind(),g.weight.pointer(s.UNSIGNED_BYTE,!1),this.idBuffer.bind(),g.pickId.pointer(s.UNSIGNED_BYTE,!1),s.drawArrays(s.TRIANGLES,0,o),r+this.shape[0]*this.shape[1]}}}(),f.pick=function(t,e,r){var n=this.pickOffset,i=this.shape[0]*this.shape[1];if(r=n+i)return null;var a=r-n,o=this.xData,s=this.yData;return{object:this,pointId:a,dataCoord:[o[a%this.shape[0]],s[a/this.shape[0]|0]]}},f.update=function(t){var e=(t=t||{}).shape||[0,0],r=t.x||i(e[0]),o=t.y||i(e[1]),s=t.z||new Float32Array(e[0]*e[1]);this.xData=r,this.yData=o;var l=t.colorLevels||[0],c=t.colorValues||[0,0,0,1],u=l.length,f=this.bounds,p=f[0]=r[0],d=f[1]=o[0],g=1/((f[2]=r[r.length-1])-p),m=1/((f[3]=o[o.length-1])-d),v=e[0],y=e[1];this.shape=[v,y];var x=(v-1)*(y-1)*(h.length>>>1);this.numVertices=x;for(var b=a.mallocUint8(4*x),_=a.mallocFloat32(2*x),w=a.mallocUint8(2*x),k=a.mallocUint32(x),M=0,A=0;A FLOAT_MAX) {\n return vec4(127.0, 128.0, 0.0, 0.0) / 255.0;\n } else if(v < -FLOAT_MAX) {\n return vec4(255.0, 128.0, 0.0, 0.0) / 255.0;\n }\n\n highp vec4 c = vec4(0,0,0,0);\n\n //Compute exponent and mantissa\n highp float e = floor(log2(av));\n highp float m = av * pow(2.0, -e) - 1.0;\n \n //Unpack mantissa\n c[1] = floor(128.0 * m);\n m -= c[1] / 128.0;\n c[2] = floor(32768.0 * m);\n m -= c[2] / 32768.0;\n c[3] = floor(8388608.0 * m);\n \n //Unpack exponent\n highp float ebias = e + 127.0;\n c[0] = floor(ebias / 2.0);\n ebias -= c[0] * 2.0;\n c[1] += floor(ebias) * 128.0; \n\n //Unpack sign bit\n c[0] += 128.0 * step(0.0, -v);\n\n //Scale back to range\n return c / 255.0;\n}\n\nuniform float pickId;\nuniform vec3 clipBounds[2];\n\nvarying vec3 worldPosition;\nvarying float pixelArcLength;\nvarying vec4 fragColor;\n\nvoid main() {\n if(any(lessThan(worldPosition, clipBounds[0])) || any(greaterThan(worldPosition, clipBounds[1]))) {\n discard;\n }\n gl_FragColor = vec4(pickId/255.0, encode_float_1540259130(pixelArcLength).xyz);\n}"]),l=[{name:"position",type:"vec3"},{name:"nextPosition",type:"vec3"},{name:"arcLength",type:"float"},{name:"lineWidth",type:"float"},{name:"color",type:"vec4"}];r.createShader=function(t){return i(t,a,o,null,l)},r.createPickShader=function(t){return i(t,a,s,null,l)}},{"gl-shader":268,glslify:353}],226:[function(t,e,r){"use strict";e.exports=function(t){var e=t.gl||t.scene&&t.scene.gl,r=u(e);r.attributes.position.location=0,r.attributes.nextPosition.location=1,r.attributes.arcLength.location=2,r.attributes.lineWidth.location=3,r.attributes.color.location=4;var o=f(e);o.attributes.position.location=0,o.attributes.nextPosition.location=1,o.attributes.arcLength.location=2,o.attributes.lineWidth.location=3,o.attributes.color.location=4;for(var s=n(e),c=i(e,[{buffer:s,size:3,offset:0,stride:48},{buffer:s,size:3,offset:12,stride:48},{buffer:s,size:1,offset:24,stride:48},{buffer:s,size:1,offset:28,stride:48},{buffer:s,size:4,offset:32,stride:48}]),h=l(new Array(1024),[256,1,4]),p=0;p<1024;++p)h.data[p]=255;var d=a(e,h);d.wrap=e.REPEAT;var g=new m(e,r,o,s,c,d);return g.update(t),g};var n=t("gl-buffer"),i=t("gl-vao"),a=t("gl-texture2d"),o=t("glsl-read-float"),s=t("binary-search-bounds"),l=t("ndarray"),c=t("./lib/shaders"),u=c.createShader,f=c.createPickShader,h=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];function p(t,e){for(var r=0,n=0;n<3;++n){var i=t[n]-e[n];r+=i*i}return Math.sqrt(r)}function d(t){for(var e=[[-1e6,-1e6,-1e6],[1e6,1e6,1e6]],r=0;r<3;++r)e[0][r]=Math.max(t[0][r],e[0][r]),e[1][r]=Math.min(t[1][r],e[1][r]);return e}function g(t,e,r,n){this.arcLength=t,this.position=e,this.index=r,this.dataCoordinate=n}function m(t,e,r,n,i,a){this.gl=t,this.shader=e,this.pickShader=r,this.buffer=n,this.vao=i,this.clipBounds=[[-1/0,-1/0,-1/0],[1/0,1/0,1/0]],this.points=[],this.arcLength=[],this.vertexCount=0,this.bounds=[[0,0,0],[0,0,0]],this.pickId=0,this.lineWidth=1,this.texture=a,this.dashScale=1,this.opacity=1,this.dirty=!0,this.pixelRatio=1}var v=m.prototype;v.isTransparent=function(){return this.opacity<1},v.isOpaque=function(){return this.opacity>=1},v.pickSlots=1,v.setPickBase=function(t){this.pickId=t},v.drawTransparent=v.draw=function(t){var e=this.gl,r=this.shader,n=this.vao;r.bind(),r.uniforms={model:t.model||h,view:t.view||h,projection:t.projection||h,clipBounds:d(this.clipBounds),dashTexture:this.texture.bind(),dashScale:this.dashScale/this.arcLength[this.arcLength.length-1],opacity:this.opacity,screenShape:[e.drawingBufferWidth,e.drawingBufferHeight],pixelRatio:this.pixelRatio},n.bind(),n.draw(e.TRIANGLE_STRIP,this.vertexCount),n.unbind()},v.drawPick=function(t){var e=this.gl,r=this.pickShader,n=this.vao;r.bind(),r.uniforms={model:t.model||h,view:t.view||h,projection:t.projection||h,pickId:this.pickId,clipBounds:d(this.clipBounds),screenShape:[e.drawingBufferWidth,e.drawingBufferHeight],pixelRatio:this.pixelRatio},n.bind(),n.draw(e.TRIANGLE_STRIP,this.vertexCount),n.unbind()},v.update=function(t){var e,r;this.dirty=!0;var n=!!t.connectGaps;"dashScale"in t&&(this.dashScale=t.dashScale),"opacity"in t&&(this.opacity=+t.opacity);var i=t.position||t.positions;if(i){var a=t.color||t.colors||[0,0,0,1],o=t.lineWidth||1,c=[],u=[],f=[],h=0,d=0,g=[[1/0,1/0,1/0],[-1/0,-1/0,-1/0]],m=!1;t:for(e=1;e0){for(var w=0;w<24;++w)c.push(c[c.length-12]);d+=2,m=!0}continue t}g[0][r]=Math.min(g[0][r],b[r],_[r]),g[1][r]=Math.max(g[1][r],b[r],_[r])}Array.isArray(a[0])?(v=a[e-1],y=a[e]):v=y=a,3===v.length&&(v=[v[0],v[1],v[2],1]),3===y.length&&(y=[y[0],y[1],y[2],1]),x=Array.isArray(o)?o[e-1]:o;var k=h;if(h+=p(b,_),m){for(r=0;r<2;++r)c.push(b[0],b[1],b[2],_[0],_[1],_[2],k,x,v[0],v[1],v[2],v[3]);d+=2,m=!1}c.push(b[0],b[1],b[2],_[0],_[1],_[2],k,x,v[0],v[1],v[2],v[3],b[0],b[1],b[2],_[0],_[1],_[2],k,-x,v[0],v[1],v[2],v[3],_[0],_[1],_[2],b[0],b[1],b[2],h,-x,y[0],y[1],y[2],y[3],_[0],_[1],_[2],b[0],b[1],b[2],h,x,y[0],y[1],y[2],y[3]),d+=4}if(this.buffer.update(c),u.push(h),f.push(i[i.length-1].slice()),this.bounds=g,this.vertexCount=d,this.points=f,this.arcLength=u,"dashes"in t){var M=t.dashes.slice();for(M.unshift(0),e=1;e1.0001)return null;m+=g[u]}if(Math.abs(m-1)>.001)return null;return[f,function(t,e){for(var r=[0,0,0],n=0;n 0.25) {\n discard;\n }\n gl_FragColor = f_color * texture2D(texture, f_uv) * opacity;\n}"]),u=n(["precision mediump float;\n#define GLSLIFY 1\n\nattribute vec3 position;\nattribute vec4 id;\n\nuniform mat4 model, view, projection;\n\nvarying vec3 f_position;\nvarying vec4 f_id;\n\nvoid main() {\n gl_Position = projection * view * model * vec4(position, 1.0);\n f_id = id;\n f_position = position;\n}"]),f=n(["precision mediump float;\n#define GLSLIFY 1\n\nuniform vec3 clipBounds[2];\nuniform float pickId;\n\nvarying vec3 f_position;\nvarying vec4 f_id;\n\nvoid main() {\n if(any(lessThan(f_position, clipBounds[0])) || \n any(greaterThan(f_position, clipBounds[1]))) {\n discard;\n }\n gl_FragColor = vec4(pickId, f_id.xyz);\n}"]),h=n(["precision mediump float;\n#define GLSLIFY 1\n\nattribute vec3 position;\nattribute float pointSize;\nattribute vec4 id;\n\nuniform mat4 model, view, projection;\nuniform vec3 clipBounds[2];\n\nvarying vec3 f_position;\nvarying vec4 f_id;\n\nvoid main() {\n if(any(lessThan(position, clipBounds[0])) || \n any(greaterThan(position, clipBounds[1]))) {\n gl_Position = vec4(0,0,0,0);\n } else {\n gl_Position = projection * view * model * vec4(position, 1.0);\n gl_PointSize = pointSize;\n }\n f_id = id;\n f_position = position;\n}"]),p=n(["precision mediump float;\n#define GLSLIFY 1\n\nattribute vec3 position;\n\nuniform mat4 model, view, projection;\n\nvoid main() {\n gl_Position = projection * view * model * vec4(position, 1.0);\n}"]),d=n(["precision mediump float;\n#define GLSLIFY 1\n\nuniform vec3 contourColor;\n\nvoid main() {\n gl_FragColor = vec4(contourColor,1);\n}\n"]);r.meshShader={vertex:i,fragment:a,attributes:[{name:"position",type:"vec3"},{name:"normal",type:"vec3"},{name:"color",type:"vec4"},{name:"uv",type:"vec2"}]},r.wireShader={vertex:o,fragment:s,attributes:[{name:"position",type:"vec3"},{name:"color",type:"vec4"},{name:"uv",type:"vec2"}]},r.pointShader={vertex:l,fragment:c,attributes:[{name:"position",type:"vec3"},{name:"color",type:"vec4"},{name:"uv",type:"vec2"},{name:"pointSize",type:"float"}]},r.pickShader={vertex:u,fragment:f,attributes:[{name:"position",type:"vec3"},{name:"id",type:"vec4"}]},r.pointPickShader={vertex:h,fragment:f,attributes:[{name:"position",type:"vec3"},{name:"pointSize",type:"float"},{name:"id",type:"vec4"}]},r.contourShader={vertex:p,fragment:d,attributes:[{name:"position",type:"vec3"}]}},{glslify:353}],249:[function(t,e,r){"use strict";var n=t("gl-shader"),i=t("gl-buffer"),a=t("gl-vao"),o=t("gl-texture2d"),s=t("normals"),l=t("gl-mat4/multiply"),c=t("gl-mat4/invert"),u=t("ndarray"),f=t("colormap"),h=t("simplicial-complex-contour"),p=t("typedarray-pool"),d=t("./lib/shaders"),g=t("./lib/closest-point"),m=d.meshShader,v=d.wireShader,y=d.pointShader,x=d.pickShader,b=d.pointPickShader,_=d.contourShader,w=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];function k(t,e,r,n,i,a,o,s,l,c,u,f,h,p,d,g,m,v,y,x,b,_,k,M,A,T,S){this.gl=t,this.cells=[],this.positions=[],this.intensity=[],this.texture=e,this.dirty=!0,this.triShader=r,this.lineShader=n,this.pointShader=i,this.pickShader=a,this.pointPickShader=o,this.contourShader=s,this.trianglePositions=l,this.triangleColors=u,this.triangleNormals=h,this.triangleUVs=f,this.triangleIds=c,this.triangleVAO=p,this.triangleCount=0,this.lineWidth=1,this.edgePositions=d,this.edgeColors=m,this.edgeUVs=v,this.edgeIds=g,this.edgeVAO=y,this.edgeCount=0,this.pointPositions=x,this.pointColors=_,this.pointUVs=k,this.pointSizes=M,this.pointIds=b,this.pointVAO=A,this.pointCount=0,this.contourLineWidth=1,this.contourPositions=T,this.contourVAO=S,this.contourCount=0,this.contourColor=[0,0,0],this.contourEnable=!0,this.pickId=1,this.bounds=[[1/0,1/0,1/0],[-1/0,-1/0,-1/0]],this.clipBounds=[[-1/0,-1/0,-1/0],[1/0,1/0,1/0]],this.lightPosition=[1e5,1e5,0],this.ambientLight=.8,this.diffuseLight=.8,this.specularLight=2,this.roughness=.5,this.fresnel=1.5,this.opacity=1,this._model=w,this._view=w,this._projection=w,this._resolution=[1,1]}var M=k.prototype;function A(t){var e=n(t,y.vertex,y.fragment);return e.attributes.position.location=0,e.attributes.color.location=2,e.attributes.uv.location=3,e.attributes.pointSize.location=4,e}function T(t){var e=n(t,x.vertex,x.fragment);return e.attributes.position.location=0,e.attributes.id.location=1,e}function S(t){var e=n(t,b.vertex,b.fragment);return e.attributes.position.location=0,e.attributes.id.location=1,e.attributes.pointSize.location=4,e}function C(t){var e=n(t,_.vertex,_.fragment);return e.attributes.position.location=0,e}M.isOpaque=function(){return this.opacity>=1},M.isTransparent=function(){return this.opacity<1},M.pickSlots=1,M.setPickBase=function(t){this.pickId=t},M.highlight=function(t){if(t&&this.contourEnable){for(var e=h(this.cells,this.intensity,t.intensity),r=e.cells,n=e.vertexIds,i=e.vertexWeights,a=r.length,o=p.mallocFloat32(6*a),s=0,l=0;l0&&((f=this.triShader).bind(),f.uniforms=s,this.triangleVAO.bind(),e.drawArrays(e.TRIANGLES,0,3*this.triangleCount),this.triangleVAO.unbind());this.edgeCount>0&&this.lineWidth>0&&((f=this.lineShader).bind(),f.uniforms=s,this.edgeVAO.bind(),e.lineWidth(this.lineWidth),e.drawArrays(e.LINES,0,2*this.edgeCount),this.edgeVAO.unbind());this.pointCount>0&&((f=this.pointShader).bind(),f.uniforms=s,this.pointVAO.bind(),e.drawArrays(e.POINTS,0,this.pointCount),this.pointVAO.unbind());this.contourEnable&&this.contourCount>0&&this.contourLineWidth>0&&((f=this.contourShader).bind(),f.uniforms=s,this.contourVAO.bind(),e.drawArrays(e.LINES,0,this.contourCount),this.contourVAO.unbind())},M.drawPick=function(t){t=t||{};for(var e=this.gl,r=t.model||w,n=t.view||w,i=t.projection||w,a=[[-1e6,-1e6,-1e6],[1e6,1e6,1e6]],o=0;o<3;++o)a[0][o]=Math.max(a[0][o],this.clipBounds[0][o]),a[1][o]=Math.min(a[1][o],this.clipBounds[1][o]);this._model=[].slice.call(r),this._view=[].slice.call(n),this._projection=[].slice.call(i),this._resolution=[e.drawingBufferWidth,e.drawingBufferHeight];var s,l={model:r,view:n,projection:i,clipBounds:a,pickId:this.pickId/255};((s=this.pickShader).bind(),s.uniforms=l,this.triangleCount>0&&(this.triangleVAO.bind(),e.drawArrays(e.TRIANGLES,0,3*this.triangleCount),this.triangleVAO.unbind()),this.edgeCount>0&&(this.edgeVAO.bind(),e.lineWidth(this.lineWidth),e.drawArrays(e.LINES,0,2*this.edgeCount),this.edgeVAO.unbind()),this.pointCount>0)&&((s=this.pointPickShader).bind(),s.uniforms=l,this.pointVAO.bind(),e.drawArrays(e.POINTS,0,this.pointCount),this.pointVAO.unbind())},M.pick=function(t){if(!t)return null;if(t.id!==this.pickId)return null;for(var e=t.value[0]+256*t.value[1]+65536*t.value[2],r=this.cells[e],n=this.positions,i=new Array(r.length),a=0;ai[M]&&(r.uniforms.dataAxis=c,r.uniforms.screenOffset=u,r.uniforms.color=m[t],r.uniforms.angle=v[t],a.drawArrays(a.TRIANGLES,i[M],i[A]-i[M]))),y[t]&&k&&(u[1^t]-=T*p*x[t],r.uniforms.dataAxis=f,r.uniforms.screenOffset=u,r.uniforms.color=b[t],r.uniforms.angle=_[t],a.drawArrays(a.TRIANGLES,w,k)),u[1^t]=T*s[2+(1^t)]-1,d[t+2]&&(u[1^t]+=T*p*g[t+2],Mi[M]&&(r.uniforms.dataAxis=c,r.uniforms.screenOffset=u,r.uniforms.color=m[t+2],r.uniforms.angle=v[t+2],a.drawArrays(a.TRIANGLES,i[M],i[A]-i[M]))),y[t+2]&&k&&(u[1^t]+=T*p*x[t+2],r.uniforms.dataAxis=f,r.uniforms.screenOffset=u,r.uniforms.color=b[t+2],r.uniforms.angle=_[t+2],a.drawArrays(a.TRIANGLES,w,k))}),g.drawTitle=function(){var t=[0,0],e=[0,0];return function(){var r=this.plot,n=this.shader,i=r.gl,a=r.screenBox,o=r.titleCenter,s=r.titleAngle,l=r.titleColor,c=r.pixelRatio;if(this.titleCount){for(var u=0;u<2;++u)e[u]=2*(o[u]*c-a[u])/(a[2+u]-a[u])-1;n.bind(),n.uniforms.dataAxis=t,n.uniforms.screenOffset=e,n.uniforms.angle=s,n.uniforms.color=l,i.drawArrays(i.TRIANGLES,this.titleOffset,this.titleCount)}}}(),g.bind=(h=[0,0],p=[0,0],d=[0,0],function(){var t=this.plot,e=this.shader,r=t._tickBounds,n=t.dataBox,i=t.screenBox,a=t.viewBox;e.bind();for(var o=0;o<2;++o){var s=r[o],l=r[o+2]-s,c=.5*(n[o+2]+n[o]),u=n[o+2]-n[o],f=a[o],g=a[o+2]-f,m=i[o],v=i[o+2]-m;p[o]=2*l/u*g/v,h[o]=2*(s-c)/u*g/v}d[1]=2*t.pixelRatio/(i[3]-i[1]),d[0]=d[1]*(i[3]-i[1])/(i[2]-i[0]),e.uniforms.dataScale=p,e.uniforms.dataShift=h,e.uniforms.textScale=d,this.vbo.bind(),e.attributes.textCoordinate.pointer()}),g.update=function(t){var e,r,n,i,o,s=[],l=t.ticks,c=t.bounds;for(o=0;o<2;++o){var u=[Math.floor(s.length/3)],f=[-1/0],h=l[o];for(e=0;e=0){var g=e[d]-n[d]*(e[d+2]-e[d])/(n[d+2]-n[d]);0===d?o.drawLine(g,e[1],g,e[3],p[d],h[d]):o.drawLine(e[0],g,e[2],g,p[d],h[d])}}for(d=0;d=0;--t)this.objects[t].dispose();this.objects.length=0;for(t=this.overlays.length-1;t>=0;--t)this.overlays[t].dispose();this.overlays.length=0,this.gl=null},c.addObject=function(t){this.objects.indexOf(t)<0&&(this.objects.push(t),this.setDirty())},c.removeObject=function(t){for(var e=this.objects,r=0;r0&&0===L[e-1];)L.pop(),z.pop().dispose()}window.addEventListener("resize",j),F.update=function(t){e||(t=t||{},P=!0,D=!0)},F.add=function(t){e||(t.axes=A,C.push(t),E.push(-1),P=!0,D=!0,V())},F.remove=function(t){if(!e){var r=C.indexOf(t);r<0||(C.splice(r,1),E.pop(),P=!0,D=!0,V())}},F.dispose=function(){if(!e&&(e=!0,window.removeEventListener("resize",j),r.removeEventListener("webglcontextlost",H),F.mouseListener.enabled=!1,!F.contextLost)){A.dispose(),S.dispose();for(var t=0;tb.distance)continue;for(var u=0;u0){r=Math.round(Math.pow(10,e));return Math.ceil(t/r)*r}return Math.ceil(t)}function m(t){return"boolean"!=typeof t||t}},{"./lib/shader":257,"3d-view-controls":41,"a-big-triangle":44,"gl-axes3d":203,"gl-axes3d/properties":210,"gl-fbo":220,"gl-mat4/perspective":238,"gl-select-static":267,"gl-spikes3d":277,"is-mobile":364,"mouse-change":378}],259:[function(t,e,r){var n=t("glslify");r.pointVertex=n(["precision mediump float;\n#define GLSLIFY 1\n\nattribute vec2 position;\n\nuniform mat3 matrix;\nuniform float pointSize;\nuniform float pointCloud;\n\nhighp float rand(vec2 co) {\n highp float a = 12.9898;\n highp float b = 78.233;\n highp float c = 43758.5453;\n highp float d = dot(co.xy, vec2(a, b));\n highp float e = mod(d, 3.14);\n return fract(sin(e) * c);\n}\n\nvoid main() {\n vec3 hgPosition = matrix * vec3(position, 1);\n gl_Position = vec4(hgPosition.xy, 0, hgPosition.z);\n // if we don't jitter the point size a bit, overall point cloud\n // saturation 'jumps' on zooming, which is disturbing and confusing\n gl_PointSize = pointSize * ((19.5 + rand(position)) / 20.0);\n if(pointCloud != 0.0) { // pointCloud is truthy\n // get the same square surface as circle would be\n gl_PointSize *= 0.886;\n }\n}"]),r.pointFragment=n(["precision mediump float;\n#define GLSLIFY 1\n\nuniform vec4 color, borderColor;\nuniform float centerFraction;\nuniform float pointCloud;\n\nvoid main() {\n float radius;\n vec4 baseColor;\n if(pointCloud != 0.0) { // pointCloud is truthy\n if(centerFraction == 1.0) {\n gl_FragColor = color;\n } else {\n gl_FragColor = mix(borderColor, color, centerFraction);\n }\n } else {\n radius = length(2.0 * gl_PointCoord.xy - 1.0);\n if(radius > 1.0) {\n discard;\n }\n baseColor = mix(borderColor, color, step(radius, centerFraction));\n gl_FragColor = vec4(baseColor.rgb * baseColor.a, baseColor.a);\n }\n}\n"]),r.pickVertex=n(["precision mediump float;\n#define GLSLIFY 1\n\nattribute vec2 position;\nattribute vec4 pickId;\n\nuniform mat3 matrix;\nuniform float pointSize;\nuniform vec4 pickOffset;\n\nvarying vec4 fragId;\n\nvoid main() {\n vec3 hgPosition = matrix * vec3(position, 1);\n gl_Position = vec4(hgPosition.xy, 0, hgPosition.z);\n gl_PointSize = pointSize;\n\n vec4 id = pickId + pickOffset;\n id.y += floor(id.x / 256.0);\n id.x -= floor(id.x / 256.0) * 256.0;\n\n id.z += floor(id.y / 256.0);\n id.y -= floor(id.y / 256.0) * 256.0;\n\n id.w += floor(id.z / 256.0);\n id.z -= floor(id.z / 256.0) * 256.0;\n\n fragId = id;\n}\n"]),r.pickFragment=n(["precision mediump float;\n#define GLSLIFY 1\n\nvarying vec4 fragId;\n\nvoid main() {\n float radius = length(2.0 * gl_PointCoord.xy - 1.0);\n if(radius > 1.0) {\n discard;\n }\n gl_FragColor = fragId / 255.0;\n}\n"])},{glslify:353}],260:[function(t,e,r){"use strict";var n=t("gl-shader"),i=t("gl-buffer"),a=t("typedarray-pool"),o=t("./lib/shader");function s(t,e,r,n,i){this.plot=t,this.offsetBuffer=e,this.pickBuffer=r,this.shader=n,this.pickShader=i,this.sizeMin=.5,this.sizeMinCap=2,this.sizeMax=20,this.areaRatio=1,this.pointCount=0,this.color=[1,0,0,1],this.borderColor=[0,0,0,1],this.blend=!1,this.pickOffset=0,this.points=null}e.exports=function(t,e){var r=t.gl,a=i(r),l=i(r),c=n(r,o.pointVertex,o.pointFragment),u=n(r,o.pickVertex,o.pickFragment),f=new s(t,a,l,c,u);return f.update(e),t.addObject(f),f};var l,c,u=s.prototype;u.dispose=function(){this.shader.dispose(),this.pickShader.dispose(),this.offsetBuffer.dispose(),this.pickBuffer.dispose(),this.plot.removeObject(this)},u.update=function(t){var e;function r(e,r){return e in t?t[e]:r}t=t||{},this.sizeMin=r("sizeMin",.5),this.sizeMax=r("sizeMax",20),this.color=r("color",[1,0,0,1]).slice(),this.areaRatio=r("areaRatio",1),this.borderColor=r("borderColor",[0,0,0,1]).slice(),this.blend=r("blend",!1);var n=t.positions.length>>>1,i=t.positions instanceof Float32Array,o=t.idToIndex instanceof Int32Array&&t.idToIndex.length>=n,s=t.positions,l=i?s:a.mallocFloat32(s.length),c=o?t.idToIndex:a.mallocInt32(n);if(i||l.set(s),!o)for(l.set(s),e=0;e>>1;for(r=0;r=e[0]&&a<=e[2]&&o>=e[1]&&o<=e[3]&&n++}return n}(this.points,i),u=this.plot.pickPixelRatio*Math.max(Math.min(this.sizeMinCap,this.sizeMin),Math.min(this.sizeMax,this.sizeMax/Math.pow(s,.33333)));l[0]=2/a,l[4]=2/o,l[6]=-2*i[0]/a-1,l[7]=-2*i[1]/o-1,this.offsetBuffer.bind(),r.bind(),r.attributes.position.pointer(),r.uniforms.matrix=l,r.uniforms.color=this.color,r.uniforms.borderColor=this.borderColor,r.uniforms.pointCloud=u<5,r.uniforms.pointSize=u,r.uniforms.centerFraction=Math.min(1,Math.max(0,Math.sqrt(1-this.areaRatio))),e&&(c[0]=255&t,c[1]=t>>8&255,c[2]=t>>16&255,c[3]=t>>24&255,this.pickBuffer.bind(),r.attributes.pickId.pointer(n.UNSIGNED_BYTE),r.uniforms.pickOffset=c,this.pickOffset=t);var f=n.getParameter(n.BLEND),h=n.getParameter(n.DITHER);return f&&!this.blend&&n.disable(n.BLEND),h&&n.disable(n.DITHER),n.drawArrays(n.POINTS,0,this.pointCount),f&&!this.blend&&n.enable(n.BLEND),h&&n.enable(n.DITHER),t+this.pointCount}),u.draw=u.unifiedDraw,u.drawPick=u.unifiedDraw,u.pick=function(t,e,r){var n=this.pickOffset,i=this.pointCount;if(r=n+i)return null;var a=r-n,o=this.points;return{object:this,pointId:a,dataCoord:[o[2*a],o[2*a+1]]}}},{"./lib/shader":259,"gl-buffer":211,"gl-shader":268,"typedarray-pool":481}],261:[function(t,e,r){e.exports=function(t,e,r,n){var i,a,o,s,l,c=e[0],u=e[1],f=e[2],h=e[3],p=r[0],d=r[1],g=r[2],m=r[3];(a=c*p+u*d+f*g+h*m)<0&&(a=-a,p=-p,d=-d,g=-g,m=-m);1-a>1e-6?(i=Math.acos(a),o=Math.sin(i),s=Math.sin((1-n)*i)/o,l=Math.sin(n*i)/o):(s=1-n,l=n);return t[0]=s*c+l*p,t[1]=s*u+l*d,t[2]=s*f+l*g,t[3]=s*h+l*m,t}},{}],262:[function(t,e,r){"use strict";var n=t("vectorize-text");e.exports=function(t,e){var r=i[e];r||(r=i[e]={});if(t in r)return r[t];for(var a=n(t,{textAlign:"center",textBaseline:"middle",lineHeight:1,font:e}),o=n(t,{triangles:!0,textAlign:"center",textBaseline:"middle",lineHeight:1,font:e}),s=[[1/0,1/0],[-1/0,-1/0]],l=0;l=1)return!0;for(var t=0;t<3;++t)if(this.axesProject[t]&&this.projectOpacity[t]>=1)return!0;return!1};var g=[0,0],m=[0,0,0],v=[0,0,0],y=[0,0,0,1],x=[0,0,0,1],b=c.slice(),_=[0,0,0],w=[[0,0,0],[0,0,0]];function k(t){return t[0]=t[1]=t[2]=0,t}function M(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=1,t}function A(t,e,r,n){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[r]=n,t}function T(t,e,r,n,i){var a,s=e.axesProject,l=e.gl,u=t.uniforms,h=r.model||c,p=r.view||c,d=r.projection||c,T=e.axesBounds,S=function(t){for(var e=w,r=0;r<2;++r)for(var n=0;n<3;++n)e[r][n]=Math.max(Math.min(t[r][n],1e8),-1e8);return e}(e.clipBounds);a=e.axes&&e.axes.lastCubeProps?e.axes.lastCubeProps.axis:[1,1,1],g[0]=2/l.drawingBufferWidth,g[1]=2/l.drawingBufferHeight,t.bind(),u.view=p,u.projection=d,u.screenSize=g,u.highlightId=e.highlightId,u.highlightScale=e.highlightScale,u.clipBounds=S,u.pickGroup=e.pickId/255,u.pixelRatio=e.pixelRatio;for(var C=0;C<3;++C)if(s[C]&&e.projectOpacity[C]<1===n){u.scale=e.projectScale[C],u.opacity=e.projectOpacity[C];for(var E=b,L=0;L<16;++L)E[L]=0;for(L=0;L<4;++L)E[5*L]=1;E[5*C]=0,a[C]<0?E[12+C]=T[0][C]:E[12+C]=T[1][C],o(E,h,E),u.model=E;var z=(C+1)%3,P=(C+2)%3,D=k(m),O=k(v);D[z]=1,O[P]=1;var I=f(0,0,0,M(y,D)),R=f(0,0,0,M(x,O));if(Math.abs(I[1])>Math.abs(R[1])){var B=I;I=R,R=B,B=D,D=O,O=B;var F=z;z=P,P=F}I[0]<0&&(D[z]=-1),R[1]>0&&(O[P]=-1);var N=0,j=0;for(L=0;L<4;++L)N+=Math.pow(h[4*z+L],2),j+=Math.pow(h[4*P+L],2);D[z]/=Math.sqrt(N),O[P]/=Math.sqrt(j),u.axes[0]=D,u.axes[1]=O,u.fragClipBounds[0]=A(_,S[0],C,-1e8),u.fragClipBounds[1]=A(_,S[1],C,1e8),e.vao.draw(l.TRIANGLES,e.vertexCount),e.lineWidth>0&&(l.lineWidth(e.lineWidth),e.vao.draw(l.LINES,e.lineVertexCount,e.vertexCount))}}var S=[[-1e8,-1e8,-1e8],[1e8,1e8,1e8]];function C(t,e,r,n,i,a){var o=r.gl;if(r.vao.bind(),i===r.opacity<1||a){t.bind();var s=t.uniforms;s.model=n.model||c,s.view=n.view||c,s.projection=n.projection||c,g[0]=2/o.drawingBufferWidth,g[1]=2/o.drawingBufferHeight,s.screenSize=g,s.highlightId=r.highlightId,s.highlightScale=r.highlightScale,s.fragClipBounds=S,s.clipBounds=r.axes.bounds,s.opacity=r.opacity,s.pickGroup=r.pickId/255,s.pixelRatio=r.pixelRatio,r.vao.draw(o.TRIANGLES,r.vertexCount),r.lineWidth>0&&(o.lineWidth(r.lineWidth),r.vao.draw(o.LINES,r.lineVertexCount,r.vertexCount))}T(e,r,n,i),r.vao.unbind()}d.draw=function(t){C(this.useOrtho?this.orthoShader:this.shader,this.projectShader,this,t,!1,!1)},d.drawTransparent=function(t){C(this.useOrtho?this.orthoShader:this.shader,this.projectShader,this,t,!0,!1)},d.drawPick=function(t){C(this.useOrtho?this.pickOrthoShader:this.pickPerspectiveShader,this.pickProjectShader,this,t,!1,!0)},d.pick=function(t){if(!t)return null;if(t.id!==this.pickId)return null;var e=t.value[2]+(t.value[1]<<8)+(t.value[0]<<16);if(e>=this.pointCount||e<0)return null;var r=this.points[e],n=this._selectResult;n.index=e;for(var i=0;i<3;++i)n.position[i]=n.dataCoordinate[i]=r[i];return n},d.highlight=function(t){if(t){var e=t.index,r=255&e,n=e>>8&255,i=e>>16&255;this.highlightId=[r/255,n/255,i/255,0]}else this.highlightId=[1,1,1,1]},d.update=function(t){if("perspective"in(t=t||{})&&(this.useOrtho=!t.perspective),"orthographic"in t&&(this.useOrtho=!!t.orthographic),"lineWidth"in t&&(this.lineWidth=t.lineWidth),"project"in t)if(Array.isArray(t.project))this.axesProject=t.project;else{var e=!!t.project;this.axesProject=[e,e,e]}if("projectScale"in t)if(Array.isArray(t.projectScale))this.projectScale=t.projectScale.slice();else{var r=+t.projectScale;this.projectScale=[r,r,r]}if("projectOpacity"in t)if(Array.isArray(t.projectOpacity))this.projectOpacity=t.projectOpacity.slice();else{r=+t.projectOpacity;this.projectOpacity=[r,r,r]}"opacity"in t&&(this.opacity=t.opacity),this.dirty=!0;var n=t.position;if(n){var i=t.font||"normal",o=t.alignment||[0,0],s=[1/0,1/0,1/0],c=[-1/0,-1/0,-1/0],u=t.glyph,f=t.color,h=t.size,p=t.angle,d=t.lineColor,g=0,m=0,v=0,y=n.length;t:for(var x=0;x0&&(L[0]=-o[0]*(1+M[0][0]));var q=w.cells,H=w.positions;for(_=0;_0){var v=r*u;o.drawBox(f-v,h-v,p+v,h+v,a),o.drawBox(f-v,d-v,p+v,d+v,a),o.drawBox(f-v,h-v,f+v,d+v,a),o.drawBox(p-v,h-v,p+v,d+v,a)}}}},s.update=function(t){t=t||{},this.innerFill=!!t.innerFill,this.outerFill=!!t.outerFill,this.innerColor=(t.innerColor||[0,0,0,.5]).slice(),this.outerColor=(t.outerColor||[0,0,0,.5]).slice(),this.borderColor=(t.borderColor||[0,0,0,1]).slice(),this.borderWidth=t.borderWidth||0,this.selectBox=(t.selectBox||this.selectBox).slice()},s.dispose=function(){this.boxBuffer.dispose(),this.boxShader.dispose(),this.plot.removeOverlay(this)}},{"./lib/shaders":265,"gl-buffer":211,"gl-shader":268}],267:[function(t,e,r){"use strict";e.exports=function(t,e){var r=n(t,e),a=i.mallocUint8(e[0]*e[1]*4);return new c(t,r,a)};var n=t("gl-fbo"),i=t("typedarray-pool"),a=t("ndarray"),o=t("bit-twiddle").nextPow2,s=t("cwise/lib/wrapper")({args:["array",{offset:[0,0,1],array:0},{offset:[0,0,2],array:0},{offset:[0,0,3],array:0},"scalar","scalar","index"],pre:{body:"{this_closestD2=1e8,this_closestX=-1,this_closestY=-1}",args:[],thisVars:["this_closestD2","this_closestX","this_closestY"],localVars:[]},body:{body:"{if(_inline_16_arg0_<255||_inline_16_arg1_<255||_inline_16_arg2_<255||_inline_16_arg3_<255){var _inline_16_l=_inline_16_arg4_-_inline_16_arg6_[0],_inline_16_a=_inline_16_arg5_-_inline_16_arg6_[1],_inline_16_f=_inline_16_l*_inline_16_l+_inline_16_a*_inline_16_a;_inline_16_fthis.buffer.length){i.free(this.buffer);for(var n=this.buffer=i.mallocUint8(o(r*e*4)),a=0;ar)for(t=r;te)for(t=e;t=0){for(var k=0|w.type.charAt(w.type.length-1),M=new Array(k),A=0;A=0;)T+=1;_[y]=T}var S=new Array(r.length);function C(){h.program=o.program(p,h._vref,h._fref,b,_);for(var t=0;t=0){var d=h.charCodeAt(h.length-1)-48;if(d<2||d>4)throw new n("","Invalid data type for attribute "+f+": "+h);o(t,e,p[0],i,d,a,f)}else{if(!(h.indexOf("mat")>=0))throw new n("","Unknown data type for attribute "+f+": "+h);var d=h.charCodeAt(h.length-1)-48;if(d<2||d>4)throw new n("","Invalid data type for attribute "+f+": "+h);s(t,e,p,i,d,a,f)}}}return a};var n=t("./GLError");function i(t,e,r,n,i,a){this._gl=t,this._wrapper=e,this._index=r,this._locations=n,this._dimension=i,this._constFunc=a}var a=i.prototype;function o(t,e,r,n,a,o,s){for(var l=["gl","v"],c=[],u=0;u4)throw new i("","Invalid uniform dimension type for matrix "+name+": "+r);return"gl.uniformMatrix"+a+"fv(locations["+e+"],false,obj"+t+")"}throw new i("","Unknown uniform data type for "+name+": "+r)}var a=r.charCodeAt(r.length-1)-48;if(a<2||a>4)throw new i("","Invalid data type");switch(r.charAt(0)){case"b":case"i":return"gl.uniform"+a+"iv(locations["+e+"],obj"+t+")";case"v":return"gl.uniform"+a+"fv(locations["+e+"],obj"+t+")";default:throw new i("","Unrecognized data type for vector "+name+": "+r)}}}function c(e){for(var n=["return function updateProperty(obj){"],i=function t(e,r){if("object"!=typeof r)return[[e,r]];var n=[];for(var i in r){var a=r[i],o=e;parseInt(i)+""===i?o+="["+i+"]":o+="."+i,"object"==typeof a?n.push.apply(n,t(o,a)):n.push([o,a])}return n}("",e),a=0;a4)throw new i("","Invalid data type");return"b"===t.charAt(0)?o(r,!1):o(r,0)}if(0===t.indexOf("mat")&&4===t.length){var r=t.charCodeAt(t.length-1)-48;if(r<2||r>4)throw new i("","Invalid uniform dimension type for matrix "+name+": "+t);return o(r*r,0)}throw new i("","Unknown uniform data type for "+name+": "+t)}}(r[u].type);var p}function f(t){var e;if(Array.isArray(t)){e=new Array(t.length);for(var r=0;r1){l[0]in o||(o[l[0]]=[]),o=o[l[0]];for(var c=1;c1)for(var l=0;l 0.0 ||\n any(lessThan(worldCoordinate, clipBounds[0])) || any(greaterThan(worldCoordinate, clipBounds[1]))) {\n discard;\n }\n\n vec3 N = normalize(surfaceNormal);\n vec3 V = normalize(eyeDirection);\n vec3 L = normalize(lightDirection);\n\n if(gl_FrontFacing) {\n N = -N;\n }\n\n float specular = max(beckmannSpecular(L, V, N, roughness), 0.);\n float diffuse = min(kambient + kdiffuse * max(dot(N, L), 0.0), 1.0);\n\n //decide how to interpolate color \u2014 in vertex or in fragment\n vec4 surfaceColor = step(vertexColor, .5) * texture2D(colormap, vec2(value, value)) + step(.5, vertexColor) * vColor;\n\n vec4 litColor = surfaceColor.a * vec4(diffuse * surfaceColor.rgb + kspecular * vec3(1,1,1) * specular, 1.0);\n\n gl_FragColor = mix(litColor, contourColor, contourTint) * opacity;\n}\n"]),s=i(["precision mediump float;\n#define GLSLIFY 1\n\nattribute vec4 uv;\nattribute float f;\n\nuniform mat3 permutation;\nuniform mat4 model, view, projection;\nuniform float height, zOffset;\nuniform sampler2D colormap;\n\nvarying float value, kill;\nvarying vec3 worldCoordinate;\nvarying vec2 planeCoordinate;\nvarying vec3 lightDirection, eyeDirection, surfaceNormal;\nvarying vec4 vColor;\n\nvoid main() {\n vec3 dataCoordinate = permutation * vec3(uv.xy, height);\n vec4 worldPosition = model * vec4(dataCoordinate, 1.0);\n\n vec4 clipPosition = projection * view * worldPosition;\n clipPosition.z = clipPosition.z + zOffset;\n\n gl_Position = clipPosition;\n value = f;\n kill = -1.0;\n worldCoordinate = dataCoordinate;\n planeCoordinate = uv.zw;\n\n vColor = texture2D(colormap, vec2(value, value));\n\n //Don't do lighting for contours\n surfaceNormal = vec3(1,0,0);\n eyeDirection = vec3(0,1,0);\n lightDirection = vec3(0,0,1);\n}\n"]),l=i(["precision mediump float;\n#define GLSLIFY 1\n\nuniform vec2 shape;\nuniform vec3 clipBounds[2];\nuniform float pickId;\n\nvarying float value, kill;\nvarying vec3 worldCoordinate;\nvarying vec2 planeCoordinate;\nvarying vec3 surfaceNormal;\n\nvec2 splitFloat(float v) {\n float vh = 255.0 * v;\n float upper = floor(vh);\n float lower = fract(vh);\n return vec2(upper / 255.0, floor(lower * 16.0) / 16.0);\n}\n\nvoid main() {\n if(kill > 0.0 ||\n any(lessThan(worldCoordinate, clipBounds[0])) || any(greaterThan(worldCoordinate, clipBounds[1]))) {\n discard;\n }\n vec2 ux = splitFloat(planeCoordinate.x / shape.x);\n vec2 uy = splitFloat(planeCoordinate.y / shape.y);\n gl_FragColor = vec4(pickId, ux.x, uy.x, ux.y + (uy.y/16.0));\n}\n"]);r.createShader=function(t){var e=n(t,a,o,null,[{name:"uv",type:"vec4"},{name:"f",type:"vec3"},{name:"normal",type:"vec3"}]);return e.attributes.uv.location=0,e.attributes.f.location=1,e.attributes.normal.location=2,e},r.createPickShader=function(t){var e=n(t,a,l,null,[{name:"uv",type:"vec4"},{name:"f",type:"vec3"},{name:"normal",type:"vec3"}]);return e.attributes.uv.location=0,e.attributes.f.location=1,e.attributes.normal.location=2,e},r.createContourShader=function(t){var e=n(t,s,o,null,[{name:"uv",type:"vec4"},{name:"f",type:"float"}]);return e.attributes.uv.location=0,e.attributes.f.location=1,e},r.createPickContourShader=function(t){var e=n(t,s,l,null,[{name:"uv",type:"vec4"},{name:"f",type:"float"}]);return e.attributes.uv.location=0,e.attributes.f.location=1,e}},{"gl-shader":268,glslify:353}],279:[function(t,e,r){"use strict";e.exports=function(t){var e=t.gl,r=y(e),n=b(e),s=x(e),l=_(e),c=i(e),u=a(e,[{buffer:c,size:4,stride:w,offset:0},{buffer:c,size:3,stride:w,offset:16},{buffer:c,size:3,stride:w,offset:28}]),f=i(e),h=a(e,[{buffer:f,size:4,stride:20,offset:0},{buffer:f,size:1,stride:20,offset:16}]),p=i(e),d=a(e,[{buffer:p,size:2,type:e.FLOAT}]),g=o(e,1,S,e.RGBA,e.UNSIGNED_BYTE);g.minFilter=e.LINEAR,g.magFilter=e.LINEAR;var m=new C(e,[0,0],[[0,0,0],[0,0,0]],r,n,c,u,g,s,l,f,h,p,d),v={levels:[[],[],[]]};for(var k in t)v[k]=t[k];return v.colormap=v.colormap||"jet",m.update(v),m};var n=t("bit-twiddle"),i=t("gl-buffer"),a=t("gl-vao"),o=t("gl-texture2d"),s=t("typedarray-pool"),l=t("colormap"),c=t("ndarray-ops"),u=t("ndarray-pack"),f=t("ndarray"),h=t("surface-nets"),p=t("gl-mat4/multiply"),d=t("gl-mat4/invert"),g=t("binary-search-bounds"),m=t("ndarray-gradient"),v=t("./lib/shaders"),y=v.createShader,x=v.createContourShader,b=v.createPickShader,_=v.createPickContourShader,w=40,k=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],M=[[0,0],[0,1],[1,0],[1,1],[1,0],[0,1]],A=[[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]];function T(t,e,r,n,i){this.position=t,this.index=e,this.uv=r,this.level=n,this.dataCoordinate=i}!function(){for(var t=0;t<3;++t){var e=A[t],r=(t+2)%3;e[(t+1)%3+0]=1,e[r+3]=1,e[t+6]=1}}();var S=256;function C(t,e,r,n,i,a,o,l,c,u,h,p,d,g){this.gl=t,this.shape=e,this.bounds=r,this.intensityBounds=[],this._shader=n,this._pickShader=i,this._coordinateBuffer=a,this._vao=o,this._colorMap=l,this._contourShader=c,this._contourPickShader=u,this._contourBuffer=h,this._contourVAO=p,this._contourOffsets=[[],[],[]],this._contourCounts=[[],[],[]],this._vertexCount=0,this._pickResult=new T([0,0,0],[0,0],[0,0],[0,0,0],[0,0,0]),this._dynamicBuffer=d,this._dynamicVAO=g,this._dynamicOffsets=[0,0,0],this._dynamicCounts=[0,0,0],this.contourWidth=[1,1,1],this.contourLevels=[[1],[1],[1]],this.contourTint=[0,0,0],this.contourColor=[[.5,.5,.5,1],[.5,.5,.5,1],[.5,.5,.5,1]],this.showContour=!0,this.showSurface=!0,this.enableHighlight=[!0,!0,!0],this.highlightColor=[[0,0,0,1],[0,0,0,1],[0,0,0,1]],this.highlightTint=[1,1,1],this.highlightLevel=[-1,-1,-1],this.enableDynamic=[!0,!0,!0],this.dynamicLevel=[NaN,NaN,NaN],this.dynamicColor=[[0,0,0,1],[0,0,0,1],[0,0,0,1]],this.dynamicTint=[1,1,1],this.dynamicWidth=[1,1,1],this.axesBounds=[[1/0,1/0,1/0],[-1/0,-1/0,-1/0]],this.surfaceProject=[!1,!1,!1],this.contourProject=[[!1,!1,!1],[!1,!1,!1],[!1,!1,!1]],this.colorBounds=[!1,!1],this._field=[f(s.mallocFloat(1024),[0,0]),f(s.mallocFloat(1024),[0,0]),f(s.mallocFloat(1024),[0,0])],this.pickId=1,this.clipBounds=[[-1/0,-1/0,-1/0],[1/0,1/0,1/0]],this.snapToData=!1,this.opacity=1,this.lightPosition=[10,1e4,0],this.ambientLight=.8,this.diffuseLight=.8,this.specularLight=2,this.roughness=.5,this.fresnel=1.5,this.vertexColor=0,this.dirty=!0}var E=C.prototype;E.isTransparent=function(){return this.opacity<1},E.isOpaque=function(){if(this.opacity>=1)return!0;for(var t=0;t<3;++t)if(this._contourCounts[t].length>0||this._dynamicCounts[t]>0)return!0;return!1},E.pickSlots=1,E.setPickBase=function(t){this.pickId=t};var L=[0,0,0],z={showSurface:!1,showContour:!1,projections:[k.slice(),k.slice(),k.slice()],clipBounds:[[[0,0,0],[0,0,0]],[[0,0,0],[0,0,0]],[[0,0,0],[0,0,0]]]};function P(t,e){var r,n,i,a=e.axes&&e.axes.lastCubeProps.axis||L,o=e.showSurface,s=e.showContour;for(r=0;r<3;++r)for(o=o||e.surfaceProject[r],n=0;n<3;++n)s=s||e.contourProject[r][n];for(r=0;r<3;++r){var l=z.projections[r];for(n=0;n<16;++n)l[n]=0;for(n=0;n<4;++n)l[5*n]=1;l[5*r]=0,l[12+r]=e.axesBounds[+(a[r]>0)][r],p(l,t.model,l);var c=z.clipBounds[r];for(i=0;i<2;++i)for(n=0;n<3;++n)c[i][n]=t.clipBounds[i][n];c[0][r]=-1e8,c[1][r]=1e8}return z.showSurface=o,z.showContour=s,z}var D={model:k,view:k,projection:k,inverseModel:k.slice(),lowerBound:[0,0,0],upperBound:[0,0,0],colorMap:0,clipBounds:[[0,0,0],[0,0,0]],height:0,contourTint:0,contourColor:[0,0,0,1],permutation:[1,0,0,0,1,0,0,0,1],zOffset:-1e-4,kambient:1,kdiffuse:1,kspecular:1,lightPosition:[1e3,1e3,1e3],eyePosition:[0,0,0],roughness:1,fresnel:1,opacity:1,vertexColor:0},O=k.slice(),I=[1,0,0,0,1,0,0,0,1];function R(t,e){t=t||{};var r=this.gl;r.disable(r.CULL_FACE),this._colorMap.bind(0);var n=D;n.model=t.model||k,n.view=t.view||k,n.projection=t.projection||k,n.lowerBound=[this.bounds[0][0],this.bounds[0][1],this.colorBounds[0]||this.bounds[0][2]],n.upperBound=[this.bounds[1][0],this.bounds[1][1],this.colorBounds[1]||this.bounds[1][2]],n.contourColor=this.contourColor[0],n.inverseModel=d(n.inverseModel,n.model);for(var i=0;i<2;++i)for(var a=n.clipBounds[i],o=0;o<3;++o)a[o]=Math.min(Math.max(this.clipBounds[i][o],-1e8),1e8);n.kambient=this.ambientLight,n.kdiffuse=this.diffuseLight,n.kspecular=this.specularLight,n.roughness=this.roughness,n.fresnel=this.fresnel,n.opacity=this.opacity,n.height=0,n.permutation=I,n.vertexColor=this.vertexColor;var s=O;for(p(s,n.view,n.model),p(s,n.projection,s),d(s,s),i=0;i<3;++i)n.eyePosition[i]=s[12+i]/s[15];var l=s[15];for(i=0;i<3;++i)l+=this.lightPosition[i]*s[4*i+3];for(i=0;i<3;++i){var c=s[12+i];for(o=0;o<3;++o)c+=s[4*o+i]*this.lightPosition[o];n.lightPosition[i]=c/l}var u=P(n,this);if(u.showSurface&&e===this.opacity<1){for(this._shader.bind(),this._shader.uniforms=n,this._vao.bind(),this.showSurface&&this._vertexCount&&this._vao.draw(r.TRIANGLES,this._vertexCount),i=0;i<3;++i)this.surfaceProject[i]&&this.vertexCount&&(this._shader.uniforms.model=u.projections[i],this._shader.uniforms.clipBounds=u.clipBounds[i],this._vao.draw(r.TRIANGLES,this._vertexCount));this._vao.unbind()}if(u.showContour&&!e){var f=this._contourShader;n.kambient=1,n.kdiffuse=0,n.kspecular=0,n.opacity=1,f.bind(),f.uniforms=n;var h=this._contourVAO;for(h.bind(),i=0;i<3;++i)for(f.uniforms.permutation=A[i],r.lineWidth(this.contourWidth[i]),o=0;o>4)/16)/255,i=Math.floor(n),a=n-i,o=e[1]*(t.value[1]+(15&t.value[2])/16)/255,s=Math.floor(o),l=o-s;i+=1,s+=1;var c=r.position;c[0]=c[1]=c[2]=0;for(var u=0;u<2;++u)for(var f=u?a:1-a,h=0;h<2;++h)for(var p=i+u,d=s+h,m=f*(h?l:1-l),v=0;v<3;++v)c[v]+=this._field[v].get(p,d)*m;for(var y=this._pickResult.level,x=0;x<3;++x)if(y[x]=g.le(this.contourLevels[x],c[x]),y[x]<0)this.contourLevels[x].length>0&&(y[x]=0);else if(y[x]Math.abs(_-c[x])&&(y[x]+=1)}for(r.index[0]=a<.5?i:i+1,r.index[1]=l<.5?s:s+1,r.uv[0]=n/e[0],r.uv[1]=o/e[1],v=0;v<3;++v)r.dataCoordinate[v]=this._field[v].get(r.index[0],r.index[1]);return r},E.update=function(t){t=t||{},this.dirty=!0,"contourWidth"in t&&(this.contourWidth=N(t.contourWidth,Number)),"showContour"in t&&(this.showContour=N(t.showContour,Boolean)),"showSurface"in t&&(this.showSurface=!!t.showSurface),"contourTint"in t&&(this.contourTint=N(t.contourTint,Boolean)),"contourColor"in t&&(this.contourColor=V(t.contourColor)),"contourProject"in t&&(this.contourProject=N(t.contourProject,function(t){return N(t,Boolean)})),"surfaceProject"in t&&(this.surfaceProject=t.surfaceProject),"dynamicColor"in t&&(this.dynamicColor=V(t.dynamicColor)),"dynamicTint"in t&&(this.dynamicTint=N(t.dynamicTint,Number)),"dynamicWidth"in t&&(this.dynamicWidth=N(t.dynamicWidth,Number)),"opacity"in t&&(this.opacity=t.opacity),"colorBounds"in t&&(this.colorBounds=t.colorBounds),"vertexColor"in t&&(this.vertexColor=t.vertexColor?1:0);var e=t.field||t.coords&&t.coords[2]||null,r=!1;if(e||(e=this._field[2].shape[0]||this._field[2].shape[2]?this._field[2].lo(1,1).hi(this._field[2].shape[0]-2,this._field[2].shape[1]-2):this._field[2].hi(0,0)),"field"in t||"coords"in t){var i=(e.shape[0]+2)*(e.shape[1]+2);i>this._field[2].data.length&&(s.freeFloat(this._field[2].data),this._field[2].data=s.mallocFloat(n.nextPow2(i))),this._field[2]=f(this._field[2].data,[e.shape[0]+2,e.shape[1]+2]),F(this._field[2],e),this.shape=e.shape.slice();for(var a=this.shape,o=0;o<2;++o)this._field[2].size>this._field[o].data.length&&(s.freeFloat(this._field[o].data),this._field[o].data=s.mallocFloat(this._field[2].size)),this._field[o]=f(this._field[o].data,[a[0]+2,a[1]+2]);if(t.coords){var p=t.coords;if(!Array.isArray(p)||3!==p.length)throw new Error("gl-surface: invalid coordinates for x/y");for(o=0;o<2;++o){var d=p[o];for(b=0;b<2;++b)if(d.shape[b]!==a[b])throw new Error("gl-surface: coords have incorrect shape");F(this._field[o],d)}}else if(t.ticks){var g=t.ticks;if(!Array.isArray(g)||2!==g.length)throw new Error("gl-surface: invalid ticks");for(o=0;o<2;++o){var v=g[o];if((Array.isArray(v)||v.length)&&(v=f(v)),v.shape[0]!==a[o])throw new Error("gl-surface: invalid tick length");var y=f(v.data,a);y.stride[o]=v.stride[0],y.stride[1^o]=0,F(this._field[o],y)}}else{for(o=0;o<2;++o){var x=[0,0];x[o]=1,this._field[o]=f(this._field[o].data,[a[0]+2,a[1]+2],x,0)}this._field[0].set(0,0,0);for(var b=0;b0){for(var kt=0;kt<5;++kt)nt.pop();W-=1}continue t}nt.push(st[0],st[1],ut[0],ut[1],st[2]),W+=1}}ot.push(W)}this._contourOffsets[it]=at,this._contourCounts[it]=ot}var Mt=s.mallocFloat(nt.length);for(o=0;os||o[1]<0||o[1]>s)throw new Error("gl-texture2d: Invalid texture size");var l=d(o,e.stride.slice()),c=0;"float32"===r?c=t.FLOAT:"float64"===r?(c=t.FLOAT,l=!1,r="float32"):"uint8"===r?c=t.UNSIGNED_BYTE:(c=t.UNSIGNED_BYTE,l=!1,r="uint8");var f,p,m=0;if(2===o.length)m=t.LUMINANCE,o=[o[0],o[1],1],e=n(e.data,o,[e.stride[0],e.stride[1],1],e.offset);else{if(3!==o.length)throw new Error("gl-texture2d: Invalid shape for texture");if(1===o[2])m=t.ALPHA;else if(2===o[2])m=t.LUMINANCE_ALPHA;else if(3===o[2])m=t.RGB;else{if(4!==o[2])throw new Error("gl-texture2d: Invalid shape for pixel coords");m=t.RGBA}}c!==t.FLOAT||t.getExtension("OES_texture_float")||(c=t.UNSIGNED_BYTE,l=!1);var v=e.size;if(l)f=0===e.offset&&e.data.length===v?e.data:e.data.subarray(e.offset,e.offset+v);else{var y=[o[2],o[2]*o[0],1];p=a.malloc(v,r);var x=n(p,o,y,0);"float32"!==r&&"float64"!==r||c!==t.UNSIGNED_BYTE?i.assign(x,e):u(x,e),f=p.subarray(0,v)}var b=g(t);t.texImage2D(t.TEXTURE_2D,0,m,o[0],o[1],0,m,c,f),l||a.free(p);return new h(t,b,o[0],o[1],m,c)}(t,e)}throw new Error("gl-texture2d: Invalid arguments for texture2d constructor")};var o=null,s=null,l=null;function c(t){return"undefined"!=typeof HTMLCanvasElement&&t instanceof HTMLCanvasElement||"undefined"!=typeof HTMLImageElement&&t instanceof HTMLImageElement||"undefined"!=typeof HTMLVideoElement&&t instanceof HTMLVideoElement||"undefined"!=typeof ImageData&&t instanceof ImageData}var u=function(t,e){i.muls(t,e,255)};function f(t,e,r){var n=t.gl,i=n.getParameter(n.MAX_TEXTURE_SIZE);if(e<0||e>i||r<0||r>i)throw new Error("gl-texture2d: Invalid texture size");return t._shape=[e,r],t.bind(),n.texImage2D(n.TEXTURE_2D,0,t.format,e,r,0,t.format,t.type,null),t._mipLevels=[0],t}function h(t,e,r,n,i,a){this.gl=t,this.handle=e,this.format=i,this.type=a,this._shape=[r,n],this._mipLevels=[0],this._magFilter=t.NEAREST,this._minFilter=t.NEAREST,this._wrapS=t.CLAMP_TO_EDGE,this._wrapT=t.CLAMP_TO_EDGE,this._anisoSamples=1;var o=this,s=[this._wrapS,this._wrapT];Object.defineProperties(s,[{get:function(){return o._wrapS},set:function(t){return o.wrapS=t}},{get:function(){return o._wrapT},set:function(t){return o.wrapT=t}}]),this._wrapVector=s;var l=[this._shape[0],this._shape[1]];Object.defineProperties(l,[{get:function(){return o._shape[0]},set:function(t){return o.width=t}},{get:function(){return o._shape[1]},set:function(t){return o.height=t}}]),this._shapeVector=l}var p=h.prototype;function d(t,e){return 3===t.length?1===e[2]&&e[1]===t[0]*t[2]&&e[0]===t[2]:1===e[0]&&e[1]===t[0]}function g(t){var e=t.createTexture();return t.bindTexture(t.TEXTURE_2D,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),e}function m(t,e,r,n,i){var a=t.getParameter(t.MAX_TEXTURE_SIZE);if(e<0||e>a||r<0||r>a)throw new Error("gl-texture2d: Invalid texture shape");if(i===t.FLOAT&&!t.getExtension("OES_texture_float"))throw new Error("gl-texture2d: Floating point textures not supported on this platform");var o=g(t);return t.texImage2D(t.TEXTURE_2D,0,n,e,r,0,n,i,null),new h(t,o,e,r,n,i)}Object.defineProperties(p,{minFilter:{get:function(){return this._minFilter},set:function(t){this.bind();var e=this.gl;if(this.type===e.FLOAT&&o.indexOf(t)>=0&&(e.getExtension("OES_texture_float_linear")||(t=e.NEAREST)),s.indexOf(t)<0)throw new Error("gl-texture2d: Unknown filter mode "+t);return e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,t),this._minFilter=t}},magFilter:{get:function(){return this._magFilter},set:function(t){this.bind();var e=this.gl;if(this.type===e.FLOAT&&o.indexOf(t)>=0&&(e.getExtension("OES_texture_float_linear")||(t=e.NEAREST)),s.indexOf(t)<0)throw new Error("gl-texture2d: Unknown filter mode "+t);return e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,t),this._magFilter=t}},mipSamples:{get:function(){return this._anisoSamples},set:function(t){var e=this._anisoSamples;if(this._anisoSamples=0|Math.max(t,1),e!==this._anisoSamples){var r=this.gl.getExtension("EXT_texture_filter_anisotropic");r&&this.gl.texParameterf(this.gl.TEXTURE_2D,r.TEXTURE_MAX_ANISOTROPY_EXT,this._anisoSamples)}return this._anisoSamples}},wrapS:{get:function(){return this._wrapS},set:function(t){if(this.bind(),l.indexOf(t)<0)throw new Error("gl-texture2d: Unknown wrap mode "+t);return this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,t),this._wrapS=t}},wrapT:{get:function(){return this._wrapT},set:function(t){if(this.bind(),l.indexOf(t)<0)throw new Error("gl-texture2d: Unknown wrap mode "+t);return this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,t),this._wrapT=t}},wrap:{get:function(){return this._wrapVector},set:function(t){if(Array.isArray(t)||(t=[t,t]),2!==t.length)throw new Error("gl-texture2d: Must specify wrap mode for rows and columns");for(var e=0;e<2;++e)if(l.indexOf(t[e])<0)throw new Error("gl-texture2d: Unknown wrap mode "+t);this._wrapS=t[0],this._wrapT=t[1];var r=this.gl;return this.bind(),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_S,this._wrapS),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_T,this._wrapT),t}},shape:{get:function(){return this._shapeVector},set:function(t){if(Array.isArray(t)){if(2!==t.length)throw new Error("gl-texture2d: Invalid texture shape")}else t=[0|t,0|t];return f(this,0|t[0],0|t[1]),[0|t[0],0|t[1]]}},width:{get:function(){return this._shape[0]},set:function(t){return f(this,t|=0,this._shape[1]),t}},height:{get:function(){return this._shape[1]},set:function(t){return t|=0,f(this,this._shape[0],t),t}}}),p.bind=function(t){var e=this.gl;return void 0!==t&&e.activeTexture(e.TEXTURE0+(0|t)),e.bindTexture(e.TEXTURE_2D,this.handle),void 0!==t?0|t:e.getParameter(e.ACTIVE_TEXTURE)-e.TEXTURE0},p.dispose=function(){this.gl.deleteTexture(this.handle)},p.generateMipmap=function(){this.bind(),this.gl.generateMipmap(this.gl.TEXTURE_2D);for(var t=Math.min(this._shape[0],this._shape[1]),e=0;t>0;++e,t>>>=1)this._mipLevels.indexOf(e)<0&&this._mipLevels.push(e)},p.setPixels=function(t,e,r,o){var s=this.gl;this.bind(),Array.isArray(e)?(o=r,r=0|e[1],e=0|e[0]):(e=e||0,r=r||0),o=o||0;var l=c(t)?t:t.raw;if(l){this._mipLevels.indexOf(o)<0?(s.texImage2D(s.TEXTURE_2D,0,this.format,this.format,this.type,l),this._mipLevels.push(o)):s.texSubImage2D(s.TEXTURE_2D,o,e,r,this.format,this.type,l)}else{if(!(t.shape&&t.stride&&t.data))throw new Error("gl-texture2d: Unsupported data type");if(t.shape.length<2||e+t.shape[1]>this._shape[1]>>>o||r+t.shape[0]>this._shape[0]>>>o||e<0||r<0)throw new Error("gl-texture2d: Texture dimensions are out of bounds");!function(t,e,r,o,s,l,c,f){var h=f.dtype,p=f.shape.slice();if(p.length<2||p.length>3)throw new Error("gl-texture2d: Invalid ndarray, must be 2d or 3d");var g=0,m=0,v=d(p,f.stride.slice());"float32"===h?g=t.FLOAT:"float64"===h?(g=t.FLOAT,v=!1,h="float32"):"uint8"===h?g=t.UNSIGNED_BYTE:(g=t.UNSIGNED_BYTE,v=!1,h="uint8");if(2===p.length)m=t.LUMINANCE,p=[p[0],p[1],1],f=n(f.data,p,[f.stride[0],f.stride[1],1],f.offset);else{if(3!==p.length)throw new Error("gl-texture2d: Invalid shape for texture");if(1===p[2])m=t.ALPHA;else if(2===p[2])m=t.LUMINANCE_ALPHA;else if(3===p[2])m=t.RGB;else{if(4!==p[2])throw new Error("gl-texture2d: Invalid shape for pixel coords");m=t.RGBA}p[2]}m!==t.LUMINANCE&&m!==t.ALPHA||s!==t.LUMINANCE&&s!==t.ALPHA||(m=s);if(m!==s)throw new Error("gl-texture2d: Incompatible texture format for setPixels");var y=f.size,x=c.indexOf(o)<0;x&&c.push(o);if(g===l&&v)0===f.offset&&f.data.length===y?x?t.texImage2D(t.TEXTURE_2D,o,s,p[0],p[1],0,s,l,f.data):t.texSubImage2D(t.TEXTURE_2D,o,e,r,p[0],p[1],s,l,f.data):x?t.texImage2D(t.TEXTURE_2D,o,s,p[0],p[1],0,s,l,f.data.subarray(f.offset,f.offset+y)):t.texSubImage2D(t.TEXTURE_2D,o,e,r,p[0],p[1],s,l,f.data.subarray(f.offset,f.offset+y));else{var b;b=l===t.FLOAT?a.mallocFloat32(y):a.mallocUint8(y);var _=n(b,p,[p[2],p[2]*p[0],1]);g===t.FLOAT&&l===t.UNSIGNED_BYTE?u(_,f):i.assign(_,f),x?t.texImage2D(t.TEXTURE_2D,o,s,p[0],p[1],0,s,l,b.subarray(0,y)):t.texSubImage2D(t.TEXTURE_2D,o,e,r,p[0],p[1],s,l,b.subarray(0,y)),l===t.FLOAT?a.freeFloat32(b):a.freeUint8(b)}}(s,e,r,o,this.format,this.type,this._mipLevels,t)}}},{ndarray:393,"ndarray-ops":387,"typedarray-pool":481}],281:[function(t,e,r){"use strict";e.exports=function(t,e,r){e?e.bind():t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,null);var n=0|t.getParameter(t.MAX_VERTEX_ATTRIBS);if(r){if(r.length>n)throw new Error("gl-vao: Too many vertex attributes");for(var i=0;i1?0:Math.acos(s)};var n=t("./fromValues"),i=t("./normalize"),a=t("./dot")},{"./dot":293,"./fromValues":295,"./normalize":304}],287:[function(t,e,r){e.exports=function(t){var e=new Float32Array(3);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e}},{}],288:[function(t,e,r){e.exports=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t}},{}],289:[function(t,e,r){e.exports=function(){var t=new Float32Array(3);return t[0]=0,t[1]=0,t[2]=0,t}},{}],290:[function(t,e,r){e.exports=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],l=r[2];return t[0]=i*l-a*s,t[1]=a*o-n*l,t[2]=n*s-i*o,t}},{}],291:[function(t,e,r){e.exports=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2];return Math.sqrt(r*r+n*n+i*i)}},{}],292:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t}},{}],293:[function(t,e,r){e.exports=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}},{}],294:[function(t,e,r){e.exports=function(t,e,r,i,a,o){var s,l;e||(e=3);r||(r=0);l=i?Math.min(i*e+r,t.length):t.length;for(s=r;s0&&(a=1/Math.sqrt(a),t[0]=e[0]*a,t[1]=e[1]*a,t[2]=e[2]*a);return t}},{}],305:[function(t,e,r){e.exports=function(t,e){e=e||1;var r=2*Math.random()*Math.PI,n=2*Math.random()-1,i=Math.sqrt(1-n*n)*e;return t[0]=Math.cos(r)*i,t[1]=Math.sin(r)*i,t[2]=n*e,t}},{}],306:[function(t,e,r){e.exports=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[0],a[1]=i[1]*Math.cos(n)-i[2]*Math.sin(n),a[2]=i[1]*Math.sin(n)+i[2]*Math.cos(n),t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t}},{}],307:[function(t,e,r){e.exports=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[2]*Math.sin(n)+i[0]*Math.cos(n),a[1]=i[1],a[2]=i[2]*Math.cos(n)-i[0]*Math.sin(n),t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t}},{}],308:[function(t,e,r){e.exports=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[0]*Math.cos(n)-i[1]*Math.sin(n),a[1]=i[0]*Math.sin(n)+i[1]*Math.cos(n),a[2]=i[2],t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t}},{}],309:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t}},{}],310:[function(t,e,r){e.exports=function(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t}},{}],311:[function(t,e,r){e.exports=function(t,e,r,n){return t[0]=e,t[1]=r,t[2]=n,t}},{}],312:[function(t,e,r){e.exports=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2];return r*r+n*n+i*i}},{}],313:[function(t,e,r){e.exports=function(t){var e=t[0],r=t[1],n=t[2];return e*e+r*r+n*n}},{}],314:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t}},{}],315:[function(t,e,r){e.exports=function(t,e,r){var n=e[0],i=e[1],a=e[2];return t[0]=n*r[0]+i*r[3]+a*r[6],t[1]=n*r[1]+i*r[4]+a*r[7],t[2]=n*r[2]+i*r[5]+a*r[8],t}},{}],316:[function(t,e,r){e.exports=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[3]*n+r[7]*i+r[11]*a+r[15];return o=o||1,t[0]=(r[0]*n+r[4]*i+r[8]*a+r[12])/o,t[1]=(r[1]*n+r[5]*i+r[9]*a+r[13])/o,t[2]=(r[2]*n+r[6]*i+r[10]*a+r[14])/o,t}},{}],317:[function(t,e,r){e.exports=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],l=r[2],c=r[3],u=c*n+s*a-l*i,f=c*i+l*n-o*a,h=c*a+o*i-s*n,p=-o*n-s*i-l*a;return t[0]=u*c+p*-o+f*-l-h*-s,t[1]=f*c+p*-s+h*-o-u*-l,t[2]=h*c+p*-l+u*-s-f*-o,t}},{}],318:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t}},{}],319:[function(t,e,r){e.exports=function(t){var e=new Float32Array(4);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}},{}],320:[function(t,e,r){e.exports=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}},{}],321:[function(t,e,r){e.exports=function(){var t=new Float32Array(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t}},{}],322:[function(t,e,r){e.exports=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return Math.sqrt(r*r+n*n+i*i+a*a)}},{}],323:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t[3]=e[3]/r[3],t}},{}],324:[function(t,e,r){e.exports=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]}},{}],325:[function(t,e,r){e.exports=function(t,e,r,n){var i=new Float32Array(4);return i[0]=t,i[1]=e,i[2]=r,i[3]=n,i}},{}],326:[function(t,e,r){e.exports={create:t("./create"),clone:t("./clone"),fromValues:t("./fromValues"),copy:t("./copy"),set:t("./set"),add:t("./add"),subtract:t("./subtract"),multiply:t("./multiply"),divide:t("./divide"),min:t("./min"),max:t("./max"),scale:t("./scale"),scaleAndAdd:t("./scaleAndAdd"),distance:t("./distance"),squaredDistance:t("./squaredDistance"),length:t("./length"),squaredLength:t("./squaredLength"),negate:t("./negate"),inverse:t("./inverse"),normalize:t("./normalize"),dot:t("./dot"),lerp:t("./lerp"),random:t("./random"),transformMat4:t("./transformMat4"),transformQuat:t("./transformQuat")}},{"./add":318,"./clone":319,"./copy":320,"./create":321,"./distance":322,"./divide":323,"./dot":324,"./fromValues":325,"./inverse":327,"./length":328,"./lerp":329,"./max":330,"./min":331,"./multiply":332,"./negate":333,"./normalize":334,"./random":335,"./scale":336,"./scaleAndAdd":337,"./set":338,"./squaredDistance":339,"./squaredLength":340,"./subtract":341,"./transformMat4":342,"./transformQuat":343}],327:[function(t,e,r){e.exports=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t[2]=1/e[2],t[3]=1/e[3],t}},{}],328:[function(t,e,r){e.exports=function(t){var e=t[0],r=t[1],n=t[2],i=t[3];return Math.sqrt(e*e+r*r+n*n+i*i)}},{}],329:[function(t,e,r){e.exports=function(t,e,r,n){var i=e[0],a=e[1],o=e[2],s=e[3];return t[0]=i+n*(r[0]-i),t[1]=a+n*(r[1]-a),t[2]=o+n*(r[2]-o),t[3]=s+n*(r[3]-s),t}},{}],330:[function(t,e,r){e.exports=function(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t[2]=Math.max(e[2],r[2]),t[3]=Math.max(e[3],r[3]),t}},{}],331:[function(t,e,r){e.exports=function(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t[2]=Math.min(e[2],r[2]),t[3]=Math.min(e[3],r[3]),t}},{}],332:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t[3]=e[3]*r[3],t}},{}],333:[function(t,e,r){e.exports=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=-e[3],t}},{}],334:[function(t,e,r){e.exports=function(t,e){var r=e[0],n=e[1],i=e[2],a=e[3],o=r*r+n*n+i*i+a*a;o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=n*o,t[2]=i*o,t[3]=a*o);return t}},{}],335:[function(t,e,r){var n=t("./normalize"),i=t("./scale");e.exports=function(t,e){return e=e||1,t[0]=Math.random(),t[1]=Math.random(),t[2]=Math.random(),t[3]=Math.random(),n(t,t),i(t,t,e),t}},{"./normalize":334,"./scale":336}],336:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t}},{}],337:[function(t,e,r){e.exports=function(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t}},{}],338:[function(t,e,r){e.exports=function(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t}},{}],339:[function(t,e,r){e.exports=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return r*r+n*n+i*i+a*a}},{}],340:[function(t,e,r){e.exports=function(t){var e=t[0],r=t[1],n=t[2],i=t[3];return e*e+r*r+n*n+i*i}},{}],341:[function(t,e,r){e.exports=function(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t}},{}],342:[function(t,e,r){e.exports=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=e[3];return t[0]=r[0]*n+r[4]*i+r[8]*a+r[12]*o,t[1]=r[1]*n+r[5]*i+r[9]*a+r[13]*o,t[2]=r[2]*n+r[6]*i+r[10]*a+r[14]*o,t[3]=r[3]*n+r[7]*i+r[11]*a+r[15]*o,t}},{}],343:[function(t,e,r){e.exports=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],l=r[2],c=r[3],u=c*n+s*a-l*i,f=c*i+l*n-o*a,h=c*a+o*i-s*n,p=-o*n-s*i-l*a;return t[0]=u*c+p*-o+f*-l-h*-s,t[1]=f*c+p*-s+h*-o-u*-l,t[2]=h*c+p*-l+u*-s-f*-o,t[3]=e[3],t}},{}],344:[function(t,e,r){e.exports=function(t,e,r,a){return n[0]=a,n[1]=r,n[2]=e,n[3]=t,i[0]};var n=new Uint8Array(4),i=new Float32Array(n.buffer)},{}],345:[function(t,e,r){var n=t("glsl-tokenizer"),i=t("atob-lite");e.exports=function(t){for(var e=Array.isArray(t)?t:n(t),r=0;r0)continue;r=t.slice(0,1).join("")}return B(r),z+=r.length,(S=S.slice(r.length)).length}}function H(){return/[^a-fA-F0-9]/.test(e)?(B(S.join("")),T=l,M):(S.push(e),r=e,M+1)}function G(){return"."===e?(S.push(e),T=g,r=e,M+1):/[eE]/.test(e)?(S.push(e),T=g,r=e,M+1):"x"===e&&1===S.length&&"0"===S[0]?(T=_,S.push(e),r=e,M+1):/[^\d]/.test(e)?(B(S.join("")),T=l,M):(S.push(e),r=e,M+1)}function W(){return"f"===e&&(S.push(e),r=e,M+=1),/[eE]/.test(e)?(S.push(e),r=e,M+1):"-"===e&&/[eE]/.test(r)?(S.push(e),r=e,M+1):/[^\d]/.test(e)?(B(S.join("")),T=l,M):(S.push(e),r=e,M+1)}function Y(){if(/[^\d\w_]/.test(e)){var t=S.join("");return T=R.indexOf(t)>-1?y:I.indexOf(t)>-1?v:m,B(S.join("")),T=l,M}return S.push(e),r=e,M+1}};var n=t("./lib/literals"),i=t("./lib/operators"),a=t("./lib/builtins"),o=t("./lib/literals-300es"),s=t("./lib/builtins-300es"),l=999,c=9999,u=0,f=1,h=2,p=3,d=4,g=5,m=6,v=7,y=8,x=9,b=10,_=11,w=["block-comment","line-comment","preprocessor","operator","integer","float","ident","builtin","keyword","whitespace","eof","integer"]},{"./lib/builtins":348,"./lib/builtins-300es":347,"./lib/literals":350,"./lib/literals-300es":349,"./lib/operators":351}],347:[function(t,e,r){var n=t("./builtins");n=n.slice().filter(function(t){return!/^(gl\_|texture)/.test(t)}),e.exports=n.concat(["gl_VertexID","gl_InstanceID","gl_Position","gl_PointSize","gl_FragCoord","gl_FrontFacing","gl_FragDepth","gl_PointCoord","gl_MaxVertexAttribs","gl_MaxVertexUniformVectors","gl_MaxVertexOutputVectors","gl_MaxFragmentInputVectors","gl_MaxVertexTextureImageUnits","gl_MaxCombinedTextureImageUnits","gl_MaxTextureImageUnits","gl_MaxFragmentUniformVectors","gl_MaxDrawBuffers","gl_MinProgramTexelOffset","gl_MaxProgramTexelOffset","gl_DepthRangeParameters","gl_DepthRange","trunc","round","roundEven","isnan","isinf","floatBitsToInt","floatBitsToUint","intBitsToFloat","uintBitsToFloat","packSnorm2x16","unpackSnorm2x16","packUnorm2x16","unpackUnorm2x16","packHalf2x16","unpackHalf2x16","outerProduct","transpose","determinant","inverse","texture","textureSize","textureProj","textureLod","textureOffset","texelFetch","texelFetchOffset","textureProjOffset","textureLodOffset","textureProjLod","textureProjLodOffset","textureGrad","textureGradOffset","textureProjGrad","textureProjGradOffset"])},{"./builtins":348}],348:[function(t,e,r){e.exports=["abs","acos","all","any","asin","atan","ceil","clamp","cos","cross","dFdx","dFdy","degrees","distance","dot","equal","exp","exp2","faceforward","floor","fract","gl_BackColor","gl_BackLightModelProduct","gl_BackLightProduct","gl_BackMaterial","gl_BackSecondaryColor","gl_ClipPlane","gl_ClipVertex","gl_Color","gl_DepthRange","gl_DepthRangeParameters","gl_EyePlaneQ","gl_EyePlaneR","gl_EyePlaneS","gl_EyePlaneT","gl_Fog","gl_FogCoord","gl_FogFragCoord","gl_FogParameters","gl_FragColor","gl_FragCoord","gl_FragData","gl_FragDepth","gl_FragDepthEXT","gl_FrontColor","gl_FrontFacing","gl_FrontLightModelProduct","gl_FrontLightProduct","gl_FrontMaterial","gl_FrontSecondaryColor","gl_LightModel","gl_LightModelParameters","gl_LightModelProducts","gl_LightProducts","gl_LightSource","gl_LightSourceParameters","gl_MaterialParameters","gl_MaxClipPlanes","gl_MaxCombinedTextureImageUnits","gl_MaxDrawBuffers","gl_MaxFragmentUniformComponents","gl_MaxLights","gl_MaxTextureCoords","gl_MaxTextureImageUnits","gl_MaxTextureUnits","gl_MaxVaryingFloats","gl_MaxVertexAttribs","gl_MaxVertexTextureImageUnits","gl_MaxVertexUniformComponents","gl_ModelViewMatrix","gl_ModelViewMatrixInverse","gl_ModelViewMatrixInverseTranspose","gl_ModelViewMatrixTranspose","gl_ModelViewProjectionMatrix","gl_ModelViewProjectionMatrixInverse","gl_ModelViewProjectionMatrixInverseTranspose","gl_ModelViewProjectionMatrixTranspose","gl_MultiTexCoord0","gl_MultiTexCoord1","gl_MultiTexCoord2","gl_MultiTexCoord3","gl_MultiTexCoord4","gl_MultiTexCoord5","gl_MultiTexCoord6","gl_MultiTexCoord7","gl_Normal","gl_NormalMatrix","gl_NormalScale","gl_ObjectPlaneQ","gl_ObjectPlaneR","gl_ObjectPlaneS","gl_ObjectPlaneT","gl_Point","gl_PointCoord","gl_PointParameters","gl_PointSize","gl_Position","gl_ProjectionMatrix","gl_ProjectionMatrixInverse","gl_ProjectionMatrixInverseTranspose","gl_ProjectionMatrixTranspose","gl_SecondaryColor","gl_TexCoord","gl_TextureEnvColor","gl_TextureMatrix","gl_TextureMatrixInverse","gl_TextureMatrixInverseTranspose","gl_TextureMatrixTranspose","gl_Vertex","greaterThan","greaterThanEqual","inversesqrt","length","lessThan","lessThanEqual","log","log2","matrixCompMult","max","min","mix","mod","normalize","not","notEqual","pow","radians","reflect","refract","sign","sin","smoothstep","sqrt","step","tan","texture2D","texture2DLod","texture2DProj","texture2DProjLod","textureCube","textureCubeLod","texture2DLodEXT","texture2DProjLodEXT","textureCubeLodEXT","texture2DGradEXT","texture2DProjGradEXT","textureCubeGradEXT"]},{}],349:[function(t,e,r){var n=t("./literals");e.exports=n.slice().concat(["layout","centroid","smooth","case","mat2x2","mat2x3","mat2x4","mat3x2","mat3x3","mat3x4","mat4x2","mat4x3","mat4x4","uint","uvec2","uvec3","uvec4","samplerCubeShadow","sampler2DArray","sampler2DArrayShadow","isampler2D","isampler3D","isamplerCube","isampler2DArray","usampler2D","usampler3D","usamplerCube","usampler2DArray","coherent","restrict","readonly","writeonly","resource","atomic_uint","noperspective","patch","sample","subroutine","common","partition","active","filter","image1D","image2D","image3D","imageCube","iimage1D","iimage2D","iimage3D","iimageCube","uimage1D","uimage2D","uimage3D","uimageCube","image1DArray","image2DArray","iimage1DArray","iimage2DArray","uimage1DArray","uimage2DArray","image1DShadow","image2DShadow","image1DArrayShadow","image2DArrayShadow","imageBuffer","iimageBuffer","uimageBuffer","sampler1DArray","sampler1DArrayShadow","isampler1D","isampler1DArray","usampler1D","usampler1DArray","isampler2DRect","usampler2DRect","samplerBuffer","isamplerBuffer","usamplerBuffer","sampler2DMS","isampler2DMS","usampler2DMS","sampler2DMSArray","isampler2DMSArray","usampler2DMSArray"])},{"./literals":350}],350:[function(t,e,r){e.exports=["precision","highp","mediump","lowp","attribute","const","uniform","varying","break","continue","do","for","while","if","else","in","out","inout","float","int","void","bool","true","false","discard","return","mat2","mat3","mat4","vec2","vec3","vec4","ivec2","ivec3","ivec4","bvec2","bvec3","bvec4","sampler1D","sampler2D","sampler3D","samplerCube","sampler1DShadow","sampler2DShadow","struct","asm","class","union","enum","typedef","template","this","packed","goto","switch","default","inline","noinline","volatile","public","static","extern","external","interface","long","short","double","half","fixed","unsigned","input","output","hvec2","hvec3","hvec4","dvec2","dvec3","dvec4","fvec2","fvec3","fvec4","sampler2DRect","sampler3DRect","sampler2DRectShadow","sizeof","cast","namespace","using"]},{}],351:[function(t,e,r){e.exports=["<<=",">>=","++","--","<<",">>","<=",">=","==","!=","&&","||","+=","-=","*=","/=","%=","&=","^^","^=","|=","(",")","[","]",".","!","~","*","/","%","+","-","<",">","&","^","|","?",":","=",",",";","{","}"]},{}],352:[function(t,e,r){var n=t("./index");e.exports=function(t,e){var r=n(e),i=[];return i=(i=i.concat(r(t))).concat(r(null))}},{"./index":346}],353:[function(t,e,r){e.exports=function(t){"string"==typeof t&&(t=[t]);for(var e=[].slice.call(arguments,1),r=[],n=0;n>1,u=-7,f=r?i-1:0,h=r?-1:1,p=t[e+f];for(f+=h,a=p&(1<<-u)-1,p>>=-u,u+=s;u>0;a=256*a+t[e+f],f+=h,u-=8);for(o=a&(1<<-u)-1,a>>=-u,u+=n;u>0;o=256*o+t[e+f],f+=h,u-=8);if(0===a)a=1-c;else{if(a===l)return o?NaN:1/0*(p?-1:1);o+=Math.pow(2,n),a-=c}return(p?-1:1)*o*Math.pow(2,a-n)},r.write=function(t,e,r,n,i,a){var o,s,l,c=8*a-i-1,u=(1<>1,h=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,p=n?0:a-1,d=n?1:-1,g=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(s=isNaN(e)?1:0,o=u):(o=Math.floor(Math.log(e)/Math.LN2),e*(l=Math.pow(2,-o))<1&&(o--,l*=2),(e+=o+f>=1?h/l:h*Math.pow(2,1-f))*l>=2&&(o++,l/=2),o+f>=u?(s=0,o=u):o+f>=1?(s=(e*l-1)*Math.pow(2,i),o+=f):(s=e*Math.pow(2,f-1)*Math.pow(2,i),o=0));i>=8;t[r+p]=255&s,p+=d,s/=256,i-=8);for(o=o<0;t[r+p]=255&o,p+=d,o/=256,c-=8);t[r+p-d]|=128*g}},{}],357:[function(t,e,r){"use strict";e.exports=function(t,e){var r=t.length;if(0===r)throw new Error("Must have at least d+1 points");var i=t[0].length;if(r<=i)throw new Error("Must input at least d+1 points");var o=t.slice(0,i+1),s=n.apply(void 0,o);if(0===s)throw new Error("Input not in general position");for(var l=new Array(i+1),u=0;u<=i;++u)l[u]=u;s<0&&(l[0]=1,l[1]=0);for(var f=new a(l,new Array(i+1),!1),h=f.adjacent,p=new Array(i+2),u=0;u<=i;++u){for(var d=l.slice(),g=0;g<=i;++g)g===u&&(d[g]=-1);var m=d[0];d[0]=d[1],d[1]=m;var v=new a(d,new Array(i+1),!0);h[u]=v,p[u]=v}p[i+1]=f;for(var u=0;u<=i;++u)for(var d=h[u].vertices,y=h[u].adjacent,g=0;g<=i;++g){var x=d[g];if(x<0)y[g]=f;else for(var b=0;b<=i;++b)h[b].vertices.indexOf(x)<0&&(y[g]=h[b])}for(var _=new c(i,o,p),w=!!e,u=i+1;u0&&e.push(","),e.push("tuple[",r,"]");e.push(")}return orient");var i=new Function("test",e.join("")),a=n[t+1];return a||(a=n),i(a)}(t)),this.orient=a}var u=c.prototype;u.handleBoundaryDegeneracy=function(t,e){var r=this.dimension,n=this.vertices.length-1,i=this.tuple,a=this.vertices,o=[t];for(t.lastVisited=-n;o.length>0;){(t=o.pop()).vertices;for(var s=t.adjacent,l=0;l<=r;++l){var c=s[l];if(c.boundary&&!(c.lastVisited<=-n)){for(var u=c.vertices,f=0;f<=r;++f){var h=u[f];i[f]=h<0?e:a[h]}var p=this.orient();if(p>0)return c;c.lastVisited=-n,0===p&&o.push(c)}}}return null},u.walk=function(t,e){var r=this.vertices.length-1,n=this.dimension,i=this.vertices,a=this.tuple,o=e?this.interior.length*Math.random()|0:this.interior.length-1,s=this.interior[o];t:for(;!s.boundary;){for(var l=s.vertices,c=s.adjacent,u=0;u<=n;++u)a[u]=i[l[u]];s.lastVisited=r;for(u=0;u<=n;++u){var f=c[u];if(!(f.lastVisited>=r)){var h=a[u];a[u]=t;var p=this.orient();if(a[u]=h,p<0){s=f;continue t}f.boundary?f.lastVisited=-r:f.lastVisited=r}}return}return s},u.addPeaks=function(t,e){var r=this.vertices.length-1,n=this.dimension,i=this.vertices,l=this.tuple,c=this.interior,u=this.simplices,f=[e];e.lastVisited=r,e.vertices[e.vertices.indexOf(-1)]=r,e.boundary=!1,c.push(e);for(var h=[];f.length>0;){var p=(e=f.pop()).vertices,d=e.adjacent,g=p.indexOf(r);if(!(g<0))for(var m=0;m<=n;++m)if(m!==g){var v=d[m];if(v.boundary&&!(v.lastVisited>=r)){var y=v.vertices;if(v.lastVisited!==-r){for(var x=0,b=0;b<=n;++b)y[b]<0?(x=b,l[b]=t):l[b]=i[y[b]];if(this.orient()>0){y[x]=r,v.boundary=!1,c.push(v),f.push(v),v.lastVisited=r;continue}v.lastVisited=-r}var _=v.adjacent,w=p.slice(),k=d.slice(),M=new a(w,k,!0);u.push(M);var A=_.indexOf(e);if(!(A<0)){_[A]=M,k[g]=v,w[m]=-1,k[m]=e,d[m]=M,M.flip();for(b=0;b<=n;++b){var T=w[b];if(!(T<0||T===r)){for(var S=new Array(n-1),C=0,E=0;E<=n;++E){var L=w[E];L<0||E===b||(S[C++]=L)}h.push(new o(S,M,b))}}}}}}h.sort(s);for(m=0;m+1=0?o[l++]=s[u]:c=1&u;if(c===(1&t)){var f=o[0];o[0]=o[1],o[1]=f}e.push(o)}}return e}},{"robust-orientation":446,"simplicial-complex":456}],358:[function(t,e,r){"use strict";var n=t("binary-search-bounds"),i=0,a=1;function o(t,e,r,n,i){this.mid=t,this.left=e,this.right=r,this.leftPoints=n,this.rightPoints=i,this.count=(e?e.count:0)+(r?r.count:0)+n.length}e.exports=function(t){if(!t||0===t.length)return new x(null);return new x(y(t))};var s=o.prototype;function l(t,e){t.mid=e.mid,t.left=e.left,t.right=e.right,t.leftPoints=e.leftPoints,t.rightPoints=e.rightPoints,t.count=e.count}function c(t,e){var r=y(e);t.mid=r.mid,t.left=r.left,t.right=r.right,t.leftPoints=r.leftPoints,t.rightPoints=r.rightPoints,t.count=r.count}function u(t,e){var r=t.intervals([]);r.push(e),c(t,r)}function f(t,e){var r=t.intervals([]),n=r.indexOf(e);return n<0?i:(r.splice(n,1),c(t,r),a)}function h(t,e,r){for(var n=0;n=0&&t[n][1]>=e;--n){var i=r(t[n]);if(i)return i}}function d(t,e){for(var r=0;r>1],i=[],a=[],s=[];for(r=0;r3*(e+1)?u(this,t):this.left.insert(t):this.left=y([t]);else if(t[0]>this.mid)this.right?4*(this.right.count+1)>3*(e+1)?u(this,t):this.right.insert(t):this.right=y([t]);else{var r=n.ge(this.leftPoints,t,m),i=n.ge(this.rightPoints,t,v);this.leftPoints.splice(r,0,t),this.rightPoints.splice(i,0,t)}},s.remove=function(t){var e=this.count-this.leftPoints;if(t[1]3*(e-1)?f(this,t):2===(c=this.left.remove(t))?(this.left=null,this.count-=1,a):(c===a&&(this.count-=1),c):i;if(t[0]>this.mid)return this.right?4*(this.left?this.left.count:0)>3*(e-1)?f(this,t):2===(c=this.right.remove(t))?(this.right=null,this.count-=1,a):(c===a&&(this.count-=1),c):i;if(1===this.count)return this.leftPoints[0]===t?2:i;if(1===this.leftPoints.length&&this.leftPoints[0]===t){if(this.left&&this.right){for(var r=this,o=this.left;o.right;)r=o,o=o.right;if(r===this)o.right=this.right;else{var s=this.left,c=this.right;r.count-=o.count,r.right=o.left,o.left=s,o.right=c}l(this,o),this.count=(this.left?this.left.count:0)+(this.right?this.right.count:0)+this.leftPoints.length}else this.left?l(this,this.left):l(this,this.right);return a}for(s=n.ge(this.leftPoints,t,m);sthis.mid){var r;if(this.right)if(r=this.right.queryPoint(t,e))return r;return p(this.rightPoints,t,e)}return d(this.leftPoints,e)},s.queryInterval=function(t,e,r){var n;if(tthis.mid&&this.right&&(n=this.right.queryInterval(t,e,r)))return n;return ethis.mid?p(this.rightPoints,t,r):d(this.leftPoints,r)};var b=x.prototype;b.insert=function(t){this.root?this.root.insert(t):this.root=new o(t[0],null,null,[t],[t])},b.remove=function(t){if(this.root){var e=this.root.remove(t);return 2===e&&(this.root=null),e!==i}return!1},b.queryPoint=function(t,e){if(this.root)return this.root.queryPoint(t,e)},b.queryInterval=function(t,e,r){if(t<=e&&this.root)return this.root.queryInterval(t,e,r)},Object.defineProperty(b,"count",{get:function(){return this.root?this.root.count:0}}),Object.defineProperty(b,"intervals",{get:function(){return this.root?this.root.intervals([]):[]}})},{"binary-search-bounds":73}],359:[function(t,e,r){"use strict";e.exports=function(t,e){e=e||new Array(t.length);for(var r=0;r4))}},{}],368:[function(t,e,r){e.exports=function(t,e,r){return t*(1-r)+e*r}},{}],369:[function(t,e,r){(function(n){"use strict";!function(t){"object"==typeof r&&void 0!==e?e.exports=t():("undefined"!=typeof window?window:void 0!==n?n:"undefined"!=typeof self?self:this).mapboxgl=t()}(function(){return function e(r,n,i){function a(s,l){if(!n[s]){if(!r[s]){var c="function"==typeof t&&t;if(!l&&c)return c(s,!0);if(o)return o(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var f=n[s]={exports:{}};r[s][0].call(f.exports,function(t){var e=r[s][1][t];return a(e||t)},f,f.exports,e,r,n,i)}return n[s].exports}for(var o="function"==typeof t&&t,s=0;s0){e+=Math.abs(i(t[0]));for(var r=1;r2){for(l=0;li.maxh||t>i.maxw||r<=i.maxh&&t<=i.maxw&&(o=i.maxw*i.maxh-t*r)a.free)){if(r===a.h)return this.allocShelf(s,t,r,n);r>a.h||ru)&&(f=2*Math.max(t,u)),(ll)&&(c=2*Math.max(r,l)),this.resize(f,c),this.packOne(t,r,n)):null},t.prototype.allocFreebin=function(t,e,r,n){var i=this.freebins.splice(t,1)[0];return i.id=n,i.w=e,i.h=r,i.refcount=0,this.bins[n]=i,this.ref(i),i},t.prototype.allocShelf=function(t,e,r,n){var i=this.shelves[t].alloc(e,r,n);return this.bins[n]=i,this.ref(i),i},t.prototype.shrink=function(){if(this.shelves.length>0){for(var t=0,e=0,r=0;rthis.free||e>this.h)return null;var i=this.x;return this.x+=t,this.free-=t,new r(n,i,this.y,t,e,t,this.h)},e.prototype.resize=function(t){return this.free+=t-this.w,this.w=t,!0},t},"object"==typeof r&&void 0!==e?e.exports=i():n.ShelfPack=i()},{}],6:[function(t,e,r){function n(t,e,r,n,i,a){this.fontSize=t||24,this.buffer=void 0===e?3:e,this.cutoff=n||.25,this.fontFamily=i||"sans-serif",this.fontWeight=a||"normal",this.radius=r||8;var o=this.size=this.fontSize+2*this.buffer;this.canvas=document.createElement("canvas"),this.canvas.width=this.canvas.height=o,this.ctx=this.canvas.getContext("2d"),this.ctx.font=this.fontWeight+" "+this.fontSize+"px "+this.fontFamily,this.ctx.textBaseline="middle",this.ctx.fillStyle="black",this.gridOuter=new Float64Array(o*o),this.gridInner=new Float64Array(o*o),this.f=new Float64Array(o),this.d=new Float64Array(o),this.z=new Float64Array(o+1),this.v=new Int16Array(o),this.middle=Math.round(o/2*(navigator.userAgent.indexOf("Gecko/")>=0?1.2:1))}function i(t,e,r,n,i,o,s){for(var l=0;l(n=1))return n;for(;ra?r=i:n=i,i=.5*(n-r)+r}return i},n.prototype.solve=function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}},{}],8:[function(t,e,r){e.exports.VectorTile=t("./lib/vectortile.js"),e.exports.VectorTileFeature=t("./lib/vectortilefeature.js"),e.exports.VectorTileLayer=t("./lib/vectortilelayer.js")},{"./lib/vectortile.js":9,"./lib/vectortilefeature.js":10,"./lib/vectortilelayer.js":11}],9:[function(t,e,r){function n(t,e,r){if(3===t){var n=new i(r,r.readVarint()+r.pos);n.length&&(e[n.name]=n)}}var i=t("./vectortilelayer");e.exports=function(t,e){this.layers=t.readFields(n,{},e)}},{"./vectortilelayer":11}],10:[function(t,e,r){function n(t,e,r,n,a){this.properties={},this.extent=r,this.type=0,this._pbf=t,this._geometry=-1,this._keys=n,this._values=a,t.readFields(i,this,e)}function i(t,e,r){1==t?e.id=r.readVarint():2==t?function(t,e){for(var r=t.readVarint()+t.pos;t.pos>3}if(i--,1===n||2===n)a+=t.readSVarint(),s+=t.readSVarint(),1===n&&(e&&l.push(e),e=[]),e.push(new o(a,s));else{if(7!==n)throw new Error("unknown command "+n);e&&e.push(e[0].clone())}}return e&&l.push(e),l},n.prototype.bbox=function(){var t=this._pbf;t.pos=this._geometry;for(var e=t.readVarint()+t.pos,r=1,n=0,i=0,a=0,o=1/0,s=-1/0,l=1/0,c=-1/0;t.pos>3}if(n--,1===r||2===r)(i+=t.readSVarint())s&&(s=i),(a+=t.readSVarint())c&&(c=a);else if(7!==r)throw new Error("unknown command "+r)}return[o,l,s,c]},n.prototype.toGeoJSON=function(t,e,r){function i(t){for(var e=0;e>3;e=1===n?t.readString():2===n?t.readFloat():3===n?t.readDouble():4===n?t.readVarint64():5===n?t.readVarint():6===n?t.readSVarint():7===n?t.readBoolean():null}return e}(r))}var a=t("./vectortilefeature.js");e.exports=n,n.prototype.feature=function(t){if(t<0||t>=this._features.length)throw new Error("feature index out of bounds");this._pbf.pos=this._features[t];var e=this._pbf.readVarint()+this._pbf.pos;return new a(this._pbf,e,this.extent,this._keys,this._values)}},{"./vectortilefeature.js":10}],12:[function(t,e,r){var n;n=this,function(t){function e(t,e,n){var i=r(256*t,256*(e=Math.pow(2,n)-e-1),n),a=r(256*(t+1),256*(e+1),n);return i[0]+","+i[1]+","+a[0]+","+a[1]}function r(t,e,r){var n=2*Math.PI*6378137/256/Math.pow(2,r);return[t*n-2*Math.PI*6378137/2,e*n-2*Math.PI*6378137/2]}t.getURL=function(t,r,n,i,a,o){return o=o||{},t+"?"+["bbox="+e(n,i,a),"format="+(o.format||"image/png"),"service="+(o.service||"WMS"),"version="+(o.version||"1.1.1"),"request="+(o.request||"GetMap"),"srs="+(o.srs||"EPSG:3857"),"width="+(o.width||256),"height="+(o.height||256),"layers="+r].join("&")},t.getTileBBox=e,t.getMercCoords=r,Object.defineProperty(t,"__esModule",{value:!0})}("object"==typeof r&&void 0!==e?r:n.WhooTS=n.WhooTS||{})},{}],13:[function(t,e,r){function n(t){return(t=Math.round(t))<0?0:t>255?255:t}function i(t){return n("%"===t[t.length-1]?parseFloat(t)/100*255:parseInt(t))}function a(t){return function(t){return t<0?0:t>1?1:t}("%"===t[t.length-1]?parseFloat(t)/100:parseFloat(t))}function o(t,e,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?t+(e-t)*r*6:2*r<1?e:3*r<2?t+(e-t)*(2/3-r)*6:t}var s={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],rebeccapurple:[102,51,153,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};try{r.parseCSSColor=function(t){var e,r=t.replace(/ /g,"").toLowerCase();if(r in s)return s[r].slice();if("#"===r[0])return 4===r.length?(e=parseInt(r.substr(1),16))>=0&&e<=4095?[(3840&e)>>4|(3840&e)>>8,240&e|(240&e)>>4,15&e|(15&e)<<4,1]:null:7===r.length&&(e=parseInt(r.substr(1),16))>=0&&e<=16777215?[(16711680&e)>>16,(65280&e)>>8,255&e,1]:null;var l=r.indexOf("("),c=r.indexOf(")");if(-1!==l&&c+1===r.length){var u=r.substr(0,l),f=r.substr(l+1,c-(l+1)).split(","),h=1;switch(u){case"rgba":if(4!==f.length)return null;h=a(f.pop());case"rgb":return 3!==f.length?null:[i(f[0]),i(f[1]),i(f[2]),h];case"hsla":if(4!==f.length)return null;h=a(f.pop());case"hsl":if(3!==f.length)return null;var p=(parseFloat(f[0])%360+360)%360/360,d=a(f[1]),g=a(f[2]),m=g<=.5?g*(d+1):g+d-g*d,v=2*g-m;return[n(255*o(v,m,p+1/3)),n(255*o(v,m,p)),n(255*o(v,m,p-1/3)),h];default:return null}}return null}}catch(t){}},{}],14:[function(t,e,r){function n(t,e,r){r=r||2;var n,s,l,c,u,p,g,m=e&&e.length,v=m?e[0]*r:t.length,y=i(t,0,v,r,!0),x=[];if(!y)return x;if(m&&(y=function(t,e,r,n){var o,s,l,c,u,p=[];for(o=0,s=e.length;o80*r){n=l=t[0],s=c=t[1];for(var b=r;bl&&(l=u),p>c&&(c=p);g=0!==(g=Math.max(l-n,c-s))?1/g:0}return o(y,x,r,n,s,g),x}function i(t,e,r,n,i){var a,o;if(i===A(t,e,r,n)>0)for(a=e;a=e;a-=n)o=w(a,t[a],t[a+1],o);return o&&y(o,o.next)&&(k(o),o=o.next),o}function a(t,e){if(!t)return t;e||(e=t);var r,n=t;do{if(r=!1,n.steiner||!y(n,n.next)&&0!==v(n.prev,n,n.next))n=n.next;else{if(k(n),(n=e=n.prev)===n.next)break;r=!0}}while(r||n!==e);return e}function o(t,e,r,n,i,f,h){if(t){!h&&f&&function(t,e,r,n){var i=t;do{null===i.z&&(i.z=p(i.x,i.y,e,r,n)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next}while(i!==t);i.prevZ.nextZ=null,i.prevZ=null,function(t){var e,r,n,i,a,o,s,l,c=1;do{for(r=t,t=null,a=null,o=0;r;){for(o++,n=r,s=0,e=0;e0||l>0&&n;)0!==s&&(0===l||!n||r.z<=n.z)?(i=r,r=r.nextZ,s--):(i=n,n=n.nextZ,l--),a?a.nextZ=i:t=i,i.prevZ=a,a=i;r=n}a.nextZ=null,c*=2}while(o>1)}(i)}(t,n,i,f);for(var d,g,m=t;t.prev!==t.next;)if(d=t.prev,g=t.next,f?l(t,n,i,f):s(t))e.push(d.i/r),e.push(t.i/r),e.push(g.i/r),k(t),t=g.next,m=g.next;else if((t=g)===m){h?1===h?o(t=c(t,e,r),e,r,n,i,f,2):2===h&&u(t,e,r,n,i,f):o(a(t),e,r,n,i,f,1);break}}}function s(t){var e=t.prev,r=t,n=t.next;if(v(e,r,n)>=0)return!1;for(var i=t.next.next;i!==t.prev;){if(g(e.x,e.y,r.x,r.y,n.x,n.y,i.x,i.y)&&v(i.prev,i,i.next)>=0)return!1;i=i.next}return!0}function l(t,e,r,n){var i=t.prev,a=t,o=t.next;if(v(i,a,o)>=0)return!1;for(var s=i.xa.x?i.x>o.x?i.x:o.x:a.x>o.x?a.x:o.x,u=i.y>a.y?i.y>o.y?i.y:o.y:a.y>o.y?a.y:o.y,f=p(s,l,e,r,n),h=p(c,u,e,r,n),d=t.prevZ,m=t.nextZ;d&&d.z>=f&&m&&m.z<=h;){if(d!==t.prev&&d!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,d.x,d.y)&&v(d.prev,d,d.next)>=0)return!1;if(d=d.prevZ,m!==t.prev&&m!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,m.x,m.y)&&v(m.prev,m,m.next)>=0)return!1;m=m.nextZ}for(;d&&d.z>=f;){if(d!==t.prev&&d!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,d.x,d.y)&&v(d.prev,d,d.next)>=0)return!1;d=d.prevZ}for(;m&&m.z<=h;){if(m!==t.prev&&m!==t.next&&g(i.x,i.y,a.x,a.y,o.x,o.y,m.x,m.y)&&v(m.prev,m,m.next)>=0)return!1;m=m.nextZ}return!0}function c(t,e,r){var n=t;do{var i=n.prev,a=n.next.next;!y(i,a)&&x(i,n,n.next,a)&&b(i,a)&&b(a,i)&&(e.push(i.i/r),e.push(n.i/r),e.push(a.i/r),k(n),k(n.next),n=t=a),n=n.next}while(n!==t);return n}function u(t,e,r,n,i,s){var l=t;do{for(var c=l.next.next;c!==l.prev;){if(l.i!==c.i&&m(l,c)){var u=_(l,c);return l=a(l,l.next),u=a(u,u.next),o(l,e,r,n,i,s),void o(u,e,r,n,i,s)}c=c.next}l=l.next}while(l!==t)}function f(t,e){return t.x-e.x}function h(t,e){if(e=function(t,e){var r,n=e,i=t.x,a=t.y,o=-1/0;do{if(a<=n.y&&a>=n.next.y&&n.next.y!==n.y){var s=n.x+(a-n.y)*(n.next.x-n.x)/(n.next.y-n.y);if(s<=i&&s>o){if(o=s,s===i){if(a===n.y)return n;if(a===n.next.y)return n.next}r=n.x=n.x&&n.x>=u&&i!==n.x&&g(ar.x)&&b(n,t)&&(r=n,h=l),n=n.next;return r}(t,e)){var r=_(e,t);a(r,r.next)}}function p(t,e,r,n,i){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-r)*i)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-n)*i)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function d(t){var e=t,r=t;do{e.x=0&&(t-o)*(n-s)-(r-o)*(e-s)>=0&&(r-o)*(a-s)-(i-o)*(n-s)>=0}function m(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){var r=t;do{if(r.i!==t.i&&r.next.i!==t.i&&r.i!==e.i&&r.next.i!==e.i&&x(r,r.next,t,e))return!0;r=r.next}while(r!==t);return!1}(t,e)&&b(t,e)&&b(e,t)&&function(t,e){var r=t,n=!1,i=(t.x+e.x)/2,a=(t.y+e.y)/2;do{r.y>a!=r.next.y>a&&r.next.y!==r.y&&i<(r.next.x-r.x)*(a-r.y)/(r.next.y-r.y)+r.x&&(n=!n),r=r.next}while(r!==t);return n}(t,e)}function v(t,e,r){return(e.y-t.y)*(r.x-e.x)-(e.x-t.x)*(r.y-e.y)}function y(t,e){return t.x===e.x&&t.y===e.y}function x(t,e,r,n){return!!(y(t,e)&&y(r,n)||y(t,n)&&y(r,e))||v(t,e,r)>0!=v(t,e,n)>0&&v(r,n,t)>0!=v(r,n,e)>0}function b(t,e){return v(t.prev,t,t.next)<0?v(t,e,t.next)>=0&&v(t,t.prev,e)>=0:v(t,e,t.prev)<0||v(t,t.next,e)<0}function _(t,e){var r=new M(t.i,t.x,t.y),n=new M(e.i,e.x,e.y),i=t.next,a=e.prev;return t.next=e,e.prev=t,r.next=i,i.prev=r,n.next=r,r.prev=n,a.next=n,n.prev=a,n}function w(t,e,r,n){var i=new M(t,e,r);return n?(i.next=n.next,i.prev=n,n.next.prev=i,n.next=i):(i.prev=i,i.next=i),i}function k(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function M(t,e,r){this.i=t,this.x=e,this.y=r,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function A(t,e,r,n){for(var i=0,a=e,o=r-n;a0&&(n+=t[i-1].length,r.holes.push(n))}return r}},{}],15:[function(t,e,r){function n(t,e){return function(r){return t(r,e)}}function i(t,e){e=!!e,t[0]=a(t[0],e);for(var r=1;r=0}(t)===e?t:t.reverse()}var o=t("@mapbox/geojson-area");e.exports=function t(e,r){switch(e&&e.type||null){case"FeatureCollection":return e.features=e.features.map(n(t,r)),e;case"Feature":return e.geometry=t(e.geometry,r),e;case"Polygon":case"MultiPolygon":return function(t,e){return"Polygon"===t.type?t.coordinates=i(t.coordinates,e):"MultiPolygon"===t.type&&(t.coordinates=t.coordinates.map(n(i,e))),t}(e,r);default:return e}}},{"@mapbox/geojson-area":1}],16:[function(t,e,r){function n(t,e,r,n,i){for(var a=0;a=r&&o<=n&&(e.push(t[a]),e.push(t[a+1]),e.push(t[a+2]))}}function i(t,e,r,n,i,a){for(var c=[],u=0===i?s:l,f=0;f=r&&u(c,h,p,g,m,r):v>n?y<=n&&u(c,h,p,g,m,n):o(c,h,p,d),y=r&&(u(c,h,p,g,m,r),x=!0),y>n&&v<=n&&(u(c,h,p,g,m,n),x=!0),!a&&x&&(c.size=t.size,e.push(c),c=[])}var b=t.length-3;h=t[b],p=t[b+1],d=t[b+2],(v=0===i?h:p)>=r&&v<=n&&o(c,h,p,d),b=c.length-3,a&&b>=3&&(c[b]!==c[0]||c[b+1]!==c[1])&&o(c,c[0],c[1],c[2]),c.length&&(c.size=t.size,e.push(c))}function a(t,e,r,n,a,o){for(var s=0;s=(r/=e)&&u<=o)return t;if(l>o||u=r&&v<=o)f.push(p);else if(!(m>o||v0&&(o+=n?(i*h-f*a)/2:Math.sqrt(Math.pow(f-i,2)+Math.pow(h-a,2))),i=f,a=h}var p=e.length-3;e[2]=1,c(e,0,p,r),e[p+2]=1,e.size=Math.abs(o)}function o(t,e,r,n){for(var i=0;i1?1:r}e.exports=function(t,e){var r=[];if("FeatureCollection"===t.type)for(var i=0;i24)throw new Error("maxZoom should be in the 0-24 range");var n=1<1&&console.time("creation"),g=this.tiles[d]=c(t,p,r,n,m,e===f.maxZoom),this.tileCoords.push({z:e,x:r,y:n}),h)){h>1&&(console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",e,r,n,g.numFeatures,g.numPoints,g.numSimplified),console.timeEnd("creation"));var v="z"+e;this.stats[v]=(this.stats[v]||0)+1,this.total++}if(g.source=t,a){if(e===f.maxZoom||e===a)continue;var y=1<1&&console.time("clipping");var x,b,_,w,k,M,A=.5*f.buffer/f.extent,T=.5-A,S=.5+A,C=1+A;x=b=_=w=null,k=s(t,p,r-A,r+S,0,g.minX,g.maxX),M=s(t,p,r+T,r+C,0,g.minX,g.maxX),t=null,k&&(x=s(k,p,n-A,n+S,1,g.minY,g.maxY),b=s(k,p,n+T,n+C,1,g.minY,g.maxY),k=null),M&&(_=s(M,p,n-A,n+S,1,g.minY,g.maxY),w=s(M,p,n+T,n+C,1,g.minY,g.maxY),M=null),h>1&&console.timeEnd("clipping"),u.push(x||[],e+1,2*r,2*n),u.push(b||[],e+1,2*r,2*n+1),u.push(_||[],e+1,2*r+1,2*n),u.push(w||[],e+1,2*r+1,2*n+1)}}},n.prototype.getTile=function(t,e,r){var n=this.options,a=n.extent,s=n.debug;if(t<0||t>24)return null;var l=1<1&&console.log("drilling down to z%d-%d-%d",t,e,r);for(var u,f=t,h=e,p=r;!u&&f>0;)f--,h=Math.floor(h/2),p=Math.floor(p/2),u=this.tiles[i(f,h,p)];return u&&u.source?(s>1&&console.log("found parent tile z%d-%d-%d",f,h,p),s>1&&console.time("drilling down"),this.splitTile(u.source,f,h,p,t,e,r),s>1&&console.timeEnd("drilling down"),this.tiles[c]?o.tile(this.tiles[c],a):null):null}},{"./clip":16,"./convert":17,"./tile":21,"./transform":22,"./wrap":23}],20:[function(t,e,r){function n(t,e,r,n,i,a){var o=i-r,s=a-n;if(0!==o||0!==s){var l=((t-r)*o+(e-n)*s)/(o*o+s*s);l>1?(r=i,n=a):l>0&&(r+=o*l,n+=s*l)}return(o=t-r)*o+(s=e-n)*s}e.exports=function t(e,r,i,a){for(var o,s=a,l=e[r],c=e[r+1],u=e[i],f=e[i+1],h=r+3;hs&&(o=h,s=p)}s>a&&(o-r>3&&t(e,r,o,a),e[o+2]=s,i-o>3&&t(e,o,i,a))}},{}],21:[function(t,e,r){function n(t,e,r,n){var a=e.geometry,o=e.type,s=[];if("Point"===o||"MultiPoint"===o)for(var l=0;ls)&&(r.numSimplified++,l.push(e[c]),l.push(e[c+1])),r.numPoints++;a&&function(t,e){for(var r=0,n=0,i=t.length,a=i-2;n0===e)for(n=0,i=t.length;ns.maxX&&(s.maxX=f),h>s.maxY&&(s.maxY=h)}return s}},{}],22:[function(t,e,r){function n(t,e,r,n,i,a){return[Math.round(r*(t*n-i)),Math.round(r*(e*n-a))]}r.tile=function(t,e){if(t.transformed)return t;var r,i,a,o=t.z2,s=t.x,l=t.y;for(r=0;r=c[h+0]&&n>=c[h+1]?(o[f]=!0,a.push(l[f])):o[f]=!1}}},n.prototype._forEachCell=function(t,e,r,n,i,a,o){for(var s=this._convertToCellCoord(t),l=this._convertToCellCoord(e),c=this._convertToCellCoord(r),u=this._convertToCellCoord(n),f=s;f<=c;f++)for(var h=l;h<=u;h++){var p=this.d*h+f;if(i.call(this,t,e,r,n,p,a,o))return}},n.prototype._convertToCellCoord=function(t){return Math.max(0,Math.min(this.d-1,Math.floor(t*this.scale)+this.padding))},n.prototype.toArrayBuffer=function(){if(this.arrayBuffer)return this.arrayBuffer;for(var t=this.cells,e=i+this.cells.length+1+1,r=0,n=0;n>1,u=-7,f=r?i-1:0,h=r?-1:1,p=t[e+f];for(f+=h,a=p&(1<<-u)-1,p>>=-u,u+=s;u>0;a=256*a+t[e+f],f+=h,u-=8);for(o=a&(1<<-u)-1,a>>=-u,u+=n;u>0;o=256*o+t[e+f],f+=h,u-=8);if(0===a)a=1-c;else{if(a===l)return o?NaN:1/0*(p?-1:1);o+=Math.pow(2,n),a-=c}return(p?-1:1)*o*Math.pow(2,a-n)},r.write=function(t,e,r,n,i,a){var o,s,l,c=8*a-i-1,u=(1<>1,h=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,p=n?0:a-1,d=n?1:-1,g=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(s=isNaN(e)?1:0,o=u):(o=Math.floor(Math.log(e)/Math.LN2),e*(l=Math.pow(2,-o))<1&&(o--,l*=2),(e+=o+f>=1?h/l:h*Math.pow(2,1-f))*l>=2&&(o++,l/=2),o+f>=u?(s=0,o=u):o+f>=1?(s=(e*l-1)*Math.pow(2,i),o+=f):(s=e*Math.pow(2,f-1)*Math.pow(2,i),o=0));i>=8;t[r+p]=255&s,p+=d,s/=256,i-=8);for(o=o<0;t[r+p]=255&o,p+=d,o/=256,c-=8);t[r+p-d]|=128*g}},{}],26:[function(t,e,r){function n(t,e,r,n,s){e=e||i,r=r||a,s=s||Array,this.nodeSize=n||64,this.points=t,this.ids=new s(t.length),this.coords=new s(2*t.length);for(var l=0;l=r&&s<=i&&l>=n&&l<=a&&u.push(t[d]);else{var g=Math.floor((p+h)/2);s=e[2*g],l=e[2*g+1],s>=r&&s<=i&&l>=n&&l<=a&&u.push(t[g]);var m=(f+1)%2;(0===f?r<=s:n<=l)&&(c.push(p),c.push(g-1),c.push(m)),(0===f?i>=s:a>=l)&&(c.push(g+1),c.push(h),c.push(m))}}return u}},{}],28:[function(t,e,r){function n(t,e,r,n){i(t,r,n),i(e,2*r,2*n),i(e,2*r+1,2*n+1)}function i(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}e.exports=function t(e,r,i,a,o,s){if(!(o-a<=i)){var l=Math.floor((a+o)/2);(function t(e,r,i,a,o,s){for(;o>a;){if(o-a>600){var l=o-a+1,c=i-a+1,u=Math.log(l),f=.5*Math.exp(2*u/3),h=.5*Math.sqrt(u*f*(l-f)/l)*(c-l/2<0?-1:1);t(e,r,i,Math.max(a,Math.floor(i-c*f/l+h)),Math.min(o,Math.floor(i+(l-c)*f/l+h)),s)}var p=r[2*i+s],d=a,g=o;for(n(e,r,a,i),r[2*o+s]>p&&n(e,r,a,o);dp;)g--}r[2*a+s]===p?n(e,r,a,g):n(e,r,++g,o),g<=i&&(a=g+1),i<=g&&(o=g-1)}})(e,r,l,a,o,s%2),t(e,r,i,a,l-1,s+1),t(e,r,i,l+1,o,s+1)}}},{}],29:[function(t,e,r){function n(t,e,r,n){var i=t-r,a=e-n;return i*i+a*a}e.exports=function(t,e,r,i,a,o){for(var s=[0,t.length-1,0],l=[],c=a*a;s.length;){var u=s.pop(),f=s.pop(),h=s.pop();if(f-h<=o)for(var p=h;p<=f;p++)n(e[2*p],e[2*p+1],r,i)<=c&&l.push(t[p]);else{var d=Math.floor((h+f)/2),g=e[2*d],m=e[2*d+1];n(g,m,r,i)<=c&&l.push(t[d]);var v=(u+1)%2;(0===u?r-a<=g:i-a<=m)&&(s.push(h),s.push(d-1),s.push(v)),(0===u?r+a>=g:i+a>=m)&&(s.push(d+1),s.push(f),s.push(v))}}return l}},{}],30:[function(t,e,r){function n(t){this.buf=ArrayBuffer.isView&&ArrayBuffer.isView(t)?t:new Uint8Array(t||0),this.pos=0,this.type=0,this.length=this.buf.length}function i(t){return t.type===n.Bytes?t.readVarint()+t.pos:t.pos+1}function a(t,e,r){return r?4294967296*e+(t>>>0):4294967296*(e>>>0)+(t>>>0)}function o(t,e,r){var n=e<=16383?1:e<=2097151?2:e<=268435455?3:Math.ceil(Math.log(e)/(7*Math.LN2));r.realloc(n);for(var i=r.pos-1;i>=t;i--)r.buf[i+n]=r.buf[i]}function s(t,e){for(var r=0;r>>8,t[r+2]=e>>>16,t[r+3]=e>>>24}function y(t,e){return(t[e]|t[e+1]<<8|t[e+2]<<16)+(t[e+3]<<24)}e.exports=n;var x=t("ieee754");n.Varint=0,n.Fixed64=1,n.Bytes=2,n.Fixed32=5;n.prototype={destroy:function(){this.buf=null},readFields:function(t,e,r){for(r=r||this.length;this.pos>3,a=this.pos;this.type=7&n,t(i,e,this),this.pos===a&&this.skip(n)}return e},readMessage:function(t,e){return this.readFields(t,e,this.readVarint()+this.pos)},readFixed32:function(){var t=m(this.buf,this.pos);return this.pos+=4,t},readSFixed32:function(){var t=y(this.buf,this.pos);return this.pos+=4,t},readFixed64:function(){var t=m(this.buf,this.pos)+4294967296*m(this.buf,this.pos+4);return this.pos+=8,t},readSFixed64:function(){var t=m(this.buf,this.pos)+4294967296*y(this.buf,this.pos+4);return this.pos+=8,t},readFloat:function(){var t=x.read(this.buf,this.pos,!0,23,4);return this.pos+=4,t},readDouble:function(){var t=x.read(this.buf,this.pos,!0,52,8);return this.pos+=8,t},readVarint:function(t){var e,r,n=this.buf;return e=127&(r=n[this.pos++]),r<128?e:(e|=(127&(r=n[this.pos++]))<<7,r<128?e:(e|=(127&(r=n[this.pos++]))<<14,r<128?e:(e|=(127&(r=n[this.pos++]))<<21,r<128?e:function(t,e,r){var n,i,o=r.buf;if(n=(112&(i=o[r.pos++]))>>4,i<128)return a(t,n,e);if(n|=(127&(i=o[r.pos++]))<<3,i<128)return a(t,n,e);if(n|=(127&(i=o[r.pos++]))<<10,i<128)return a(t,n,e);if(n|=(127&(i=o[r.pos++]))<<17,i<128)return a(t,n,e);if(n|=(127&(i=o[r.pos++]))<<24,i<128)return a(t,n,e);if(n|=(1&(i=o[r.pos++]))<<31,i<128)return a(t,n,e);throw new Error("Expected varint not more than 10 bytes")}(e|=(15&(r=n[this.pos]))<<28,t,this))))},readVarint64:function(){return this.readVarint(!0)},readSVarint:function(){var t=this.readVarint();return t%2==1?(t+1)/-2:t/2},readBoolean:function(){return Boolean(this.readVarint())},readString:function(){var t=this.readVarint()+this.pos,e=function(t,e,r){for(var n="",i=e;i239?4:l>223?3:l>191?2:1;if(i+u>r)break;1===u?l<128&&(c=l):2===u?128==(192&(a=t[i+1]))&&(c=(31&l)<<6|63&a)<=127&&(c=null):3===u?(a=t[i+1],o=t[i+2],128==(192&a)&&128==(192&o)&&((c=(15&l)<<12|(63&a)<<6|63&o)<=2047||c>=55296&&c<=57343)&&(c=null)):4===u&&(a=t[i+1],o=t[i+2],s=t[i+3],128==(192&a)&&128==(192&o)&&128==(192&s)&&((c=(15&l)<<18|(63&a)<<12|(63&o)<<6|63&s)<=65535||c>=1114112)&&(c=null)),null===c?(c=65533,u=1):c>65535&&(c-=65536,n+=String.fromCharCode(c>>>10&1023|55296),c=56320|1023&c),n+=String.fromCharCode(c),i+=u}return n}(this.buf,this.pos,t);return this.pos=t,e},readBytes:function(){var t=this.readVarint()+this.pos,e=this.buf.subarray(this.pos,t);return this.pos=t,e},readPackedVarint:function(t,e){var r=i(this);for(t=t||[];this.pos127;);else if(e===n.Bytes)this.pos=this.readVarint()+this.pos;else if(e===n.Fixed32)this.pos+=4;else{if(e!==n.Fixed64)throw new Error("Unimplemented type: "+e);this.pos+=8}},writeTag:function(t,e){this.writeVarint(t<<3|e)},realloc:function(t){for(var e=this.length||16;e268435455||t<0?function(t,e){var r,n;if(t>=0?(r=t%4294967296|0,n=t/4294967296|0):(n=~(-t/4294967296),4294967295^(r=~(-t%4294967296))?r=r+1|0:(r=0,n=n+1|0)),t>=0x10000000000000000||t<-0x10000000000000000)throw new Error("Given varint doesn't fit into 10 bytes");e.realloc(10),function(t,e,r){r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos++]=127&t|128,t>>>=7,r.buf[r.pos]=127&t}(r,0,e),function(t,e){var r=(7&t)<<4;e.buf[e.pos++]|=r|((t>>>=3)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t|((t>>>=7)?128:0),t&&(e.buf[e.pos++]=127&t)))))}(n,e)}(t,this):(this.realloc(4),this.buf[this.pos++]=127&t|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=t>>>7&127))))},writeSVarint:function(t){this.writeVarint(t<0?2*-t-1:2*t)},writeBoolean:function(t){this.writeVarint(Boolean(t))},writeString:function(t){t=String(t),this.realloc(4*t.length),this.pos++;var e=this.pos;this.pos=function(t,e,r){for(var n,i,a=0;a55295&&n<57344){if(!i){n>56319||a+1===e.length?(t[r++]=239,t[r++]=191,t[r++]=189):i=n;continue}if(n<56320){t[r++]=239,t[r++]=191,t[r++]=189,i=n;continue}n=i-55296<<10|n-56320|65536,i=null}else i&&(t[r++]=239,t[r++]=191,t[r++]=189,i=null);n<128?t[r++]=n:(n<2048?t[r++]=n>>6|192:(n<65536?t[r++]=n>>12|224:(t[r++]=n>>18|240,t[r++]=n>>12&63|128),t[r++]=n>>6&63|128),t[r++]=63&n|128)}return r}(this.buf,t,this.pos);var r=this.pos-e;r>=128&&o(e,r,this),this.pos=e-1,this.writeVarint(r),this.pos+=r},writeFloat:function(t){this.realloc(4),x.write(this.buf,t,this.pos,!0,23,4),this.pos+=4},writeDouble:function(t){this.realloc(8),x.write(this.buf,t,this.pos,!0,52,8),this.pos+=8},writeBytes:function(t){var e=t.length;this.writeVarint(e),this.realloc(e);for(var r=0;r=128&&o(r,n,this),this.pos=r-1,this.writeVarint(n),this.pos+=n},writeMessage:function(t,e,r){this.writeTag(t,n.Bytes),this.writeRawMessage(e,r)},writePackedVarint:function(t,e){this.writeMessage(t,s,e)},writePackedSVarint:function(t,e){this.writeMessage(t,l,e)},writePackedBoolean:function(t,e){this.writeMessage(t,f,e)},writePackedFloat:function(t,e){this.writeMessage(t,c,e)},writePackedDouble:function(t,e){this.writeMessage(t,u,e)},writePackedFixed32:function(t,e){this.writeMessage(t,h,e)},writePackedSFixed32:function(t,e){this.writeMessage(t,p,e)},writePackedFixed64:function(t,e){this.writeMessage(t,d,e)},writePackedSFixed64:function(t,e){this.writeMessage(t,g,e)},writeBytesField:function(t,e){this.writeTag(t,n.Bytes),this.writeBytes(e)},writeFixed32Field:function(t,e){this.writeTag(t,n.Fixed32),this.writeFixed32(e)},writeSFixed32Field:function(t,e){this.writeTag(t,n.Fixed32),this.writeSFixed32(e)},writeFixed64Field:function(t,e){this.writeTag(t,n.Fixed64),this.writeFixed64(e)},writeSFixed64Field:function(t,e){this.writeTag(t,n.Fixed64),this.writeSFixed64(e)},writeVarintField:function(t,e){this.writeTag(t,n.Varint),this.writeVarint(e)},writeSVarintField:function(t,e){this.writeTag(t,n.Varint),this.writeSVarint(e)},writeStringField:function(t,e){this.writeTag(t,n.Bytes),this.writeString(e)},writeFloatField:function(t,e){this.writeTag(t,n.Fixed32),this.writeFloat(e)},writeDoubleField:function(t,e){this.writeTag(t,n.Fixed64),this.writeDouble(e)},writeBooleanField:function(t,e){this.writeVarintField(t,Boolean(e))}}},{ieee754:25}],31:[function(t,e,r){function n(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}function i(t,e){return te?1:0}e.exports=function t(e,r,a,o,s){for(a=a||0,o=o||e.length-1,s=s||i;o>a;){if(o-a>600){var l=o-a+1,c=r-a+1,u=Math.log(l),f=.5*Math.exp(2*u/3),h=.5*Math.sqrt(u*f*(l-f)/l)*(c-l/2<0?-1:1);t(e,r,Math.max(a,Math.floor(r-c*f/l+h)),Math.min(o,Math.floor(r+(l-c)*f/l+h)),s)}var p=e[r],d=a,g=o;for(n(e,a,r),s(e[o],p)>0&&n(e,a,o);d0;)g--}0===s(e[a],p)?n(e,a,g):n(e,++g,o),g<=r&&(a=g+1),r<=g&&(o=g-1)}}},{}],32:[function(t,e,r){function n(t){this.options=u(Object.create(this.options),t),this.trees=new Array(this.options.maxZoom+1)}function i(t,e,r,n,i){return{x:t,y:e,zoom:1/0,id:n,properties:i,parentId:-1,numPoints:r}}function a(t,e){var r=t.geometry.coordinates;return{x:l(r[0]),y:c(r[1]),zoom:1/0,id:e,parentId:-1}}function o(t){return{type:"Feature",properties:s(t),geometry:{type:"Point",coordinates:[function(t){return 360*(t-.5)}(t.x),function(t){var e=(180-360*t)*Math.PI/180;return 360*Math.atan(Math.exp(e))/Math.PI-90}(t.y)]}}}function s(t){var e=t.numPoints,r=e>=1e4?Math.round(e/1e3)+"k":e>=1e3?Math.round(e/100)/10+"k":e;return u(u({},t.properties),{cluster:!0,cluster_id:t.id,point_count:e,point_count_abbreviated:r})}function l(t){return t/360+.5}function c(t){var e=Math.sin(t*Math.PI/180),r=.5-.25*Math.log((1+e)/(1-e))/Math.PI;return r<0?0:r>1?1:r}function u(t,e){for(var r in e)t[r]=e[r];return t}function f(t){return t.x}function h(t){return t.y}var p=t("kdbush");e.exports=function(t){return new n(t)},n.prototype={options:{minZoom:0,maxZoom:16,radius:40,extent:512,nodeSize:64,log:!1,reduce:null,initial:function(){return{}},map:function(t){return t}},load:function(t){var e=this.options.log;e&&console.time("total time");var r="prepare "+t.length+" points";e&&console.time(r),this.points=t;var n=t.map(a);e&&console.timeEnd(r);for(var i=this.options.maxZoom;i>=this.options.minZoom;i--){var o=+Date.now();this.trees[i+1]=p(n,f,h,this.options.nodeSize,Float32Array),n=this._cluster(n,i),e&&console.log("z%d: %d clusters in %dms",i,n.length,+Date.now()-o)}return this.trees[this.options.minZoom]=p(n,f,h,this.options.nodeSize,Float32Array),e&&console.timeEnd("total time"),this},getClusters:function(t,e){for(var r=this.trees[this._limitZoom(e)],n=r.range(l(t[0]),c(t[3]),l(t[2]),c(t[1])),i=[],a=0;a0)for(var r=this.length>>1;r>=0;r--)this._down(r)}function i(t,e){return te?1:0}e.exports=n,n.prototype={push:function(t){this.data.push(t),this.length++,this._up(this.length-1)},pop:function(){if(0!==this.length){var t=this.data[0];return this.length--,this.length>0&&(this.data[0]=this.data[this.length],this._down(0)),this.data.pop(),t}},peek:function(){return this.data[0]},_up:function(t){for(var e=this.data,r=this.compare,n=e[t];t>0;){var i=t-1>>1,a=e[i];if(r(n,a)>=0)break;e[t]=a,t=i}e[t]=n},_down:function(t){for(var e=this.data,r=this.compare,n=this.length,i=n>>1,a=e[t];t=0)break;e[t]=l,t=o}e[t]=a}}},{}],34:[function(t,e,r){function n(t){var e=new f;return function(t,e){for(var r in t.layers)e.writeMessage(3,i,t.layers[r])}(t,e),e.finish()}function i(t,e){e.writeVarintField(15,t.version||1),e.writeStringField(1,t.name||""),e.writeVarintField(5,t.extent||4096);var r,n={keys:[],values:[],keycache:{},valuecache:{}};for(r=0;r>31}function c(t,e){for(var r=t.loadGeometry(),n=t.type,i=0,a=0,o=r.length,c=0;c=u||f<0||f>=u)){var h=r.segments.prepareSegment(4,r.layoutVertexArray,r.indexArray),p=h.vertexLength;n(r.layoutVertexArray,c,f,-1,-1),n(r.layoutVertexArray,c,f,1,-1),n(r.layoutVertexArray,c,f,1,1),n(r.layoutVertexArray,c,f,-1,1),r.indexArray.emplaceBack(p,p+1,p+2),r.indexArray.emplaceBack(p,p+3,p+2),h.vertexLength+=4,h.primitiveLength+=2}}this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,t)},f("CircleBucket",h,{omit:["layers"]}),e.exports=h},{"../../util/web_worker_transfer":278,"../array_types":39,"../extent":53,"../index_array_type":55,"../load_geometry":56,"../program_configuration":58,"../segment":60,"./circle_attributes":41}],43:[function(t,e,r){arguments[4][41][0].apply(r,arguments)},{"../../util/struct_array":271,dup:41}],44:[function(t,e,r){var n=t("../array_types").FillLayoutArray,i=t("./fill_attributes").members,a=t("../segment").SegmentVector,o=t("../program_configuration").ProgramConfigurationSet,s=t("../index_array_type"),l=s.LineIndexArray,c=s.TriangleIndexArray,u=t("../load_geometry"),f=t("earcut"),h=t("../../util/classify_rings"),p=t("../../util/web_worker_transfer").register,d=function(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map(function(t){return t.id}),this.index=t.index,this.layoutVertexArray=new n,this.indexArray=new c,this.indexArray2=new l,this.programConfigurations=new o(i,t.layers,t.zoom),this.segments=new a,this.segments2=new a};d.prototype.populate=function(t,e){for(var r=this,n=0,i=t;nd)||t.y===e.y&&(t.y<0||t.y>d)}function a(t){return t.every(function(t){return t.x<0})||t.every(function(t){return t.x>d})||t.every(function(t){return t.y<0})||t.every(function(t){return t.y>d})}var o=t("../array_types").FillExtrusionLayoutArray,s=t("./fill_extrusion_attributes").members,l=t("../segment"),c=l.SegmentVector,u=l.MAX_VERTEX_ARRAY_LENGTH,f=t("../program_configuration").ProgramConfigurationSet,h=t("../index_array_type").TriangleIndexArray,p=t("../load_geometry"),d=t("../extent"),g=t("earcut"),m=t("../../util/classify_rings"),v=t("../../util/web_worker_transfer").register,y=Math.pow(2,13),x=function(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map(function(t){return t.id}),this.index=t.index,this.layoutVertexArray=new o,this.indexArray=new h,this.programConfigurations=new f(s,t.layers,t.zoom),this.segments=new c};x.prototype.populate=function(t,e){for(var r=this,n=0,i=t;n=1){var w=y[b-1];if(!i(_,w)){p.vertexLength+4>u&&(p=r.segments.prepareSegment(4,r.layoutVertexArray,r.indexArray));var k=_.sub(w)._perp()._unit(),M=w.dist(_);x+M>32768&&(x=0),n(r.layoutVertexArray,_.x,_.y,k.x,k.y,0,0,x),n(r.layoutVertexArray,_.x,_.y,k.x,k.y,0,1,x),x+=M,n(r.layoutVertexArray,w.x,w.y,k.x,k.y,0,0,x),n(r.layoutVertexArray,w.x,w.y,k.x,k.y,0,1,x);var A=p.vertexLength;r.indexArray.emplaceBack(A,A+1,A+2),r.indexArray.emplaceBack(A+1,A+2,A+3),p.vertexLength+=4,p.primitiveLength+=2}}}}p.vertexLength+c>u&&(p=r.segments.prepareSegment(c,r.layoutVertexArray,r.indexArray));for(var T=[],S=[],C=p.vertexLength,E=0,L=l;E>6)}var i=t("../array_types").LineLayoutArray,a=t("./line_attributes").members,o=t("../segment").SegmentVector,s=t("../program_configuration").ProgramConfigurationSet,l=t("../index_array_type").TriangleIndexArray,c=t("../load_geometry"),u=t("../extent"),f=t("@mapbox/vector-tile").VectorTileFeature.types,h=t("../../util/web_worker_transfer").register,p=63,d=Math.cos(Math.PI/180*37.5),g=.5,m=Math.pow(2,14)/g,v=function(t){this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map(function(t){return t.id}),this.index=t.index,this.layoutVertexArray=new i,this.indexArray=new l,this.programConfigurations=new s(a,t.layers,t.zoom),this.segments=new o};v.prototype.populate=function(t,e){for(var r=this,n=0,i=t;n=2&&t[l-1].equals(t[l-2]);)l--;for(var c=0;cc){var z=m.dist(w);if(z>2*h){var P=m.sub(m.sub(w)._mult(h/z)._round());o.distance+=P.dist(w),o.addCurrentVertex(P,o.distance,M.mult(1),0,0,!1,g),w=P}}var D=w&&k,O=D?r:k?x:b;if(D&&"round"===O&&(Ei&&(O="bevel"),"bevel"===O&&(E>2&&(O="flipbevel"),E100)S=A.clone().mult(-1);else{var I=M.x*A.y-M.y*A.x>0?-1:1,R=E*M.add(A).mag()/M.sub(A).mag();S._perp()._mult(R*I)}o.addCurrentVertex(m,o.distance,S,0,0,!1,g),o.addCurrentVertex(m,o.distance,S.mult(-1),0,0,!1,g)}else if("bevel"===O||"fakeround"===O){var B=M.x*A.y-M.y*A.x>0,F=-Math.sqrt(E*E-1);if(B?(y=0,v=F):(v=0,y=F),_||o.addCurrentVertex(m,o.distance,M,v,y,!1,g),"fakeround"===O){for(var N=Math.floor(8*(.5-(C-.5))),j=void 0,V=0;V=0;U--)j=M.mult((U+1)/(N+1))._add(A)._unit(),o.addPieSliceVertex(m,o.distance,j,B,g)}k&&o.addCurrentVertex(m,o.distance,A,-v,-y,!1,g)}else"butt"===O?(_||o.addCurrentVertex(m,o.distance,M,0,0,!1,g),k&&o.addCurrentVertex(m,o.distance,A,0,0,!1,g)):"square"===O?(_||(o.addCurrentVertex(m,o.distance,M,1,1,!1,g),o.e1=o.e2=-1),k&&o.addCurrentVertex(m,o.distance,A,-1,-1,!1,g)):"round"===O&&(_||(o.addCurrentVertex(m,o.distance,M,0,0,!1,g),o.addCurrentVertex(m,o.distance,M,1,1,!0,g),o.e1=o.e2=-1),k&&(o.addCurrentVertex(m,o.distance,A,-1,-1,!0,g),o.addCurrentVertex(m,o.distance,A,0,0,!1,g)));if(L&&T2*h){var H=m.add(k.sub(m)._mult(h/q)._round());o.distance+=H.dist(m),o.addCurrentVertex(H,o.distance,A.mult(1),0,0,!1,g),m=H}}_=!1}this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length,e)}},v.prototype.addCurrentVertex=function(t,e,r,i,a,o,s){var l,c=this.layoutVertexArray,u=this.indexArray;l=r.clone(),i&&l._sub(r.perp()._mult(i)),n(c,t,l,o,!1,i,e),this.e3=s.vertexLength++,this.e1>=0&&this.e2>=0&&(u.emplaceBack(this.e1,this.e2,this.e3),s.primitiveLength++),this.e1=this.e2,this.e2=this.e3,l=r.mult(-1),a&&l._sub(r.perp()._mult(a)),n(c,t,l,o,!0,-a,e),this.e3=s.vertexLength++,this.e1>=0&&this.e2>=0&&(u.emplaceBack(this.e1,this.e2,this.e3),s.primitiveLength++),this.e1=this.e2,this.e2=this.e3,e>m/2&&(this.distance=0,this.addCurrentVertex(t,this.distance,r,i,a,o,s))},v.prototype.addPieSliceVertex=function(t,e,r,i,a){r=r.mult(i?-1:1);var o=this.layoutVertexArray,s=this.indexArray;n(o,t,r,!1,i,0,e),this.e3=a.vertexLength++,this.e1>=0&&this.e2>=0&&(s.emplaceBack(this.e1,this.e2,this.e3),a.primitiveLength++),i?this.e2=this.e3:this.e1=this.e3},h("LineBucket",v,{omit:["layers"]}),e.exports=v},{"../../util/web_worker_transfer":278,"../array_types":39,"../extent":53,"../index_array_type":55,"../load_geometry":56,"../program_configuration":58,"../segment":60,"./line_attributes":48,"@mapbox/vector-tile":8}],50:[function(t,e,r){var n=t("../../util/struct_array").createLayout,i={symbolLayoutAttributes:n([{name:"a_pos_offset",components:4,type:"Int16"},{name:"a_data",components:4,type:"Uint16"}]),dynamicLayoutAttributes:n([{name:"a_projected_pos",components:3,type:"Float32"}],4),placementOpacityAttributes:n([{name:"a_fade_opacity",components:1,type:"Uint32"}],4),collisionVertexAttributes:n([{name:"a_placed",components:2,type:"Uint8"}],4),collisionBox:n([{type:"Int16",name:"anchorPointX"},{type:"Int16",name:"anchorPointY"},{type:"Int16",name:"x1"},{type:"Int16",name:"y1"},{type:"Int16",name:"x2"},{type:"Int16",name:"y2"},{type:"Uint32",name:"featureIndex"},{type:"Uint16",name:"sourceLayerIndex"},{type:"Uint16",name:"bucketIndex"},{type:"Int16",name:"radius"},{type:"Int16",name:"signedDistanceFromAnchor"}]),collisionBoxLayout:n([{name:"a_pos",components:2,type:"Int16"},{name:"a_anchor_pos",components:2,type:"Int16"},{name:"a_extrude",components:2,type:"Int16"}],4),collisionCircleLayout:n([{name:"a_pos",components:2,type:"Int16"},{name:"a_anchor_pos",components:2,type:"Int16"},{name:"a_extrude",components:2,type:"Int16"}],4),placement:n([{type:"Int16",name:"anchorX"},{type:"Int16",name:"anchorY"},{type:"Uint16",name:"glyphStartIndex"},{type:"Uint16",name:"numGlyphs"},{type:"Uint32",name:"vertexStartIndex"},{type:"Uint32",name:"lineStartIndex"},{type:"Uint32",name:"lineLength"},{type:"Uint16",name:"segment"},{type:"Uint16",name:"lowerSize"},{type:"Uint16",name:"upperSize"},{type:"Float32",name:"lineOffsetX"},{type:"Float32",name:"lineOffsetY"},{type:"Uint8",name:"writingMode"},{type:"Uint8",name:"hidden"}]),glyphOffset:n([{type:"Float32",name:"offsetX"}]),lineVertex:n([{type:"Int16",name:"x"},{type:"Int16",name:"y"},{type:"Int16",name:"tileUnitDistanceFromAnchor"}])};e.exports=i},{"../../util/struct_array":271}],51:[function(t,e,r){function n(t,e,r,n,i,a,o,s){t.emplaceBack(e,r,Math.round(64*n),Math.round(64*i),a,o,s?s[0]:0,s?s[1]:0)}function i(t,e,r){t.emplaceBack(e.x,e.y,r),t.emplaceBack(e.x,e.y,r),t.emplaceBack(e.x,e.y,r),t.emplaceBack(e.x,e.y,r)}var a=t("./symbol_attributes"),o=a.symbolLayoutAttributes,s=a.collisionVertexAttributes,l=a.collisionBoxLayout,c=a.collisionCircleLayout,u=a.dynamicLayoutAttributes,f=t("../array_types"),h=f.SymbolLayoutArray,p=f.SymbolDynamicLayoutArray,d=f.SymbolOpacityArray,g=f.CollisionBoxLayoutArray,m=f.CollisionCircleLayoutArray,v=f.CollisionVertexArray,y=f.PlacedSymbolArray,x=f.GlyphOffsetArray,b=f.SymbolLineVertexArray,_=t("@mapbox/point-geometry"),w=t("../segment").SegmentVector,k=t("../program_configuration").ProgramConfigurationSet,M=t("../index_array_type"),A=M.TriangleIndexArray,T=M.LineIndexArray,S=t("../../symbol/transform_text"),C=t("../../symbol/mergelines"),E=t("../../util/script_detection"),L=t("../load_geometry"),z=t("@mapbox/vector-tile").VectorTileFeature.types,P=t("../../util/verticalize_punctuation"),D=(t("../../symbol/anchor"),t("../../symbol/symbol_size").getSizeData),O=t("../../util/web_worker_transfer").register,I=[{name:"a_fade_opacity",components:1,type:"Uint8",offset:0}],R=function(t){this.layoutVertexArray=new h,this.indexArray=new A,this.programConfigurations=t,this.segments=new w,this.dynamicLayoutVertexArray=new p,this.opacityVertexArray=new d,this.placedSymbolArray=new y};R.prototype.upload=function(t,e){this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,o.members),this.indexBuffer=t.createIndexBuffer(this.indexArray,e),this.programConfigurations.upload(t),this.dynamicLayoutVertexBuffer=t.createVertexBuffer(this.dynamicLayoutVertexArray,u.members,!0),this.opacityVertexBuffer=t.createVertexBuffer(this.opacityVertexArray,I,!0),this.opacityVertexBuffer.itemSize=1},R.prototype.destroy=function(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.programConfigurations.destroy(),this.segments.destroy(),this.dynamicLayoutVertexBuffer.destroy(),this.opacityVertexBuffer.destroy())},O("SymbolBuffers",R);var B=function(t,e,r){this.layoutVertexArray=new t,this.layoutAttributes=e,this.indexArray=new r,this.segments=new w,this.collisionVertexArray=new v};B.prototype.upload=function(t){this.layoutVertexBuffer=t.createVertexBuffer(this.layoutVertexArray,this.layoutAttributes),this.indexBuffer=t.createIndexBuffer(this.indexArray),this.collisionVertexBuffer=t.createVertexBuffer(this.collisionVertexArray,s.members,!0)},B.prototype.destroy=function(){this.layoutVertexBuffer&&(this.layoutVertexBuffer.destroy(),this.indexBuffer.destroy(),this.segments.destroy(),this.collisionVertexBuffer.destroy())},O("CollisionBuffers",B);var F=function(t){this.collisionBoxArray=t.collisionBoxArray,this.zoom=t.zoom,this.overscaling=t.overscaling,this.layers=t.layers,this.layerIds=this.layers.map(function(t){return t.id}),this.index=t.index,this.pixelRatio=t.pixelRatio;var e=this.layers[0]._unevaluatedLayout._values;this.textSizeData=D(this.zoom,e["text-size"]),this.iconSizeData=D(this.zoom,e["icon-size"]);var r=this.layers[0].layout;this.sortFeaturesByY=r.get("text-allow-overlap")||r.get("icon-allow-overlap")||r.get("text-ignore-placement")||r.get("icon-ignore-placement")};F.prototype.createArrays=function(){this.text=new R(new k(o.members,this.layers,this.zoom,function(t){return/^text/.test(t)})),this.icon=new R(new k(o.members,this.layers,this.zoom,function(t){return/^icon/.test(t)})),this.collisionBox=new B(g,l.members,T),this.collisionCircle=new B(m,c.members,A),this.glyphOffsetArray=new x,this.lineVertexArray=new b},F.prototype.populate=function(t,e){var r=this.layers[0],n=r.layout,i=n.get("text-font"),a=n.get("text-field"),o=n.get("icon-image"),s=("constant"!==a.value.kind||a.value.value.length>0)&&("constant"!==i.value.kind||i.value.value.length>0),l="constant"!==o.value.kind||o.value.value&&o.value.value.length>0;if(this.features=[],s||l){for(var c=e.iconDependencies,u=e.glyphDependencies,f={zoom:this.zoom},h=0,p=t;h=0;s--)a[s]={x:e[s].x,y:e[s].y,tileUnitDistanceFromAnchor:i},s>0&&(i+=e[s-1].dist(e[s]));for(var l=0;l0;t.addCollisionDebugVertices(l,c,u,f,h?t.collisionCircle:t.collisionBox,s.anchorPoint,n,h)}}}},F.prototype.deserializeCollisionBoxes=function(t,e,r,n,i){for(var a={},o=e;o0},F.prototype.hasIconData=function(){return this.icon.segments.get().length>0},F.prototype.hasCollisionBoxData=function(){return this.collisionBox.segments.get().length>0},F.prototype.hasCollisionCircleData=function(){return this.collisionCircle.segments.get().length>0},F.prototype.sortFeatures=function(t){var e=this;if(this.sortFeaturesByY&&this.sortedAngle!==t&&(this.sortedAngle=t,!(this.text.segments.get().length>1||this.icon.segments.get().length>1))){for(var r=[],n=0;n=this.dim+this.border||e<-this.border||e>=this.dim+this.border)throw new RangeError("out of range source coordinates for DEM data");return(e+this.border)*this.stride+(t+this.border)},a("Level",o);var s=function(t,e,r){this.uid=t,this.scale=e||1,this.level=r||new o(256,512),this.loaded=!!r};s.prototype.loadFromImage=function(t){if(t.height!==t.width)throw new RangeError("DEM tiles must be square");for(var e=this.level=new o(t.width,t.width/2),r=t.data,n=0;no.max||c.yo.max)&&i.warnOnce("Geometry exceeds allowed extent, reduce your vector tile buffer size")}return r}},{"../util/util":275,"./extent":53}],57:[function(t,e,r){var n=t("../util/struct_array").createLayout;e.exports=n([{name:"a_pos",type:"Int16",components:2}])},{"../util/struct_array":271}],58:[function(t,e,r){function n(t){return[a(255*t.r,255*t.g),a(255*t.b,255*t.a)]}function i(t,e){return{"text-opacity":"opacity","icon-opacity":"opacity","text-color":"fill_color","icon-color":"fill_color","text-halo-color":"halo_color","icon-halo-color":"halo_color","text-halo-blur":"halo_blur","icon-halo-blur":"halo_blur","text-halo-width":"halo_width","icon-halo-width":"halo_width","line-gap-width":"gapwidth"}[t]||t.replace(e+"-","").replace(/-/g,"_")}var a=t("../shaders/encode_attribute").packUint8ToFloat,o=(t("../style-spec/util/color"),t("../util/web_worker_transfer").register),s=t("../style/properties").PossiblyEvaluatedPropertyValue,l=t("./array_types"),c=l.StructArrayLayout1f4,u=l.StructArrayLayout2f8,f=l.StructArrayLayout4f16,h=function(t,e,r){this.value=t,this.name=e,this.type=r,this.statistics={max:-1/0}};h.prototype.defines=function(){return["#define HAS_UNIFORM_u_"+this.name]},h.prototype.populatePaintArray=function(){},h.prototype.upload=function(){},h.prototype.destroy=function(){},h.prototype.setUniforms=function(t,e,r,n){var i=n.constantOr(this.value),a=t.gl;"color"===this.type?a.uniform4f(e.uniforms["u_"+this.name],i.r,i.g,i.b,i.a):a.uniform1f(e.uniforms["u_"+this.name],i)};var p=function(t,e,r){this.expression=t,this.name=e,this.type=r,this.statistics={max:-1/0};var n="color"===r?u:c;this.paintVertexAttributes=[{name:"a_"+e,type:"Float32",components:"color"===r?2:1,offset:0}],this.paintVertexArray=new n};p.prototype.defines=function(){return[]},p.prototype.populatePaintArray=function(t,e){var r=this.paintVertexArray,i=r.length;r.reserve(t);var a=this.expression.evaluate({zoom:0},e);if("color"===this.type)for(var o=n(a),s=i;sa&&n("Max vertices per segment is "+a+": bucket requested "+t),(!o||o.vertexLength+t>e.exports.MAX_VERTEX_ARRAY_LENGTH)&&(o={vertexOffset:r.length,primitiveOffset:i.length,vertexLength:0,primitiveLength:0},this.segments.push(o)),o},o.prototype.get=function(){return this.segments},o.prototype.destroy=function(){for(var t=0,e=this.segments;t90||this.lat<-90)throw new Error("Invalid LngLat latitude value: must be between -90 and 90")};i.prototype.wrap=function(){return new i(n(this.lng,-180,180),this.lat)},i.prototype.toArray=function(){return[this.lng,this.lat]},i.prototype.toString=function(){return"LngLat("+this.lng+", "+this.lat+")"},i.prototype.toBounds=function(e){var r=360*e/40075017,n=r/Math.cos(Math.PI/180*this.lat);return new(t("./lng_lat_bounds"))(new i(this.lng-n,this.lat-r),new i(this.lng+n,this.lat+r))},i.convert=function(t){if(t instanceof i)return t;if(Array.isArray(t)&&(2===t.length||3===t.length))return new i(Number(t[0]),Number(t[1]));if(!Array.isArray(t)&&"object"==typeof t&&null!==t)return new i(Number(t.lng),Number(t.lat));throw new Error("`LngLatLike` argument must be specified as a LngLat instance, an object {lng: , lat: }, or an array of [, ]")},e.exports=i},{"../util/util":275,"./lng_lat_bounds":63}],63:[function(t,e,r){var n=t("./lng_lat"),i=function(t,e){t&&(e?this.setSouthWest(t).setNorthEast(e):4===t.length?this.setSouthWest([t[0],t[1]]).setNorthEast([t[2],t[3]]):this.setSouthWest(t[0]).setNorthEast(t[1]))};i.prototype.setNorthEast=function(t){return this._ne=t instanceof n?new n(t.lng,t.lat):n.convert(t),this},i.prototype.setSouthWest=function(t){return this._sw=t instanceof n?new n(t.lng,t.lat):n.convert(t),this},i.prototype.extend=function(t){var e,r,a=this._sw,o=this._ne;if(t instanceof n)e=t,r=t;else{if(!(t instanceof i))return Array.isArray(t)?t.every(Array.isArray)?this.extend(i.convert(t)):this.extend(n.convert(t)):this;if(e=t._sw,r=t._ne,!e||!r)return this}return a||o?(a.lng=Math.min(e.lng,a.lng),a.lat=Math.min(e.lat,a.lat),o.lng=Math.max(r.lng,o.lng),o.lat=Math.max(r.lat,o.lat)):(this._sw=new n(e.lng,e.lat),this._ne=new n(r.lng,r.lat)),this},i.prototype.getCenter=function(){return new n((this._sw.lng+this._ne.lng)/2,(this._sw.lat+this._ne.lat)/2)},i.prototype.getSouthWest=function(){return this._sw},i.prototype.getNorthEast=function(){return this._ne},i.prototype.getNorthWest=function(){return new n(this.getWest(),this.getNorth())},i.prototype.getSouthEast=function(){return new n(this.getEast(),this.getSouth())},i.prototype.getWest=function(){return this._sw.lng},i.prototype.getSouth=function(){return this._sw.lat},i.prototype.getEast=function(){return this._ne.lng},i.prototype.getNorth=function(){return this._ne.lat},i.prototype.toArray=function(){return[this._sw.toArray(),this._ne.toArray()]},i.prototype.toString=function(){return"LngLatBounds("+this._sw.toString()+", "+this._ne.toString()+")"},i.prototype.isEmpty=function(){return!(this._sw&&this._ne)},i.convert=function(t){return!t||t instanceof i?t:new i(t)},e.exports=i},{"./lng_lat":62}],64:[function(t,e,r){var n=t("./lng_lat"),i=t("@mapbox/point-geometry"),a=t("./coordinate"),o=t("../util/util"),s=t("../style-spec/util/interpolate").number,l=t("../util/tile_cover"),c=t("../source/tile_id"),u=(c.CanonicalTileID,c.UnwrappedTileID),f=t("../data/extent"),h=t("@mapbox/gl-matrix"),p=h.vec4,d=h.mat4,g=h.mat2,m=function(t,e,r){this.tileSize=512,this._renderWorldCopies=void 0===r||r,this._minZoom=t||0,this._maxZoom=e||22,this.latRange=[-85.05113,85.05113],this.width=0,this.height=0,this._center=new n(0,0),this.zoom=0,this.angle=0,this._fov=.6435011087932844,this._pitch=0,this._unmodified=!0,this._posMatrixCache={},this._alignedPosMatrixCache={}},v={minZoom:{},maxZoom:{},renderWorldCopies:{},worldSize:{},centerPoint:{},size:{},bearing:{},pitch:{},fov:{},zoom:{},center:{},unmodified:{},x:{},y:{},point:{}};m.prototype.clone=function(){var t=new m(this._minZoom,this._maxZoom,this._renderWorldCopies);return t.tileSize=this.tileSize,t.latRange=this.latRange,t.width=this.width,t.height=this.height,t._center=this._center,t.zoom=this.zoom,t.angle=this.angle,t._fov=this._fov,t._pitch=this._pitch,t._unmodified=this._unmodified,t._calcMatrices(),t},v.minZoom.get=function(){return this._minZoom},v.minZoom.set=function(t){this._minZoom!==t&&(this._minZoom=t,this.zoom=Math.max(this.zoom,t))},v.maxZoom.get=function(){return this._maxZoom},v.maxZoom.set=function(t){this._maxZoom!==t&&(this._maxZoom=t,this.zoom=Math.min(this.zoom,t))},v.renderWorldCopies.get=function(){return this._renderWorldCopies},v.worldSize.get=function(){return this.tileSize*this.scale},v.centerPoint.get=function(){return this.size._div(2)},v.size.get=function(){return new i(this.width,this.height)},v.bearing.get=function(){return-this.angle/Math.PI*180},v.bearing.set=function(t){var e=-o.wrap(t,-180,180)*Math.PI/180;this.angle!==e&&(this._unmodified=!1,this.angle=e,this._calcMatrices(),this.rotationMatrix=g.create(),g.rotate(this.rotationMatrix,this.rotationMatrix,this.angle))},v.pitch.get=function(){return this._pitch/Math.PI*180},v.pitch.set=function(t){var e=o.clamp(t,0,60)/180*Math.PI;this._pitch!==e&&(this._unmodified=!1,this._pitch=e,this._calcMatrices())},v.fov.get=function(){return this._fov/Math.PI*180},v.fov.set=function(t){t=Math.max(.01,Math.min(60,t)),this._fov!==t&&(this._unmodified=!1,this._fov=t/180*Math.PI,this._calcMatrices())},v.zoom.get=function(){return this._zoom},v.zoom.set=function(t){var e=Math.min(Math.max(t,this.minZoom),this.maxZoom);this._zoom!==e&&(this._unmodified=!1,this._zoom=e,this.scale=this.zoomScale(e),this.tileZoom=Math.floor(e),this.zoomFraction=e-this.tileZoom,this._constrain(),this._calcMatrices())},v.center.get=function(){return this._center},v.center.set=function(t){t.lat===this._center.lat&&t.lng===this._center.lng||(this._unmodified=!1,this._center=t,this._constrain(),this._calcMatrices())},m.prototype.coveringZoomLevel=function(t){return(t.roundZoom?Math.round:Math.floor)(this.zoom+this.scaleZoom(this.tileSize/t.tileSize))},m.prototype.getVisibleUnwrappedCoordinates=function(t){var e=this.pointCoordinate(new i(0,0),0),r=this.pointCoordinate(new i(this.width,0),0),n=Math.floor(e.column),a=Math.floor(r.column),o=[new u(0,t)];if(this._renderWorldCopies)for(var s=n;s<=a;s++)0!==s&&o.push(new u(s,t));return o},m.prototype.coveringTiles=function(t){var e=this.coveringZoomLevel(t),r=e;if(void 0!==t.minzoom&&et.maxzoom&&(e=t.maxzoom);var n=this.pointCoordinate(this.centerPoint,e),a=new i(n.column-.5,n.row-.5),o=[this.pointCoordinate(new i(0,0),e),this.pointCoordinate(new i(this.width,0),e),this.pointCoordinate(new i(this.width,this.height),e),this.pointCoordinate(new i(0,this.height),e)];return l(e,o,t.reparseOverscaled?r:e,this._renderWorldCopies).sort(function(t,e){return a.dist(t.canonical)-a.dist(e.canonical)})},m.prototype.resize=function(t,e){this.width=t,this.height=e,this.pixelsToGLUnits=[2/t,-2/e],this._constrain(),this._calcMatrices()},v.unmodified.get=function(){return this._unmodified},m.prototype.zoomScale=function(t){return Math.pow(2,t)},m.prototype.scaleZoom=function(t){return Math.log(t)/Math.LN2},m.prototype.project=function(t){return new i(this.lngX(t.lng),this.latY(t.lat))},m.prototype.unproject=function(t){return new n(this.xLng(t.x),this.yLat(t.y))},v.x.get=function(){return this.lngX(this.center.lng)},v.y.get=function(){return this.latY(this.center.lat)},v.point.get=function(){return new i(this.x,this.y)},m.prototype.lngX=function(t){return(180+t)*this.worldSize/360},m.prototype.latY=function(t){return(180-180/Math.PI*Math.log(Math.tan(Math.PI/4+t*Math.PI/360)))*this.worldSize/360},m.prototype.xLng=function(t){return 360*t/this.worldSize-180},m.prototype.yLat=function(t){var e=180-360*t/this.worldSize;return 360/Math.PI*Math.atan(Math.exp(e*Math.PI/180))-90},m.prototype.setLocationAtPoint=function(t,e){var r=this.pointCoordinate(e)._sub(this.pointCoordinate(this.centerPoint));this.center=this.coordinateLocation(this.locationCoordinate(t)._sub(r)),this._renderWorldCopies&&(this.center=this.center.wrap())},m.prototype.locationPoint=function(t){return this.coordinatePoint(this.locationCoordinate(t))},m.prototype.pointLocation=function(t){return this.coordinateLocation(this.pointCoordinate(t))},m.prototype.locationCoordinate=function(t){return new a(this.lngX(t.lng)/this.tileSize,this.latY(t.lat)/this.tileSize,this.zoom).zoomTo(this.tileZoom)},m.prototype.coordinateLocation=function(t){var e=t.zoomTo(this.zoom);return new n(this.xLng(e.column*this.tileSize),this.yLat(e.row*this.tileSize))},m.prototype.pointCoordinate=function(t,e){void 0===e&&(e=this.tileZoom);var r=[t.x,t.y,0,1],n=[t.x,t.y,1,1];p.transformMat4(r,r,this.pixelMatrixInverse),p.transformMat4(n,n,this.pixelMatrixInverse);var i=r[3],o=n[3],l=r[1]/i,c=n[1]/o,u=r[2]/i,f=n[2]/o,h=u===f?0:(0-u)/(f-u);return new a(s(r[0]/i,n[0]/o,h)/this.tileSize,s(l,c,h)/this.tileSize,this.zoom)._zoomTo(e)},m.prototype.coordinatePoint=function(t){var e=t.zoomTo(this.zoom),r=[e.column*this.tileSize,e.row*this.tileSize,0,1];return p.transformMat4(r,r,this.pixelMatrix),new i(r[0]/r[3],r[1]/r[3])},m.prototype.calculatePosMatrix=function(t,e){void 0===e&&(e=!1);var r=t.key,n=e?this._alignedPosMatrixCache:this._posMatrixCache;if(n[r])return n[r];var i=t.canonical,a=this.worldSize/this.zoomScale(i.z),o=i.x+Math.pow(2,i.z)*t.wrap,s=d.identity(new Float64Array(16));return d.translate(s,s,[o*a,i.y*a,0]),d.scale(s,s,[a/f,a/f,1]),d.multiply(s,e?this.alignedProjMatrix:this.projMatrix,s),n[r]=new Float32Array(s),n[r]},m.prototype._constrain=function(){if(this.center&&this.width&&this.height&&!this._constraining){this._constraining=!0;var t,e,r,n,a=-90,o=90,s=-180,l=180,c=this.size,u=this._unmodified;if(this.latRange){var f=this.latRange;a=this.latY(f[1]),t=(o=this.latY(f[0]))-ao&&(n=o-g)}if(this.lngRange){var m=this.x,v=c.x/2;m-vl&&(r=l-v)}void 0===r&&void 0===n||(this.center=this.unproject(new i(void 0!==r?r:this.x,void 0!==n?n:this.y))),this._unmodified=u,this._constraining=!1}},m.prototype._calcMatrices=function(){if(this.height){this.cameraToCenterDistance=.5/Math.tan(this._fov/2)*this.height;var t=this._fov/2,e=Math.PI/2+this._pitch,r=Math.sin(t)*this.cameraToCenterDistance/Math.sin(Math.PI-e-t),n=this.x,i=this.y,a=1.01*(Math.cos(Math.PI/2-this._pitch)*r+this.cameraToCenterDistance),o=new Float64Array(16);d.perspective(o,this._fov,this.width/this.height,1,a),d.scale(o,o,[1,-1,1]),d.translate(o,o,[0,0,-this.cameraToCenterDistance]),d.rotateX(o,o,this._pitch),d.rotateZ(o,o,this.angle),d.translate(o,o,[-n,-i,0]);var s=this.worldSize/(2*Math.PI*6378137*Math.abs(Math.cos(this.center.lat*(Math.PI/180))));d.scale(o,o,[1,1,s,1]),this.projMatrix=o;var l=this.width%2/2,c=this.height%2/2,u=Math.cos(this.angle),f=Math.sin(this.angle),h=n-Math.round(n)+u*l+f*c,p=i-Math.round(i)+u*c+f*l,g=new Float64Array(o);if(d.translate(g,g,[h>.5?h-1:h,p>.5?p-1:p,0]),this.alignedProjMatrix=g,o=d.create(),d.scale(o,o,[this.width/2,-this.height/2,1]),d.translate(o,o,[1,-1,0]),this.pixelMatrix=d.multiply(new Float64Array(16),o,this.projMatrix),!(o=d.invert(new Float64Array(16),this.pixelMatrix)))throw new Error("failed to invert matrix");this.pixelMatrixInverse=o,this._posMatrixCache={},this._alignedPosMatrixCache={}}},Object.defineProperties(m.prototype,v),e.exports=m},{"../data/extent":53,"../source/tile_id":114,"../style-spec/util/interpolate":158,"../util/tile_cover":273,"../util/util":275,"./coordinate":61,"./lng_lat":62,"@mapbox/gl-matrix":2,"@mapbox/point-geometry":4}],65:[function(t,e,r){var n=t("../style-spec/util/color"),i=function(t,e,r){this.blendFunction=t,this.blendColor=e,this.mask=r};i.disabled=new i(i.Replace=[1,0],n.transparent,[!1,!1,!1,!1]),i.unblended=new i(i.Replace,n.transparent,[!0,!0,!0,!0]),i.alphaBlended=new i([1,771],n.transparent,[!0,!0,!0,!0]),e.exports=i},{"../style-spec/util/color":153}],66:[function(t,e,r){var n=t("./index_buffer"),i=t("./vertex_buffer"),a=t("./framebuffer"),o=(t("./depth_mode"),t("./stencil_mode"),t("./color_mode")),s=t("../util/util"),l=t("./value"),c=l.ClearColor,u=l.ClearDepth,f=l.ClearStencil,h=l.ColorMask,p=l.DepthMask,d=l.StencilMask,g=l.StencilFunc,m=l.StencilOp,v=l.StencilTest,y=l.DepthRange,x=l.DepthTest,b=l.DepthFunc,_=l.Blend,w=l.BlendFunc,k=l.BlendColor,M=l.Program,A=l.LineWidth,T=l.ActiveTextureUnit,S=l.Viewport,C=l.BindFramebuffer,E=l.BindRenderbuffer,L=l.BindTexture,z=l.BindVertexBuffer,P=l.BindElementBuffer,D=l.BindVertexArrayOES,O=l.PixelStoreUnpack,I=l.PixelStoreUnpackPremultiplyAlpha,R=function(t){this.gl=t,this.extVertexArrayObject=this.gl.getExtension("OES_vertex_array_object"),this.lineWidthRange=t.getParameter(t.ALIASED_LINE_WIDTH_RANGE),this.clearColor=new c(this),this.clearDepth=new u(this),this.clearStencil=new f(this),this.colorMask=new h(this),this.depthMask=new p(this),this.stencilMask=new d(this),this.stencilFunc=new g(this),this.stencilOp=new m(this),this.stencilTest=new v(this),this.depthRange=new y(this),this.depthTest=new x(this),this.depthFunc=new b(this),this.blend=new _(this),this.blendFunc=new w(this),this.blendColor=new k(this),this.program=new M(this),this.lineWidth=new A(this),this.activeTexture=new T(this),this.viewport=new S(this),this.bindFramebuffer=new C(this),this.bindRenderbuffer=new E(this),this.bindTexture=new L(this),this.bindVertexBuffer=new z(this),this.bindElementBuffer=new P(this),this.bindVertexArrayOES=this.extVertexArrayObject&&new D(this),this.pixelStoreUnpack=new O(this),this.pixelStoreUnpackPremultiplyAlpha=new I(this),this.extTextureFilterAnisotropic=t.getExtension("EXT_texture_filter_anisotropic")||t.getExtension("MOZ_EXT_texture_filter_anisotropic")||t.getExtension("WEBKIT_EXT_texture_filter_anisotropic"),this.extTextureFilterAnisotropic&&(this.extTextureFilterAnisotropicMax=t.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT)),this.extTextureHalfFloat=t.getExtension("OES_texture_half_float"),this.extTextureHalfFloat&&t.getExtension("OES_texture_half_float_linear")};R.prototype.createIndexBuffer=function(t,e){return new n(this,t,e)},R.prototype.createVertexBuffer=function(t,e,r){return new i(this,t,e,r)},R.prototype.createRenderbuffer=function(t,e,r){var n=this.gl,i=n.createRenderbuffer();return this.bindRenderbuffer.set(i),n.renderbufferStorage(n.RENDERBUFFER,t,e,r),this.bindRenderbuffer.set(null),i},R.prototype.createFramebuffer=function(t,e){return new a(this,t,e)},R.prototype.clear=function(t){var e=t.color,r=t.depth,n=this.gl,i=0;e&&(i|=n.COLOR_BUFFER_BIT,this.clearColor.set(e),this.colorMask.set([!0,!0,!0,!0])),void 0!==r&&(i|=n.DEPTH_BUFFER_BIT,this.clearDepth.set(r),this.depthMask.set(!0)),n.clear(i)},R.prototype.setDepthMode=function(t){t.func!==this.gl.ALWAYS||t.mask?(this.depthTest.set(!0),this.depthFunc.set(t.func),this.depthMask.set(t.mask),this.depthRange.set(t.range)):this.depthTest.set(!1)},R.prototype.setStencilMode=function(t){t.func!==this.gl.ALWAYS||t.mask?(this.stencilTest.set(!0),this.stencilMask.set(t.mask),this.stencilOp.set([t.fail,t.depthFail,t.pass]),this.stencilFunc.set({func:t.test.func,ref:t.ref,mask:t.test.mask})):this.stencilTest.set(!1)},R.prototype.setColorMode=function(t){s.deepEqual(t.blendFunction,o.Replace)?this.blend.set(!1):(this.blend.set(!0),this.blendFunc.set(t.blendFunction),this.blendColor.set(t.blendColor)),this.colorMask.set(t.mask)},e.exports=R},{"../util/util":275,"./color_mode":65,"./depth_mode":67,"./framebuffer":68,"./index_buffer":69,"./stencil_mode":70,"./value":71,"./vertex_buffer":72}],67:[function(t,e,r){var n=function(t,e,r){this.func=t,this.mask=e,this.range=r};n.ReadOnly=!1,n.ReadWrite=!0,n.disabled=new n(519,n.ReadOnly,[0,1]),e.exports=n},{}],68:[function(t,e,r){var n=t("./value"),i=n.ColorAttachment,a=n.DepthAttachment,o=function(t,e,r){this.context=t,this.width=e,this.height=r;var n=t.gl,o=this.framebuffer=n.createFramebuffer();this.colorAttachment=new i(t,o),this.depthAttachment=new a(t,o)};o.prototype.destroy=function(){var t=this.context.gl,e=this.colorAttachment.get();e&&t.deleteTexture(e);var r=this.depthAttachment.get();r&&t.deleteRenderbuffer(r),t.deleteFramebuffer(this.framebuffer)},e.exports=o},{"./value":71}],69:[function(t,e,r){var n=function(t,e,r){this.context=t;var n=t.gl;this.buffer=n.createBuffer(),this.dynamicDraw=Boolean(r),this.unbindVAO(),t.bindElementBuffer.set(this.buffer),n.bufferData(n.ELEMENT_ARRAY_BUFFER,e.arrayBuffer,this.dynamicDraw?n.DYNAMIC_DRAW:n.STATIC_DRAW),this.dynamicDraw||delete e.arrayBuffer};n.prototype.unbindVAO=function(){this.context.extVertexArrayObject&&this.context.bindVertexArrayOES.set(null)},n.prototype.bind=function(){this.context.bindElementBuffer.set(this.buffer)},n.prototype.updateData=function(t){var e=this.context.gl;this.unbindVAO(),this.bind(),e.bufferSubData(e.ELEMENT_ARRAY_BUFFER,0,t.arrayBuffer)},n.prototype.destroy=function(){var t=this.context.gl;this.buffer&&(t.deleteBuffer(this.buffer),delete this.buffer)},e.exports=n},{}],70:[function(t,e,r){var n=function(t,e,r,n,i,a){this.test=t,this.ref=e,this.mask=r,this.fail=n,this.depthFail=i,this.pass=a};n.disabled=new n({func:519,mask:0},0,0,7680,7680,7680),e.exports=n},{}],71:[function(t,e,r){var n=t("../style-spec/util/color"),i=t("../util/util"),a=function(t){this.context=t,this.current=n.transparent};a.prototype.get=function(){return this.current},a.prototype.set=function(t){var e=this.current;t.r===e.r&&t.g===e.g&&t.b===e.b&&t.a===e.a||(this.context.gl.clearColor(t.r,t.g,t.b,t.a),this.current=t)};var o=function(t){this.context=t,this.current=1};o.prototype.get=function(){return this.current},o.prototype.set=function(t){this.current!==t&&(this.context.gl.clearDepth(t),this.current=t)};var s=function(t){this.context=t,this.current=0};s.prototype.get=function(){return this.current},s.prototype.set=function(t){this.current!==t&&(this.context.gl.clearStencil(t),this.current=t)};var l=function(t){this.context=t,this.current=[!0,!0,!0,!0]};l.prototype.get=function(){return this.current},l.prototype.set=function(t){var e=this.current;t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]||(this.context.gl.colorMask(t[0],t[1],t[2],t[3]),this.current=t)};var c=function(t){this.context=t,this.current=!0};c.prototype.get=function(){return this.current},c.prototype.set=function(t){this.current!==t&&(this.context.gl.depthMask(t),this.current=t)};var u=function(t){this.context=t,this.current=255};u.prototype.get=function(){return this.current},u.prototype.set=function(t){this.current!==t&&(this.context.gl.stencilMask(t),this.current=t)};var f=function(t){this.context=t,this.current={func:t.gl.ALWAYS,ref:0,mask:255}};f.prototype.get=function(){return this.current},f.prototype.set=function(t){var e=this.current;t.func===e.func&&t.ref===e.ref&&t.mask===e.mask||(this.context.gl.stencilFunc(t.func,t.ref,t.mask),this.current=t)};var h=function(t){this.context=t;var e=this.context.gl;this.current=[e.KEEP,e.KEEP,e.KEEP]};h.prototype.get=function(){return this.current},h.prototype.set=function(t){var e=this.current;t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]||(this.context.gl.stencilOp(t[0],t[1],t[2]),this.current=t)};var p=function(t){this.context=t,this.current=!1};p.prototype.get=function(){return this.current},p.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;t?e.enable(e.STENCIL_TEST):e.disable(e.STENCIL_TEST),this.current=t}};var d=function(t){this.context=t,this.current=[0,1]};d.prototype.get=function(){return this.current},d.prototype.set=function(t){var e=this.current;t[0]===e[0]&&t[1]===e[1]||(this.context.gl.depthRange(t[0],t[1]),this.current=t)};var g=function(t){this.context=t,this.current=!1};g.prototype.get=function(){return this.current},g.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;t?e.enable(e.DEPTH_TEST):e.disable(e.DEPTH_TEST),this.current=t}};var m=function(t){this.context=t,this.current=t.gl.LESS};m.prototype.get=function(){return this.current},m.prototype.set=function(t){this.current!==t&&(this.context.gl.depthFunc(t),this.current=t)};var v=function(t){this.context=t,this.current=!1};v.prototype.get=function(){return this.current},v.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;t?e.enable(e.BLEND):e.disable(e.BLEND),this.current=t}};var y=function(t){this.context=t;var e=this.context.gl;this.current=[e.ONE,e.ZERO]};y.prototype.get=function(){return this.current},y.prototype.set=function(t){var e=this.current;t[0]===e[0]&&t[1]===e[1]||(this.context.gl.blendFunc(t[0],t[1]),this.current=t)};var x=function(t){this.context=t,this.current=n.transparent};x.prototype.get=function(){return this.current},x.prototype.set=function(t){var e=this.current;t.r===e.r&&t.g===e.g&&t.b===e.b&&t.a===e.a||(this.context.gl.blendColor(t.r,t.g,t.b,t.a),this.current=t)};var b=function(t){this.context=t,this.current=null};b.prototype.get=function(){return this.current},b.prototype.set=function(t){this.current!==t&&(this.context.gl.useProgram(t),this.current=t)};var _=function(t){this.context=t,this.current=1};_.prototype.get=function(){return this.current},_.prototype.set=function(t){var e=this.context.lineWidthRange,r=i.clamp(t,e[0],e[1]);this.current!==r&&(this.context.gl.lineWidth(r),this.current=t)};var w=function(t){this.context=t,this.current=t.gl.TEXTURE0};w.prototype.get=function(){return this.current},w.prototype.set=function(t){this.current!==t&&(this.context.gl.activeTexture(t),this.current=t)};var k=function(t){this.context=t;var e=this.context.gl;this.current=[0,0,e.drawingBufferWidth,e.drawingBufferHeight]};k.prototype.get=function(){return this.current},k.prototype.set=function(t){var e=this.current;t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]||(this.context.gl.viewport(t[0],t[1],t[2],t[3]),this.current=t)};var M=function(t){this.context=t,this.current=null};M.prototype.get=function(){return this.current},M.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;e.bindFramebuffer(e.FRAMEBUFFER,t),this.current=t}};var A=function(t){this.context=t,this.current=null};A.prototype.get=function(){return this.current},A.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;e.bindRenderbuffer(e.RENDERBUFFER,t),this.current=t}};var T=function(t){this.context=t,this.current=null};T.prototype.get=function(){return this.current},T.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;e.bindTexture(e.TEXTURE_2D,t),this.current=t}};var S=function(t){this.context=t,this.current=null};S.prototype.get=function(){return this.current},S.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;e.bindBuffer(e.ARRAY_BUFFER,t),this.current=t}};var C=function(t){this.context=t,this.current=null};C.prototype.get=function(){return this.current},C.prototype.set=function(t){var e=this.context.gl;e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,t),this.current=t};var E=function(t){this.context=t,this.current=null};E.prototype.get=function(){return this.current},E.prototype.set=function(t){this.current!==t&&this.context.extVertexArrayObject&&(this.context.extVertexArrayObject.bindVertexArrayOES(t),this.current=t)};var L=function(t){this.context=t,this.current=4};L.prototype.get=function(){return this.current},L.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;e.pixelStorei(e.UNPACK_ALIGNMENT,t),this.current=t}};var z=function(t){this.context=t,this.current=!1};z.prototype.get=function(){return this.current},z.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,t),this.current=t}};var P=function(t,e){this.context=t,this.current=null,this.parent=e};P.prototype.get=function(){return this.current};var D=function(t){function e(e,r){t.call(this,e,r),this.dirty=!1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.set=function(t){if(this.dirty||this.current!==t){var e=this.context.gl;this.context.bindFramebuffer.set(this.parent),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,t,0),this.current=t,this.dirty=!1}},e.prototype.setDirty=function(){this.dirty=!0},e}(P),O=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.set=function(t){if(this.current!==t){var e=this.context.gl;this.context.bindFramebuffer.set(this.parent),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.DEPTH_ATTACHMENT,e.RENDERBUFFER,t),this.current=t}},e}(P);e.exports={ClearColor:a,ClearDepth:o,ClearStencil:s,ColorMask:l,DepthMask:c,StencilMask:u,StencilFunc:f,StencilOp:h,StencilTest:p,DepthRange:d,DepthTest:g,DepthFunc:m,Blend:v,BlendFunc:y,BlendColor:x,Program:b,LineWidth:_,ActiveTextureUnit:w,Viewport:k,BindFramebuffer:M,BindRenderbuffer:A,BindTexture:T,BindVertexBuffer:S,BindElementBuffer:C,BindVertexArrayOES:E,PixelStoreUnpack:L,PixelStoreUnpackPremultiplyAlpha:z,ColorAttachment:D,DepthAttachment:O}},{"../style-spec/util/color":153,"../util/util":275}],72:[function(t,e,r){var n={Int8:"BYTE",Uint8:"UNSIGNED_BYTE",Int16:"SHORT",Uint16:"UNSIGNED_SHORT",Int32:"INT",Uint32:"UNSIGNED_INT",Float32:"FLOAT"},i=function(t,e,r,n){this.length=e.length,this.attributes=r,this.itemSize=e.bytesPerElement,this.dynamicDraw=n,this.context=t;var i=t.gl;this.buffer=i.createBuffer(),t.bindVertexBuffer.set(this.buffer),i.bufferData(i.ARRAY_BUFFER,e.arrayBuffer,this.dynamicDraw?i.DYNAMIC_DRAW:i.STATIC_DRAW),this.dynamicDraw||delete e.arrayBuffer};i.prototype.bind=function(){this.context.bindVertexBuffer.set(this.buffer)},i.prototype.updateData=function(t){var e=this.context.gl;this.bind(),e.bufferSubData(e.ARRAY_BUFFER,0,t.arrayBuffer)},i.prototype.enableAttributes=function(t,e){for(var r=0;r":[24,[4,18,20,9,4,0]],"?":[18,[3,16,3,17,4,19,5,20,7,21,11,21,13,20,14,19,15,17,15,15,14,13,13,12,9,10,9,7,-1,-1,9,2,8,1,9,0,10,1,9,2]],"@":[27,[18,13,17,15,15,16,12,16,10,15,9,14,8,11,8,8,9,6,11,5,14,5,16,6,17,8,-1,-1,12,16,10,14,9,11,9,8,10,6,11,5,-1,-1,18,16,17,8,17,6,19,5,21,5,23,7,24,10,24,12,23,15,22,17,20,19,18,20,15,21,12,21,9,20,7,19,5,17,4,15,3,12,3,9,4,6,5,4,7,2,9,1,12,0,15,0,18,1,20,2,21,3,-1,-1,19,16,18,8,18,6,19,5]],A:[18,[9,21,1,0,-1,-1,9,21,17,0,-1,-1,4,7,14,7]],B:[21,[4,21,4,0,-1,-1,4,21,13,21,16,20,17,19,18,17,18,15,17,13,16,12,13,11,-1,-1,4,11,13,11,16,10,17,9,18,7,18,4,17,2,16,1,13,0,4,0]],C:[21,[18,16,17,18,15,20,13,21,9,21,7,20,5,18,4,16,3,13,3,8,4,5,5,3,7,1,9,0,13,0,15,1,17,3,18,5]],D:[21,[4,21,4,0,-1,-1,4,21,11,21,14,20,16,18,17,16,18,13,18,8,17,5,16,3,14,1,11,0,4,0]],E:[19,[4,21,4,0,-1,-1,4,21,17,21,-1,-1,4,11,12,11,-1,-1,4,0,17,0]],F:[18,[4,21,4,0,-1,-1,4,21,17,21,-1,-1,4,11,12,11]],G:[21,[18,16,17,18,15,20,13,21,9,21,7,20,5,18,4,16,3,13,3,8,4,5,5,3,7,1,9,0,13,0,15,1,17,3,18,5,18,8,-1,-1,13,8,18,8]],H:[22,[4,21,4,0,-1,-1,18,21,18,0,-1,-1,4,11,18,11]],I:[8,[4,21,4,0]],J:[16,[12,21,12,5,11,2,10,1,8,0,6,0,4,1,3,2,2,5,2,7]],K:[21,[4,21,4,0,-1,-1,18,21,4,7,-1,-1,9,12,18,0]],L:[17,[4,21,4,0,-1,-1,4,0,16,0]],M:[24,[4,21,4,0,-1,-1,4,21,12,0,-1,-1,20,21,12,0,-1,-1,20,21,20,0]],N:[22,[4,21,4,0,-1,-1,4,21,18,0,-1,-1,18,21,18,0]],O:[22,[9,21,7,20,5,18,4,16,3,13,3,8,4,5,5,3,7,1,9,0,13,0,15,1,17,3,18,5,19,8,19,13,18,16,17,18,15,20,13,21,9,21]],P:[21,[4,21,4,0,-1,-1,4,21,13,21,16,20,17,19,18,17,18,14,17,12,16,11,13,10,4,10]],Q:[22,[9,21,7,20,5,18,4,16,3,13,3,8,4,5,5,3,7,1,9,0,13,0,15,1,17,3,18,5,19,8,19,13,18,16,17,18,15,20,13,21,9,21,-1,-1,12,4,18,-2]],R:[21,[4,21,4,0,-1,-1,4,21,13,21,16,20,17,19,18,17,18,15,17,13,16,12,13,11,4,11,-1,-1,11,11,18,0]],S:[20,[17,18,15,20,12,21,8,21,5,20,3,18,3,16,4,14,5,13,7,12,13,10,15,9,16,8,17,6,17,3,15,1,12,0,8,0,5,1,3,3]],T:[16,[8,21,8,0,-1,-1,1,21,15,21]],U:[22,[4,21,4,6,5,3,7,1,10,0,12,0,15,1,17,3,18,6,18,21]],V:[18,[1,21,9,0,-1,-1,17,21,9,0]],W:[24,[2,21,7,0,-1,-1,12,21,7,0,-1,-1,12,21,17,0,-1,-1,22,21,17,0]],X:[20,[3,21,17,0,-1,-1,17,21,3,0]],Y:[18,[1,21,9,11,9,0,-1,-1,17,21,9,11]],Z:[20,[17,21,3,0,-1,-1,3,21,17,21,-1,-1,3,0,17,0]],"[":[14,[4,25,4,-7,-1,-1,5,25,5,-7,-1,-1,4,25,11,25,-1,-1,4,-7,11,-7]],"\\":[14,[0,21,14,-3]],"]":[14,[9,25,9,-7,-1,-1,10,25,10,-7,-1,-1,3,25,10,25,-1,-1,3,-7,10,-7]],"^":[16,[6,15,8,18,10,15,-1,-1,3,12,8,17,13,12,-1,-1,8,17,8,0]],_:[16,[0,-2,16,-2]],"`":[10,[6,21,5,20,4,18,4,16,5,15,6,16,5,17]],a:[19,[15,14,15,0,-1,-1,15,11,13,13,11,14,8,14,6,13,4,11,3,8,3,6,4,3,6,1,8,0,11,0,13,1,15,3]],b:[19,[4,21,4,0,-1,-1,4,11,6,13,8,14,11,14,13,13,15,11,16,8,16,6,15,3,13,1,11,0,8,0,6,1,4,3]],c:[18,[15,11,13,13,11,14,8,14,6,13,4,11,3,8,3,6,4,3,6,1,8,0,11,0,13,1,15,3]],d:[19,[15,21,15,0,-1,-1,15,11,13,13,11,14,8,14,6,13,4,11,3,8,3,6,4,3,6,1,8,0,11,0,13,1,15,3]],e:[18,[3,8,15,8,15,10,14,12,13,13,11,14,8,14,6,13,4,11,3,8,3,6,4,3,6,1,8,0,11,0,13,1,15,3]],f:[12,[10,21,8,21,6,20,5,17,5,0,-1,-1,2,14,9,14]],g:[19,[15,14,15,-2,14,-5,13,-6,11,-7,8,-7,6,-6,-1,-1,15,11,13,13,11,14,8,14,6,13,4,11,3,8,3,6,4,3,6,1,8,0,11,0,13,1,15,3]],h:[19,[4,21,4,0,-1,-1,4,10,7,13,9,14,12,14,14,13,15,10,15,0]],i:[8,[3,21,4,20,5,21,4,22,3,21,-1,-1,4,14,4,0]],j:[10,[5,21,6,20,7,21,6,22,5,21,-1,-1,6,14,6,-3,5,-6,3,-7,1,-7]],k:[17,[4,21,4,0,-1,-1,14,14,4,4,-1,-1,8,8,15,0]],l:[8,[4,21,4,0]],m:[30,[4,14,4,0,-1,-1,4,10,7,13,9,14,12,14,14,13,15,10,15,0,-1,-1,15,10,18,13,20,14,23,14,25,13,26,10,26,0]],n:[19,[4,14,4,0,-1,-1,4,10,7,13,9,14,12,14,14,13,15,10,15,0]],o:[19,[8,14,6,13,4,11,3,8,3,6,4,3,6,1,8,0,11,0,13,1,15,3,16,6,16,8,15,11,13,13,11,14,8,14]],p:[19,[4,14,4,-7,-1,-1,4,11,6,13,8,14,11,14,13,13,15,11,16,8,16,6,15,3,13,1,11,0,8,0,6,1,4,3]],q:[19,[15,14,15,-7,-1,-1,15,11,13,13,11,14,8,14,6,13,4,11,3,8,3,6,4,3,6,1,8,0,11,0,13,1,15,3]],r:[13,[4,14,4,0,-1,-1,4,8,5,11,7,13,9,14,12,14]],s:[17,[14,11,13,13,10,14,7,14,4,13,3,11,4,9,6,8,11,7,13,6,14,4,14,3,13,1,10,0,7,0,4,1,3,3]],t:[12,[5,21,5,4,6,1,8,0,10,0,-1,-1,2,14,9,14]],u:[19,[4,14,4,4,5,1,7,0,10,0,12,1,15,4,-1,-1,15,14,15,0]],v:[16,[2,14,8,0,-1,-1,14,14,8,0]],w:[22,[3,14,7,0,-1,-1,11,14,7,0,-1,-1,11,14,15,0,-1,-1,19,14,15,0]],x:[17,[3,14,14,0,-1,-1,14,14,3,0]],y:[16,[2,14,8,0,-1,-1,14,14,8,0,6,-4,4,-6,2,-7,1,-7]],z:[17,[14,14,3,0,-1,-1,3,14,14,14,-1,-1,3,0,14,0]],"{":[14,[9,25,7,24,6,23,5,21,5,19,6,17,7,16,8,14,8,12,6,10,-1,-1,7,24,6,22,6,20,7,18,8,17,9,15,9,13,8,11,4,9,8,7,9,5,9,3,8,1,7,0,6,-2,6,-4,7,-6,-1,-1,6,8,8,6,8,4,7,2,6,1,5,-1,5,-3,6,-5,7,-6,9,-7]],"|":[8,[4,25,4,-7]],"}":[14,[5,25,7,24,8,23,9,21,9,19,8,17,7,16,6,14,6,12,8,10,-1,-1,7,24,8,22,8,20,7,18,6,17,5,15,5,13,6,11,10,9,6,7,5,5,5,3,6,1,7,0,8,-2,8,-4,7,-6,-1,-1,8,8,6,6,6,4,7,2,8,1,9,-1,9,-3,8,-5,7,-6,5,-7]],"~":[24,[3,6,3,8,4,11,6,12,8,12,10,11,14,8,16,7,18,7,20,8,21,10,-1,-1,3,8,4,10,6,11,8,11,10,10,14,7,16,6,18,6,20,7,21,10,21,12]]}},{"../data/array_types":39,"../data/extent":53,"../data/pos_attributes":57,"../gl/depth_mode":67,"../gl/stencil_mode":70,"../util/browser":252,"./vertex_array_object":95,"@mapbox/gl-matrix":2}],78:[function(t,e,r){function n(t,e,r,n,i){if(!s.isPatternMissing(r.paint.get("fill-pattern"),t))for(var a=!0,o=0,l=n;o0){var l=o.now(),c=(l-t.timeAdded)/s,u=e?(l-e.timeAdded)/s:-1,f=r.getSource(),h=a.coveringZoomLevel({tileSize:f.tileSize,roundZoom:f.roundZoom}),p=!e||Math.abs(e.tileID.overscaledZ-h)>Math.abs(t.tileID.overscaledZ-h),d=p&&t.refreshedUponExpiration?1:i.clamp(p?c:1-u,0,1);return t.refreshedUponExpiration&&c>=1&&(t.refreshedUponExpiration=!1),e?{opacity:1,mix:1-d}:{opacity:d,mix:0}}return{opacity:1,mix:0}}var i=t("../util/util"),a=t("../source/image_source"),o=t("../util/browser"),s=t("../gl/stencil_mode"),l=t("../gl/depth_mode");e.exports=function(t,e,r,i){if("translucent"===t.renderPass&&0!==r.paint.get("raster-opacity")){var o=t.context,c=o.gl,u=e.getSource(),f=t.useProgram("raster");o.setStencilMode(s.disabled),o.setColorMode(t.colorModeForRenderPass()),c.uniform1f(f.uniforms.u_brightness_low,r.paint.get("raster-brightness-min")),c.uniform1f(f.uniforms.u_brightness_high,r.paint.get("raster-brightness-max")),c.uniform1f(f.uniforms.u_saturation_factor,function(t){return t>0?1-1/(1.001-t):-t}(r.paint.get("raster-saturation"))),c.uniform1f(f.uniforms.u_contrast_factor,function(t){return t>0?1/(1-t):1+t}(r.paint.get("raster-contrast"))),c.uniform3fv(f.uniforms.u_spin_weights,function(t){t*=Math.PI/180;var e=Math.sin(t),r=Math.cos(t);return[(2*r+1)/3,(-Math.sqrt(3)*e-r+1)/3,(Math.sqrt(3)*e-r+1)/3]}(r.paint.get("raster-hue-rotate"))),c.uniform1f(f.uniforms.u_buffer_scale,1),c.uniform1i(f.uniforms.u_image0,0),c.uniform1i(f.uniforms.u_image1,1);for(var h=i.length&&i[0].overscaledZ,p=0,d=i;p65535)e(new Error("glyphs > 65535 not supported"));else{var c=o.requests[l];c||(c=o.requests[l]=[],n(i,l,r.url,r.requestTransform,function(t,e){if(e)for(var r in e)o.glyphs[+r]=e[+r];for(var n=0,i=c;nthis.height)return n.warnOnce("LineAtlas out of space"),null;for(var o=0,s=0;s=0;this.currentLayer--){var m=r.style._layers[o[r.currentLayer]];m.source!==(d&&d.id)&&(g=[],(d=r.style.sourceCaches[m.source])&&(r.clearStencil(),g=d.getVisibleCoordinates(),d.getSource().isTileClipped&&r._renderTileClippingMasks(g))),r.renderLayer(r,d,m,g)}this.renderPass="translucent";var v,y=[];for(this.currentLayer=0,this.currentLayer;this.currentLayer0?e.pop():null},T.prototype._createProgramCached=function(t,e){this.cache=this.cache||{};var r=""+t+(e.cacheKey||"")+(this._showOverdrawInspector?"/overdraw":"");return this.cache[r]||(this.cache[r]=new y(this.context,v[t],e,this._showOverdrawInspector)),this.cache[r]},T.prototype.useProgram=function(t,e){var r=this._createProgramCached(t,e||this.emptyProgramConfiguration);return this.context.program.set(r.program),r},e.exports=T},{"../data/array_types":39,"../data/extent":53,"../data/pos_attributes":57,"../data/program_configuration":58,"../data/raster_bounds_attributes":59,"../gl/color_mode":65,"../gl/context":66,"../gl/depth_mode":67,"../gl/stencil_mode":70,"../shaders":97,"../source/pixels_to_tile_units":104,"../source/source_cache":111,"../style-spec/util/color":153,"../symbol/cross_tile_symbol_index":218,"../util/browser":252,"../util/util":275,"./draw_background":74,"./draw_circle":75,"./draw_debug":77,"./draw_fill":78,"./draw_fill_extrusion":79,"./draw_heatmap":80,"./draw_hillshade":81,"./draw_line":82,"./draw_raster":83,"./draw_symbol":84,"./program":92,"./texture":93,"./tile_mask":94,"./vertex_array_object":95,"@mapbox/gl-matrix":2}],91:[function(t,e,r){var n=t("../source/pixels_to_tile_units");r.isPatternMissing=function(t,e){if(!t)return!1;var r=e.imageManager.getPattern(t.from),n=e.imageManager.getPattern(t.to);return!r||!n},r.prepare=function(t,e,r){var n=e.context,i=n.gl,a=e.imageManager.getPattern(t.from),o=e.imageManager.getPattern(t.to);i.uniform1i(r.uniforms.u_image,0),i.uniform2fv(r.uniforms.u_pattern_tl_a,a.tl),i.uniform2fv(r.uniforms.u_pattern_br_a,a.br),i.uniform2fv(r.uniforms.u_pattern_tl_b,o.tl),i.uniform2fv(r.uniforms.u_pattern_br_b,o.br);var s=e.imageManager.getPixelSize(),l=s.width,c=s.height;i.uniform2fv(r.uniforms.u_texsize,[l,c]),i.uniform1f(r.uniforms.u_mix,t.t),i.uniform2fv(r.uniforms.u_pattern_size_a,a.displaySize),i.uniform2fv(r.uniforms.u_pattern_size_b,o.displaySize),i.uniform1f(r.uniforms.u_scale_a,t.fromScale),i.uniform1f(r.uniforms.u_scale_b,t.toScale),n.activeTexture.set(i.TEXTURE0),e.imageManager.bind(e.context)},r.setTile=function(t,e,r){var i=e.context.gl;i.uniform1f(r.uniforms.u_tile_units_to_pixels,1/n(t,1,e.transform.tileZoom));var a=Math.pow(2,t.tileID.overscaledZ),o=t.tileSize*Math.pow(2,e.transform.tileZoom)/a,s=o*(t.tileID.canonical.x+t.tileID.wrap*a),l=o*t.tileID.canonical.y;i.uniform2f(r.uniforms.u_pixel_coord_upper,s>>16,l>>16),i.uniform2f(r.uniforms.u_pixel_coord_lower,65535&s,65535&l)}},{"../source/pixels_to_tile_units":104}],92:[function(t,e,r){var n=t("../util/browser"),i=t("../shaders"),a=(t("../data/program_configuration").ProgramConfiguration,t("./vertex_array_object")),o=(t("../gl/context"),function(t,e,r,a){var o=this,s=t.gl;this.program=s.createProgram();var l=r.defines().concat("#define DEVICE_PIXEL_RATIO "+n.devicePixelRatio.toFixed(1));a&&l.push("#define OVERDRAW_INSPECTOR;");var c=l.concat(i.prelude.fragmentSource,e.fragmentSource).join("\n"),u=l.concat(i.prelude.vertexSource,e.vertexSource).join("\n"),f=s.createShader(s.FRAGMENT_SHADER);s.shaderSource(f,c),s.compileShader(f),s.attachShader(this.program,f);var h=s.createShader(s.VERTEX_SHADER);s.shaderSource(h,u),s.compileShader(h),s.attachShader(this.program,h);for(var p=r.layoutAttributes||[],d=0;d 0.5) {\n gl_FragColor = vec4(0.0, 0.0, 1.0, 0.5) * alpha;\n }\n\n if (v_notUsed > 0.5) {\n // This box not used, fade it out\n gl_FragColor *= .1;\n }\n}",vertexSource:"attribute vec2 a_pos;\nattribute vec2 a_anchor_pos;\nattribute vec2 a_extrude;\nattribute vec2 a_placed;\n\nuniform mat4 u_matrix;\nuniform vec2 u_extrude_scale;\nuniform float u_camera_to_center_distance;\n\nvarying float v_placed;\nvarying float v_notUsed;\n\nvoid main() {\n vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1);\n highp float camera_to_anchor_distance = projectedPoint.w;\n highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance);\n\n gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);\n gl_Position.xy += a_extrude * u_extrude_scale * gl_Position.w * collision_perspective_ratio;\n\n v_placed = a_placed.x;\n v_notUsed = a_placed.y;\n}\n"},collisionCircle:{fragmentSource:"\nvarying float v_placed;\nvarying float v_notUsed;\nvarying float v_radius;\nvarying vec2 v_extrude;\nvarying vec2 v_extrude_scale;\n\nvoid main() {\n float alpha = 0.5;\n\n // Red = collision, hide label\n vec4 color = vec4(1.0, 0.0, 0.0, 1.0) * alpha;\n\n // Blue = no collision, label is showing\n if (v_placed > 0.5) {\n color = vec4(0.0, 0.0, 1.0, 0.5) * alpha;\n }\n\n if (v_notUsed > 0.5) {\n // This box not used, fade it out\n color *= .2;\n }\n\n float extrude_scale_length = length(v_extrude_scale);\n float extrude_length = length(v_extrude) * extrude_scale_length;\n float stroke_width = 15.0 * extrude_scale_length;\n float radius = v_radius * extrude_scale_length;\n\n float distance_to_edge = abs(extrude_length - radius);\n float opacity_t = smoothstep(-stroke_width, 0.0, -distance_to_edge);\n\n gl_FragColor = opacity_t * color;\n}\n",vertexSource:"attribute vec2 a_pos;\nattribute vec2 a_anchor_pos;\nattribute vec2 a_extrude;\nattribute vec2 a_placed;\n\nuniform mat4 u_matrix;\nuniform vec2 u_extrude_scale;\nuniform float u_camera_to_center_distance;\n\nvarying float v_placed;\nvarying float v_notUsed;\nvarying float v_radius;\n\nvarying vec2 v_extrude;\nvarying vec2 v_extrude_scale;\n\nvoid main() {\n vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1);\n highp float camera_to_anchor_distance = projectedPoint.w;\n highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance);\n\n gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);\n\n highp float padding_factor = 1.2; // Pad the vertices slightly to make room for anti-alias blur\n gl_Position.xy += a_extrude * u_extrude_scale * padding_factor * gl_Position.w * collision_perspective_ratio;\n\n v_placed = a_placed.x;\n v_notUsed = a_placed.y;\n v_radius = abs(a_extrude.y); // We don't pitch the circles, so both units of the extrusion vector are equal in magnitude to the radius\n\n v_extrude = a_extrude * padding_factor;\n v_extrude_scale = u_extrude_scale * u_camera_to_center_distance * collision_perspective_ratio;\n}\n"},debug:{fragmentSource:"uniform highp vec4 u_color;\n\nvoid main() {\n gl_FragColor = u_color;\n}\n",vertexSource:"attribute vec2 a_pos;\n\nuniform mat4 u_matrix;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n"},fill:{fragmentSource:"#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 color\n #pragma mapbox: initialize lowp float opacity\n\n gl_FragColor = color * opacity;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"attribute vec2 a_pos;\n\nuniform mat4 u_matrix;\n\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 color\n #pragma mapbox: initialize lowp float opacity\n\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n"},fillOutline:{fragmentSource:"#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\n\nvarying vec2 v_pos;\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 outline_color\n #pragma mapbox: initialize lowp float opacity\n\n float dist = length(v_pos - gl_FragCoord.xy);\n float alpha = 1.0 - smoothstep(0.0, 1.0, dist);\n gl_FragColor = outline_color * (alpha * opacity);\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"attribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform vec2 u_world;\n\nvarying vec2 v_pos;\n\n#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 outline_color\n #pragma mapbox: initialize lowp float opacity\n\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;\n}\n"},fillOutlinePattern:{fragmentSource:"uniform vec2 u_pattern_tl_a;\nuniform vec2 u_pattern_br_a;\nuniform vec2 u_pattern_tl_b;\nuniform vec2 u_pattern_br_b;\nuniform vec2 u_texsize;\nuniform float u_mix;\n\nuniform sampler2D u_image;\n\nvarying vec2 v_pos_a;\nvarying vec2 v_pos_b;\nvarying vec2 v_pos;\n\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize lowp float opacity\n\n vec2 imagecoord = mod(v_pos_a, 1.0);\n vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);\n vec4 color1 = texture2D(u_image, pos);\n\n vec2 imagecoord_b = mod(v_pos_b, 1.0);\n vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b);\n vec4 color2 = texture2D(u_image, pos2);\n\n // find distance to outline for alpha interpolation\n\n float dist = length(v_pos - gl_FragCoord.xy);\n float alpha = 1.0 - smoothstep(0.0, 1.0, dist);\n\n\n gl_FragColor = mix(color1, color2, u_mix) * alpha * opacity;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\nuniform vec2 u_world;\nuniform vec2 u_pattern_size_a;\nuniform vec2 u_pattern_size_b;\nuniform vec2 u_pixel_coord_upper;\nuniform vec2 u_pixel_coord_lower;\nuniform float u_scale_a;\nuniform float u_scale_b;\nuniform float u_tile_units_to_pixels;\n\nattribute vec2 a_pos;\n\nvarying vec2 v_pos_a;\nvarying vec2 v_pos_b;\nvarying vec2 v_pos;\n\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize lowp float opacity\n\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n\n v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos);\n v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, a_pos);\n\n v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;\n}\n"},fillPattern:{fragmentSource:"uniform vec2 u_pattern_tl_a;\nuniform vec2 u_pattern_br_a;\nuniform vec2 u_pattern_tl_b;\nuniform vec2 u_pattern_br_b;\nuniform vec2 u_texsize;\nuniform float u_mix;\n\nuniform sampler2D u_image;\n\nvarying vec2 v_pos_a;\nvarying vec2 v_pos_b;\n\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize lowp float opacity\n\n vec2 imagecoord = mod(v_pos_a, 1.0);\n vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);\n vec4 color1 = texture2D(u_image, pos);\n\n vec2 imagecoord_b = mod(v_pos_b, 1.0);\n vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b);\n vec4 color2 = texture2D(u_image, pos2);\n\n gl_FragColor = mix(color1, color2, u_mix) * opacity;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\nuniform vec2 u_pattern_size_a;\nuniform vec2 u_pattern_size_b;\nuniform vec2 u_pixel_coord_upper;\nuniform vec2 u_pixel_coord_lower;\nuniform float u_scale_a;\nuniform float u_scale_b;\nuniform float u_tile_units_to_pixels;\n\nattribute vec2 a_pos;\n\nvarying vec2 v_pos_a;\nvarying vec2 v_pos_b;\n\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize lowp float opacity\n\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n\n v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos);\n v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, a_pos);\n}\n"},fillExtrusion:{fragmentSource:"varying vec4 v_color;\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n#pragma mapbox: define highp vec4 color\n\nvoid main() {\n #pragma mapbox: initialize lowp float base\n #pragma mapbox: initialize lowp float height\n #pragma mapbox: initialize highp vec4 color\n\n gl_FragColor = v_color;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\nuniform vec3 u_lightcolor;\nuniform lowp vec3 u_lightpos;\nuniform lowp float u_lightintensity;\n\nattribute vec2 a_pos;\nattribute vec4 a_normal_ed;\n\nvarying vec4 v_color;\n\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n\n#pragma mapbox: define highp vec4 color\n\nvoid main() {\n #pragma mapbox: initialize lowp float base\n #pragma mapbox: initialize lowp float height\n #pragma mapbox: initialize highp vec4 color\n\n vec3 normal = a_normal_ed.xyz;\n\n base = max(0.0, base);\n height = max(0.0, height);\n\n float t = mod(normal.x, 2.0);\n\n gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1);\n\n // Relative luminance (how dark/bright is the surface color?)\n float colorvalue = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;\n\n v_color = vec4(0.0, 0.0, 0.0, 1.0);\n\n // Add slight ambient lighting so no extrusions are totally black\n vec4 ambientlight = vec4(0.03, 0.03, 0.03, 1.0);\n color += ambientlight;\n\n // Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray\n float directional = clamp(dot(normal / 16384.0, u_lightpos), 0.0, 1.0);\n\n // Adjust directional so that\n // the range of values for highlight/shading is narrower\n // with lower light intensity\n // and with lighter/brighter surface colors\n directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional);\n\n // Add gradient along z axis of side surfaces\n if (normal.y != 0.0) {\n directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);\n }\n\n // Assign final color based on surface + ambient light color, diffuse light directional, and light color\n // with lower bounds adjusted to hue of light\n // so that shading is tinted with the complementary (opposite) color to the light color\n v_color.r += clamp(color.r * directional * u_lightcolor.r, mix(0.0, 0.3, 1.0 - u_lightcolor.r), 1.0);\n v_color.g += clamp(color.g * directional * u_lightcolor.g, mix(0.0, 0.3, 1.0 - u_lightcolor.g), 1.0);\n v_color.b += clamp(color.b * directional * u_lightcolor.b, mix(0.0, 0.3, 1.0 - u_lightcolor.b), 1.0);\n}\n"},fillExtrusionPattern:{fragmentSource:"uniform vec2 u_pattern_tl_a;\nuniform vec2 u_pattern_br_a;\nuniform vec2 u_pattern_tl_b;\nuniform vec2 u_pattern_br_b;\nuniform vec2 u_texsize;\nuniform float u_mix;\n\nuniform sampler2D u_image;\n\nvarying vec2 v_pos_a;\nvarying vec2 v_pos_b;\nvarying vec4 v_lighting;\n\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n\nvoid main() {\n #pragma mapbox: initialize lowp float base\n #pragma mapbox: initialize lowp float height\n\n vec2 imagecoord = mod(v_pos_a, 1.0);\n vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);\n vec4 color1 = texture2D(u_image, pos);\n\n vec2 imagecoord_b = mod(v_pos_b, 1.0);\n vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b);\n vec4 color2 = texture2D(u_image, pos2);\n\n vec4 mixedColor = mix(color1, color2, u_mix);\n\n gl_FragColor = mixedColor * v_lighting;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\nuniform vec2 u_pattern_size_a;\nuniform vec2 u_pattern_size_b;\nuniform vec2 u_pixel_coord_upper;\nuniform vec2 u_pixel_coord_lower;\nuniform float u_scale_a;\nuniform float u_scale_b;\nuniform float u_tile_units_to_pixels;\nuniform float u_height_factor;\n\nuniform vec3 u_lightcolor;\nuniform lowp vec3 u_lightpos;\nuniform lowp float u_lightintensity;\n\nattribute vec2 a_pos;\nattribute vec4 a_normal_ed;\n\nvarying vec2 v_pos_a;\nvarying vec2 v_pos_b;\nvarying vec4 v_lighting;\nvarying float v_directional;\n\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n\nvoid main() {\n #pragma mapbox: initialize lowp float base\n #pragma mapbox: initialize lowp float height\n\n vec3 normal = a_normal_ed.xyz;\n float edgedistance = a_normal_ed.w;\n\n base = max(0.0, base);\n height = max(0.0, height);\n\n float t = mod(normal.x, 2.0);\n float z = t > 0.0 ? height : base;\n\n gl_Position = u_matrix * vec4(a_pos, z, 1);\n\n vec2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0\n ? a_pos // extrusion top\n : vec2(edgedistance, z * u_height_factor); // extrusion side\n\n v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, pos);\n v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, pos);\n\n v_lighting = vec4(0.0, 0.0, 0.0, 1.0);\n float directional = clamp(dot(normal / 16383.0, u_lightpos), 0.0, 1.0);\n directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional);\n\n if (normal.y != 0.0) {\n directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);\n }\n\n v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0));\n}\n"},extrusionTexture:{fragmentSource:"uniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\n\nvoid main() {\n gl_FragColor = texture2D(u_image, v_pos) * u_opacity;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(0.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\nuniform vec2 u_world;\nattribute vec2 a_pos;\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos * u_world, 0, 1);\n\n v_pos.x = a_pos.x;\n v_pos.y = 1.0 - a_pos.y;\n}\n"},hillshadePrepare:{fragmentSource:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform sampler2D u_image;\nvarying vec2 v_pos;\nuniform vec2 u_dimension;\nuniform float u_zoom;\n\nfloat getElevation(vec2 coord, float bias) {\n // Convert encoded elevation value to meters\n vec4 data = texture2D(u_image, coord) * 255.0;\n return (data.r + data.g * 256.0 + data.b * 256.0 * 256.0) / 4.0;\n}\n\nvoid main() {\n vec2 epsilon = 1.0 / u_dimension;\n\n // queried pixels:\n // +-----------+\n // | | | |\n // | a | b | c |\n // | | | |\n // +-----------+\n // | | | |\n // | d | e | f |\n // | | | |\n // +-----------+\n // | | | |\n // | g | h | i |\n // | | | |\n // +-----------+\n\n float a = getElevation(v_pos + vec2(-epsilon.x, -epsilon.y), 0.0);\n float b = getElevation(v_pos + vec2(0, -epsilon.y), 0.0);\n float c = getElevation(v_pos + vec2(epsilon.x, -epsilon.y), 0.0);\n float d = getElevation(v_pos + vec2(-epsilon.x, 0), 0.0);\n float e = getElevation(v_pos, 0.0);\n float f = getElevation(v_pos + vec2(epsilon.x, 0), 0.0);\n float g = getElevation(v_pos + vec2(-epsilon.x, epsilon.y), 0.0);\n float h = getElevation(v_pos + vec2(0, epsilon.y), 0.0);\n float i = getElevation(v_pos + vec2(epsilon.x, epsilon.y), 0.0);\n\n // here we divide the x and y slopes by 8 * pixel size\n // where pixel size (aka meters/pixel) is:\n // circumference of the world / (pixels per tile * number of tiles)\n // which is equivalent to: 8 * 40075016.6855785 / (512 * pow(2, u_zoom))\n // which can be reduced to: pow(2, 19.25619978527 - u_zoom)\n // we want to vertically exaggerate the hillshading though, because otherwise\n // it is barely noticeable at low zooms. to do this, we multiply this by some\n // scale factor pow(2, (u_zoom - 14) * a) where a is an arbitrary value and 14 is the\n // maxzoom of the tile source. here we use a=0.3 which works out to the\n // expression below. see nickidlugash's awesome breakdown for more info\n // https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556\n float exaggeration = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;\n\n vec2 deriv = vec2(\n (c + f + f + i) - (a + d + d + g),\n (g + h + h + i) - (a + b + b + c)\n ) / pow(2.0, (u_zoom - 14.0) * exaggeration + 19.2562 - u_zoom);\n\n gl_FragColor = clamp(vec4(\n deriv.x / 2.0 + 0.5,\n deriv.y / 2.0 + 0.5,\n 1.0,\n 1.0), 0.0, 1.0);\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\n\nattribute vec2 a_pos;\nattribute vec2 a_texture_pos;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n v_pos = (a_texture_pos / 8192.0) / 2.0 + 0.25;\n}\n"},hillshade:{fragmentSource:"uniform sampler2D u_image;\nvarying vec2 v_pos;\n\nuniform vec2 u_latrange;\nuniform vec2 u_light;\nuniform vec4 u_shadow;\nuniform vec4 u_highlight;\nuniform vec4 u_accent;\n\n#define PI 3.141592653589793\n\nvoid main() {\n vec4 pixel = texture2D(u_image, v_pos);\n\n vec2 deriv = ((pixel.rg * 2.0) - 1.0);\n\n // We divide the slope by a scale factor based on the cosin of the pixel's approximate latitude\n // to account for mercator projection distortion. see #4807 for details\n float scaleFactor = cos(radians((u_latrange[0] - u_latrange[1]) * (1.0 - v_pos.y) + u_latrange[1]));\n // We also multiply the slope by an arbitrary z-factor of 1.25\n float slope = atan(1.25 * length(deriv) / scaleFactor);\n float aspect = deriv.x != 0.0 ? atan(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);\n\n float intensity = u_light.x;\n // We add PI to make this property match the global light object, which adds PI/2 to the light's azimuthal\n // position property to account for 0deg corresponding to north/the top of the viewport in the style spec\n // and the original shader was written to accept (-illuminationDirection - 90) as the azimuthal.\n float azimuth = u_light.y + PI;\n\n // We scale the slope exponentially based on intensity, using a calculation similar to\n // the exponential interpolation function in the style spec:\n // https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/expression/definitions/interpolate.js#L217-L228\n // so that higher intensity values create more opaque hillshading.\n float base = 1.875 - intensity * 1.75;\n float maxValue = 0.5 * PI;\n float scaledSlope = intensity != 0.5 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;\n\n // The accent color is calculated with the cosine of the slope while the shade color is calculated with the sine\n // so that the accent color's rate of change eases in while the shade color's eases out.\n float accent = cos(scaledSlope);\n // We multiply both the accent and shade color by a clamped intensity value\n // so that intensities >= 0.5 do not additionally affect the color values\n // while intensity values < 0.5 make the overall color more transparent.\n vec4 accent_color = (1.0 - accent) * u_accent * clamp(intensity * 2.0, 0.0, 1.0);\n float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);\n vec4 shade_color = mix(u_shadow, u_highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);\n gl_FragColor = accent_color * (1.0 - shade_color.a) + shade_color;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\n\nattribute vec2 a_pos;\nattribute vec2 a_texture_pos;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n v_pos = a_texture_pos / 8192.0;\n}\n"},line:{fragmentSource:"#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n\nvarying vec2 v_width2;\nvarying vec2 v_normal;\nvarying float v_gamma_scale;\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 color\n #pragma mapbox: initialize lowp float blur\n #pragma mapbox: initialize lowp float opacity\n\n // Calculate the distance of the pixel from the line in pixels.\n float dist = length(v_normal) * v_width2.s;\n\n // Calculate the antialiasing fade factor. This is either when fading in\n // the line in case of an offset line (v_width2.t) or when fading out\n // (v_width2.s)\n float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale;\n float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0);\n\n gl_FragColor = color * (alpha * opacity);\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"\n\n// the distance over which the line edge fades out.\n// Retina devices need a smaller distance to avoid aliasing.\n#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0\n\n// floor(127 / 2) == 63.0\n// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is\n// stored in a byte (-128..127). we scale regular normals up to length 63, but\n// there are also \"special\" normals that have a bigger length (of up to 126 in\n// this case).\n// #define scale 63.0\n#define scale 0.015873016\n\nattribute vec4 a_pos_normal;\nattribute vec4 a_data;\n\nuniform mat4 u_matrix;\nuniform mediump float u_ratio;\nuniform vec2 u_gl_units_to_pixels;\n\nvarying vec2 v_normal;\nvarying vec2 v_width2;\nvarying float v_gamma_scale;\n\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 color\n #pragma mapbox: initialize lowp float blur\n #pragma mapbox: initialize lowp float opacity\n #pragma mapbox: initialize mediump float gapwidth\n #pragma mapbox: initialize lowp float offset\n #pragma mapbox: initialize mediump float width\n\n vec2 a_extrude = a_data.xy - 128.0;\n float a_direction = mod(a_data.z, 4.0) - 1.0;\n\n vec2 pos = a_pos_normal.xy;\n\n // x is 1 if it's a round cap, 0 otherwise\n // y is 1 if the normal points up, and -1 if it points down\n mediump vec2 normal = a_pos_normal.zw;\n v_normal = normal;\n\n // these transformations used to be applied in the JS and native code bases.\n // moved them into the shader for clarity and simplicity.\n gapwidth = gapwidth / 2.0;\n float halfwidth = width / 2.0;\n offset = -1.0 * offset;\n\n float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);\n float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;\n\n // Scale the extrusion vector down to a normal and then up by the line width\n // of this vertex.\n mediump vec2 dist = outset * a_extrude * scale;\n\n // Calculate the offset when drawing a line that is to the side of the actual line.\n // We do this by creating a vector that points towards the extrude, but rotate\n // it when we're drawing round end points (a_direction = -1 or 1) since their\n // extrude vector points in another direction.\n mediump float u = 0.5 * a_direction;\n mediump float t = 1.0 - abs(u);\n mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);\n\n vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);\n gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;\n\n // calculate how much the perspective view squishes or stretches the extrude\n float extrude_length_without_perspective = length(dist);\n float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels);\n v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective;\n\n v_width2 = vec2(outset, inset);\n}\n"},linePattern:{fragmentSource:"uniform vec2 u_pattern_size_a;\nuniform vec2 u_pattern_size_b;\nuniform vec2 u_pattern_tl_a;\nuniform vec2 u_pattern_br_a;\nuniform vec2 u_pattern_tl_b;\nuniform vec2 u_pattern_br_b;\nuniform vec2 u_texsize;\nuniform float u_fade;\n\nuniform sampler2D u_image;\n\nvarying vec2 v_normal;\nvarying vec2 v_width2;\nvarying float v_linesofar;\nvarying float v_gamma_scale;\n\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n\nvoid main() {\n #pragma mapbox: initialize lowp float blur\n #pragma mapbox: initialize lowp float opacity\n\n // Calculate the distance of the pixel from the line in pixels.\n float dist = length(v_normal) * v_width2.s;\n\n // Calculate the antialiasing fade factor. This is either when fading in\n // the line in case of an offset line (v_width2.t) or when fading out\n // (v_width2.s)\n float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale;\n float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0);\n\n float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0);\n float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0);\n float y_a = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_a.y);\n float y_b = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_b.y);\n vec2 pos_a = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, vec2(x_a, y_a));\n vec2 pos_b = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, vec2(x_b, y_b));\n\n vec4 color = mix(texture2D(u_image, pos_a), texture2D(u_image, pos_b), u_fade);\n\n gl_FragColor = color * alpha * opacity;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"// floor(127 / 2) == 63.0\n// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is\n// stored in a byte (-128..127). we scale regular normals up to length 63, but\n// there are also \"special\" normals that have a bigger length (of up to 126 in\n// this case).\n// #define scale 63.0\n#define scale 0.015873016\n\n// We scale the distance before adding it to the buffers so that we can store\n// long distances for long segments. Use this value to unscale the distance.\n#define LINE_DISTANCE_SCALE 2.0\n\n// the distance over which the line edge fades out.\n// Retina devices need a smaller distance to avoid aliasing.\n#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0\n\nattribute vec4 a_pos_normal;\nattribute vec4 a_data;\n\nuniform mat4 u_matrix;\nuniform mediump float u_ratio;\nuniform vec2 u_gl_units_to_pixels;\n\nvarying vec2 v_normal;\nvarying vec2 v_width2;\nvarying float v_linesofar;\nvarying float v_gamma_scale;\n\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define mediump float width\n\nvoid main() {\n #pragma mapbox: initialize lowp float blur\n #pragma mapbox: initialize lowp float opacity\n #pragma mapbox: initialize lowp float offset\n #pragma mapbox: initialize mediump float gapwidth\n #pragma mapbox: initialize mediump float width\n\n vec2 a_extrude = a_data.xy - 128.0;\n float a_direction = mod(a_data.z, 4.0) - 1.0;\n float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;\n\n vec2 pos = a_pos_normal.xy;\n\n // x is 1 if it's a round cap, 0 otherwise\n // y is 1 if the normal points up, and -1 if it points down\n mediump vec2 normal = a_pos_normal.zw;\n v_normal = normal;\n\n // these transformations used to be applied in the JS and native code bases.\n // moved them into the shader for clarity and simplicity.\n gapwidth = gapwidth / 2.0;\n float halfwidth = width / 2.0;\n offset = -1.0 * offset;\n\n float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);\n float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;\n\n // Scale the extrusion vector down to a normal and then up by the line width\n // of this vertex.\n mediump vec2 dist = outset * a_extrude * scale;\n\n // Calculate the offset when drawing a line that is to the side of the actual line.\n // We do this by creating a vector that points towards the extrude, but rotate\n // it when we're drawing round end points (a_direction = -1 or 1) since their\n // extrude vector points in another direction.\n mediump float u = 0.5 * a_direction;\n mediump float t = 1.0 - abs(u);\n mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);\n\n vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);\n gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;\n\n // calculate how much the perspective view squishes or stretches the extrude\n float extrude_length_without_perspective = length(dist);\n float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels);\n v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective;\n\n v_linesofar = a_linesofar;\n v_width2 = vec2(outset, inset);\n}\n"},lineSDF:{fragmentSource:"\nuniform sampler2D u_image;\nuniform float u_sdfgamma;\nuniform float u_mix;\n\nvarying vec2 v_normal;\nvarying vec2 v_width2;\nvarying vec2 v_tex_a;\nvarying vec2 v_tex_b;\nvarying float v_gamma_scale;\n\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 color\n #pragma mapbox: initialize lowp float blur\n #pragma mapbox: initialize lowp float opacity\n #pragma mapbox: initialize mediump float width\n #pragma mapbox: initialize lowp float floorwidth\n\n // Calculate the distance of the pixel from the line in pixels.\n float dist = length(v_normal) * v_width2.s;\n\n // Calculate the antialiasing fade factor. This is either when fading in\n // the line in case of an offset line (v_width2.t) or when fading out\n // (v_width2.s)\n float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale;\n float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0);\n\n float sdfdist_a = texture2D(u_image, v_tex_a).a;\n float sdfdist_b = texture2D(u_image, v_tex_b).a;\n float sdfdist = mix(sdfdist_a, sdfdist_b, u_mix);\n alpha *= smoothstep(0.5 - u_sdfgamma / floorwidth, 0.5 + u_sdfgamma / floorwidth, sdfdist);\n\n gl_FragColor = color * (alpha * opacity);\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"// floor(127 / 2) == 63.0\n// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is\n// stored in a byte (-128..127). we scale regular normals up to length 63, but\n// there are also \"special\" normals that have a bigger length (of up to 126 in\n// this case).\n// #define scale 63.0\n#define scale 0.015873016\n\n// We scale the distance before adding it to the buffers so that we can store\n// long distances for long segments. Use this value to unscale the distance.\n#define LINE_DISTANCE_SCALE 2.0\n\n// the distance over which the line edge fades out.\n// Retina devices need a smaller distance to avoid aliasing.\n#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0\n\nattribute vec4 a_pos_normal;\nattribute vec4 a_data;\n\nuniform mat4 u_matrix;\nuniform mediump float u_ratio;\nuniform vec2 u_patternscale_a;\nuniform float u_tex_y_a;\nuniform vec2 u_patternscale_b;\nuniform float u_tex_y_b;\nuniform vec2 u_gl_units_to_pixels;\n\nvarying vec2 v_normal;\nvarying vec2 v_width2;\nvarying vec2 v_tex_a;\nvarying vec2 v_tex_b;\nvarying float v_gamma_scale;\n\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 color\n #pragma mapbox: initialize lowp float blur\n #pragma mapbox: initialize lowp float opacity\n #pragma mapbox: initialize mediump float gapwidth\n #pragma mapbox: initialize lowp float offset\n #pragma mapbox: initialize mediump float width\n #pragma mapbox: initialize lowp float floorwidth\n\n vec2 a_extrude = a_data.xy - 128.0;\n float a_direction = mod(a_data.z, 4.0) - 1.0;\n float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;\n\n vec2 pos = a_pos_normal.xy;\n\n // x is 1 if it's a round cap, 0 otherwise\n // y is 1 if the normal points up, and -1 if it points down\n mediump vec2 normal = a_pos_normal.zw;\n v_normal = normal;\n\n // these transformations used to be applied in the JS and native code bases.\n // moved them into the shader for clarity and simplicity.\n gapwidth = gapwidth / 2.0;\n float halfwidth = width / 2.0;\n offset = -1.0 * offset;\n\n float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);\n float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;\n\n // Scale the extrusion vector down to a normal and then up by the line width\n // of this vertex.\n mediump vec2 dist =outset * a_extrude * scale;\n\n // Calculate the offset when drawing a line that is to the side of the actual line.\n // We do this by creating a vector that points towards the extrude, but rotate\n // it when we're drawing round end points (a_direction = -1 or 1) since their\n // extrude vector points in another direction.\n mediump float u = 0.5 * a_direction;\n mediump float t = 1.0 - abs(u);\n mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);\n\n vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);\n gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;\n\n // calculate how much the perspective view squishes or stretches the extrude\n float extrude_length_without_perspective = length(dist);\n float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels);\n v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective;\n\n v_tex_a = vec2(a_linesofar * u_patternscale_a.x / floorwidth, normal.y * u_patternscale_a.y + u_tex_y_a);\n v_tex_b = vec2(a_linesofar * u_patternscale_b.x / floorwidth, normal.y * u_patternscale_b.y + u_tex_y_b);\n\n v_width2 = vec2(outset, inset);\n}\n"},raster:{fragmentSource:"uniform float u_fade_t;\nuniform float u_opacity;\nuniform sampler2D u_image0;\nuniform sampler2D u_image1;\nvarying vec2 v_pos0;\nvarying vec2 v_pos1;\n\nuniform float u_brightness_low;\nuniform float u_brightness_high;\n\nuniform float u_saturation_factor;\nuniform float u_contrast_factor;\nuniform vec3 u_spin_weights;\n\nvoid main() {\n\n // read and cross-fade colors from the main and parent tiles\n vec4 color0 = texture2D(u_image0, v_pos0);\n vec4 color1 = texture2D(u_image1, v_pos1);\n if (color0.a > 0.0) {\n color0.rgb = color0.rgb / color0.a;\n }\n if (color1.a > 0.0) {\n color1.rgb = color1.rgb / color1.a;\n }\n vec4 color = mix(color0, color1, u_fade_t);\n color.a *= u_opacity;\n vec3 rgb = color.rgb;\n\n // spin\n rgb = vec3(\n dot(rgb, u_spin_weights.xyz),\n dot(rgb, u_spin_weights.zxy),\n dot(rgb, u_spin_weights.yzx));\n\n // saturation\n float average = (color.r + color.g + color.b) / 3.0;\n rgb += (average - rgb) * u_saturation_factor;\n\n // contrast\n rgb = (rgb - 0.5) * u_contrast_factor + 0.5;\n\n // brightness\n vec3 u_high_vec = vec3(u_brightness_low, u_brightness_low, u_brightness_low);\n vec3 u_low_vec = vec3(u_brightness_high, u_brightness_high, u_brightness_high);\n\n gl_FragColor = vec4(mix(u_high_vec, u_low_vec, rgb) * color.a, color.a);\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"uniform mat4 u_matrix;\nuniform vec2 u_tl_parent;\nuniform float u_scale_parent;\nuniform float u_buffer_scale;\n\nattribute vec2 a_pos;\nattribute vec2 a_texture_pos;\n\nvarying vec2 v_pos0;\nvarying vec2 v_pos1;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n // We are using Int16 for texture position coordinates to give us enough precision for\n // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer\n // as an arbitrarily high number to preserve adequate precision when rendering.\n // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates,\n // so math for modifying either is consistent.\n v_pos0 = (((a_texture_pos / 8192.0) - 0.5) / u_buffer_scale ) + 0.5;\n v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent;\n}\n"},symbolIcon:{fragmentSource:"uniform sampler2D u_texture;\n\n#pragma mapbox: define lowp float opacity\n\nvarying vec2 v_tex;\nvarying float v_fade_opacity;\n\nvoid main() {\n #pragma mapbox: initialize lowp float opacity\n\n lowp float alpha = opacity * v_fade_opacity;\n gl_FragColor = texture2D(u_texture, v_tex) * alpha;\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"const float PI = 3.141592653589793;\n\nattribute vec4 a_pos_offset;\nattribute vec4 a_data;\nattribute vec3 a_projected_pos;\nattribute float a_fade_opacity;\n\nuniform bool u_is_size_zoom_constant;\nuniform bool u_is_size_feature_constant;\nuniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function\nuniform highp float u_size; // used when size is both zoom and feature constant\nuniform highp float u_camera_to_center_distance;\nuniform highp float u_pitch;\nuniform bool u_rotate_symbol;\nuniform highp float u_aspect_ratio;\nuniform float u_fade_change;\n\n#pragma mapbox: define lowp float opacity\n\nuniform mat4 u_matrix;\nuniform mat4 u_label_plane_matrix;\nuniform mat4 u_gl_coord_matrix;\n\nuniform bool u_is_text;\nuniform bool u_pitch_with_map;\n\nuniform vec2 u_texsize;\n\nvarying vec2 v_tex;\nvarying float v_fade_opacity;\n\nvoid main() {\n #pragma mapbox: initialize lowp float opacity\n\n vec2 a_pos = a_pos_offset.xy;\n vec2 a_offset = a_pos_offset.zw;\n\n vec2 a_tex = a_data.xy;\n vec2 a_size = a_data.zw;\n\n highp float segment_angle = -a_projected_pos[2];\n\n float size;\n if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {\n size = mix(a_size[0], a_size[1], u_size_t) / 10.0;\n } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {\n size = a_size[0] / 10.0;\n } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {\n size = u_size;\n } else {\n size = u_size;\n }\n\n vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1);\n highp float camera_to_anchor_distance = projectedPoint.w;\n // See comments in symbol_sdf.vertex\n highp float distance_ratio = u_pitch_with_map ?\n camera_to_anchor_distance / u_camera_to_center_distance :\n u_camera_to_center_distance / camera_to_anchor_distance;\n highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;\n\n size *= perspective_ratio;\n\n float fontScale = u_is_text ? size / 24.0 : size;\n\n highp float symbol_rotation = 0.0;\n if (u_rotate_symbol) {\n // See comments in symbol_sdf.vertex\n vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1);\n\n vec2 a = projectedPoint.xy / projectedPoint.w;\n vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w;\n\n symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x);\n }\n\n highp float angle_sin = sin(segment_angle + symbol_rotation);\n highp float angle_cos = cos(segment_angle + symbol_rotation);\n mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);\n\n vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);\n gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);\n\n v_tex = a_tex / u_texsize;\n vec2 fade_opacity = unpack_opacity(a_fade_opacity);\n float fade_change = fade_opacity[1] > 0.5 ? u_fade_change : -u_fade_change;\n v_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change));\n}\n"},symbolSDF:{fragmentSource:"#define SDF_PX 8.0\n#define EDGE_GAMMA 0.105/DEVICE_PIXEL_RATIO\n\nuniform bool u_is_halo;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\n\nuniform sampler2D u_texture;\nuniform highp float u_gamma_scale;\nuniform bool u_is_text;\n\nvarying vec2 v_data0;\nvarying vec3 v_data1;\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 fill_color\n #pragma mapbox: initialize highp vec4 halo_color\n #pragma mapbox: initialize lowp float opacity\n #pragma mapbox: initialize lowp float halo_width\n #pragma mapbox: initialize lowp float halo_blur\n\n vec2 tex = v_data0.xy;\n float gamma_scale = v_data1.x;\n float size = v_data1.y;\n float fade_opacity = v_data1[2];\n\n float fontScale = u_is_text ? size / 24.0 : size;\n\n lowp vec4 color = fill_color;\n highp float gamma = EDGE_GAMMA / (fontScale * u_gamma_scale);\n lowp float buff = (256.0 - 64.0) / 256.0;\n if (u_is_halo) {\n color = halo_color;\n gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale);\n buff = (6.0 - halo_width / fontScale) / SDF_PX;\n }\n\n lowp float dist = texture2D(u_texture, tex).a;\n highp float gamma_scaled = gamma * gamma_scale;\n highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist);\n\n gl_FragColor = color * (alpha * opacity * fade_opacity);\n\n#ifdef OVERDRAW_INSPECTOR\n gl_FragColor = vec4(1.0);\n#endif\n}\n",vertexSource:"const float PI = 3.141592653589793;\n\nattribute vec4 a_pos_offset;\nattribute vec4 a_data;\nattribute vec3 a_projected_pos;\nattribute float a_fade_opacity;\n\n// contents of a_size vary based on the type of property value\n// used for {text,icon}-size.\n// For constants, a_size is disabled.\n// For source functions, we bind only one value per vertex: the value of {text,icon}-size evaluated for the current feature.\n// For composite functions:\n// [ text-size(lowerZoomStop, feature),\n// text-size(upperZoomStop, feature) ]\nuniform bool u_is_size_zoom_constant;\nuniform bool u_is_size_feature_constant;\nuniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function\nuniform highp float u_size; // used when size is both zoom and feature constant\n\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\n\nuniform mat4 u_matrix;\nuniform mat4 u_label_plane_matrix;\nuniform mat4 u_gl_coord_matrix;\n\nuniform bool u_is_text;\nuniform bool u_pitch_with_map;\nuniform highp float u_pitch;\nuniform bool u_rotate_symbol;\nuniform highp float u_aspect_ratio;\nuniform highp float u_camera_to_center_distance;\nuniform float u_fade_change;\n\nuniform vec2 u_texsize;\n\nvarying vec2 v_data0;\nvarying vec3 v_data1;\n\nvoid main() {\n #pragma mapbox: initialize highp vec4 fill_color\n #pragma mapbox: initialize highp vec4 halo_color\n #pragma mapbox: initialize lowp float opacity\n #pragma mapbox: initialize lowp float halo_width\n #pragma mapbox: initialize lowp float halo_blur\n\n vec2 a_pos = a_pos_offset.xy;\n vec2 a_offset = a_pos_offset.zw;\n\n vec2 a_tex = a_data.xy;\n vec2 a_size = a_data.zw;\n\n highp float segment_angle = -a_projected_pos[2];\n float size;\n\n if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {\n size = mix(a_size[0], a_size[1], u_size_t) / 10.0;\n } else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {\n size = a_size[0] / 10.0;\n } else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {\n size = u_size;\n } else {\n size = u_size;\n }\n\n vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1);\n highp float camera_to_anchor_distance = projectedPoint.w;\n // If the label is pitched with the map, layout is done in pitched space,\n // which makes labels in the distance smaller relative to viewport space.\n // We counteract part of that effect by multiplying by the perspective ratio.\n // If the label isn't pitched with the map, we do layout in viewport space,\n // which makes labels in the distance larger relative to the features around\n // them. We counteract part of that effect by dividing by the perspective ratio.\n highp float distance_ratio = u_pitch_with_map ?\n camera_to_anchor_distance / u_camera_to_center_distance :\n u_camera_to_center_distance / camera_to_anchor_distance;\n highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;\n\n size *= perspective_ratio;\n\n float fontScale = u_is_text ? size / 24.0 : size;\n\n highp float symbol_rotation = 0.0;\n if (u_rotate_symbol) {\n // Point labels with 'rotation-alignment: map' are horizontal with respect to tile units\n // To figure out that angle in projected space, we draw a short horizontal line in tile\n // space, project it, and measure its angle in projected space.\n vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1);\n\n vec2 a = projectedPoint.xy / projectedPoint.w;\n vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w;\n\n symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x);\n }\n\n highp float angle_sin = sin(segment_angle + symbol_rotation);\n highp float angle_cos = cos(segment_angle + symbol_rotation);\n mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);\n\n vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);\n gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);\n float gamma_scale = gl_Position.w;\n\n vec2 tex = a_tex / u_texsize;\n vec2 fade_opacity = unpack_opacity(a_fade_opacity);\n float fade_change = fade_opacity[1] > 0.5 ? u_fade_change : -u_fade_change;\n float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change));\n\n v_data0 = vec2(tex.x, tex.y);\n v_data1 = vec3(gamma_scale, size, interpolated_fade_opacity);\n}\n"}},i=/#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g,a=function(t){var e=n[t],r={};e.fragmentSource=e.fragmentSource.replace(i,function(t,e,n,i,a){return r[a]=!0,"define"===e?"\n#ifndef HAS_UNIFORM_u_"+a+"\nvarying "+n+" "+i+" "+a+";\n#else\nuniform "+n+" "+i+" u_"+a+";\n#endif\n":"\n#ifdef HAS_UNIFORM_u_"+a+"\n "+n+" "+i+" "+a+" = u_"+a+";\n#endif\n"}),e.vertexSource=e.vertexSource.replace(i,function(t,e,n,i,a){var o="float"===i?"vec2":"vec4";return r[a]?"define"===e?"\n#ifndef HAS_UNIFORM_u_"+a+"\nuniform lowp float a_"+a+"_t;\nattribute "+n+" "+o+" a_"+a+";\nvarying "+n+" "+i+" "+a+";\n#else\nuniform "+n+" "+i+" u_"+a+";\n#endif\n":"\n#ifndef HAS_UNIFORM_u_"+a+"\n "+a+" = unpack_mix_"+o+"(a_"+a+", a_"+a+"_t);\n#else\n "+n+" "+i+" "+a+" = u_"+a+";\n#endif\n":"define"===e?"\n#ifndef HAS_UNIFORM_u_"+a+"\nuniform lowp float a_"+a+"_t;\nattribute "+n+" "+o+" a_"+a+";\n#else\nuniform "+n+" "+i+" u_"+a+";\n#endif\n":"\n#ifndef HAS_UNIFORM_u_"+a+"\n "+n+" "+i+" "+a+" = unpack_mix_"+o+"(a_"+a+", a_"+a+"_t);\n#else\n "+n+" "+i+" "+a+" = u_"+a+";\n#endif\n"})};for(var o in n)a(o);e.exports=n},{}],98:[function(t,e,r){var n=t("./image_source"),i=t("../util/window"),a=t("../data/raster_bounds_attributes"),o=t("../render/vertex_array_object"),s=t("../render/texture"),l=function(t){function e(e,r,n,i){t.call(this,e,r,n,i),this.options=r,this.animate=void 0===r.animate||r.animate}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.load=function(){this.canvas=this.canvas||i.document.getElementById(this.options.canvas),this.width=this.canvas.width,this.height=this.canvas.height,this._hasInvalidDimensions()?this.fire("error",new Error("Canvas dimensions cannot be less than or equal to zero.")):(this.play=function(){this._playing=!0,this.map._rerender()},this.pause=function(){this._playing=!1},this._finishLoading())},e.prototype.getCanvas=function(){return this.canvas},e.prototype.onAdd=function(t){this.map=t,this.load(),this.canvas&&this.animate&&this.play()},e.prototype.onRemove=function(){this.pause()},e.prototype.prepare=function(){var t=this,e=!1;if(this.canvas.width!==this.width&&(this.width=this.canvas.width,e=!0),this.canvas.height!==this.height&&(this.height=this.canvas.height,e=!0),!this._hasInvalidDimensions()&&0!==Object.keys(this.tiles).length){var r=this.map.painter.context,n=r.gl;for(var i in this.boundsBuffer||(this.boundsBuffer=r.createVertexBuffer(this._boundsArray,a.members)),this.boundsVAO||(this.boundsVAO=new o),this.texture?e?this.texture.update(this.canvas):this._playing&&(this.texture.bind(n.LINEAR,n.CLAMP_TO_EDGE),n.texSubImage2D(n.TEXTURE_2D,0,0,0,n.RGBA,n.UNSIGNED_BYTE,this.canvas)):(this.texture=new s(r,this.canvas,n.RGBA),this.texture.bind(n.LINEAR,n.CLAMP_TO_EDGE)),t.tiles){var l=t.tiles[i];"loaded"!==l.state&&(l.state="loaded",l.texture=t.texture)}}},e.prototype.serialize=function(){return{type:"canvas",canvas:this.canvas,coordinates:this.coordinates}},e.prototype.hasTransition=function(){return this._playing},e.prototype._hasInvalidDimensions=function(){for(var t=0,e=[this.canvas.width,this.canvas.height];t0&&(r.resourceTiming=t._resourceTiming,t._resourceTiming=[]),t.fire("data",r)}})},e.prototype.onAdd=function(t){this.map=t,this.load()},e.prototype.setData=function(t){var e=this;return this._data=t,this.fire("dataloading",{dataType:"source"}),this._updateWorkerData(function(t){if(t)return e.fire("error",{error:t});var r={dataType:"source",sourceDataType:"content"};e._collectResourceTiming&&e._resourceTiming&&e._resourceTiming.length>0&&(r.resourceTiming=e._resourceTiming,e._resourceTiming=[]),e.fire("data",r)}),this},e.prototype._updateWorkerData=function(t){var e=this,r=i.extend({},this.workerOptions),n=this._data;"string"==typeof n?(r.request=this.map._transformRequest(function(t){var e=a.document.createElement("a");return e.href=t,e.href}(n),s.Source),r.request.collectResourceTiming=this._collectResourceTiming):r.data=JSON.stringify(n),this.workerID=this.dispatcher.send(this.type+".loadData",r,function(r,n){e._loaded=!0,n&&n.resourceTiming&&n.resourceTiming[e.id]&&(e._resourceTiming=n.resourceTiming[e.id].slice(0)),t(r)},this.workerID)},e.prototype.loadTile=function(t,e){var r=this,n=void 0===t.workerID||"expired"===t.state?"loadTile":"reloadTile",i={type:this.type,uid:t.uid,tileID:t.tileID,zoom:t.tileID.overscaledZ,maxZoom:this.maxzoom,tileSize:this.tileSize,source:this.id,pixelRatio:l.devicePixelRatio,overscaling:t.tileID.overscaleFactor(),showCollisionBoxes:this.map.showCollisionBoxes};t.workerID=this.dispatcher.send(n,i,function(i,a){return t.unloadVectorData(),t.aborted?e(null):i?e(i):(t.loadVectorData(a,r.map.painter,"reloadTile"===n),e(null))},this.workerID)},e.prototype.abortTile=function(t){t.aborted=!0},e.prototype.unloadTile=function(t){t.unloadVectorData(),this.dispatcher.send("removeTile",{uid:t.uid,type:this.type,source:this.id},null,t.workerID)},e.prototype.onRemove=function(){this.dispatcher.broadcast("removeSource",{type:this.type,source:this.id})},e.prototype.serialize=function(){return i.extend({},this._options,{type:this.type,data:this._data})},e.prototype.hasTransition=function(){return!1},e}(n);e.exports=c},{"../data/extent":53,"../util/ajax":251,"../util/browser":252,"../util/evented":260,"../util/util":275,"../util/window":254}],100:[function(t,e,r){function n(t,e){var r=t.source,n=t.tileID.canonical;if(!this._geoJSONIndexes[r])return e(null,null);var i=this._geoJSONIndexes[r].getTile(n.z,n.x,n.y);if(!i)return e(null,null);var a=new s(i.features),o=l(a);0===o.byteOffset&&o.byteLength===o.buffer.byteLength||(o=new Uint8Array(o)),e(null,{vectorTile:a,rawData:o.buffer})}var i=t("../util/ajax"),a=t("../util/performance"),o=t("geojson-rewind"),s=t("./geojson_wrapper"),l=t("vt-pbf"),c=t("supercluster"),u=t("geojson-vt"),f=function(t){function e(e,r,i){t.call(this,e,r,n),i&&(this.loadGeoJSON=i),this._geoJSONIndexes={}}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.loadData=function(t,e){var r=this;this.loadGeoJSON(t,function(n,i){if(n||!i)return e(n);if("object"!=typeof i)return e(new Error("Input data is not a valid GeoJSON object."));o(i,!0);try{r._geoJSONIndexes[t.source]=t.cluster?c(t.superclusterOptions).load(i.features):u(i,t.geojsonVtOptions)}catch(n){return e(n)}r.loaded[t.source]={};var s={};if(t.request&&t.request.collectResourceTiming){var l=a.getEntriesByName(t.request.url);l&&(s.resourceTiming={},s.resourceTiming[t.source]=JSON.parse(JSON.stringify(l)))}e(null,s)})},e.prototype.reloadTile=function(e,r){var n=this.loaded[e.source],i=e.uid;return n&&n[i]?t.prototype.reloadTile.call(this,e,r):this.loadTile(e,r)},e.prototype.loadGeoJSON=function(t,e){if(t.request)i.getJSON(t.request,e);else{if("string"!=typeof t.data)return e(new Error("Input data is not a valid GeoJSON object."));try{return e(null,JSON.parse(t.data))}catch(t){return e(new Error("Input data is not a valid GeoJSON object."))}}},e.prototype.removeSource=function(t,e){this._geoJSONIndexes[t.source]&&delete this._geoJSONIndexes[t.source],e()},e}(t("./vector_tile_worker_source"));e.exports=f},{"../util/ajax":251,"../util/performance":268,"./geojson_wrapper":101,"./vector_tile_worker_source":116,"geojson-rewind":15,"geojson-vt":19,supercluster:32,"vt-pbf":34}],101:[function(t,e,r){var n=t("@mapbox/point-geometry"),i=t("@mapbox/vector-tile").VectorTileFeature.prototype.toGeoJSON,a=t("../data/extent"),o=function(t){this._feature=t,this.extent=a,this.type=t.type,this.properties=t.tags,"id"in t&&!isNaN(t.id)&&(this.id=parseInt(t.id,10))};o.prototype.loadGeometry=function(){if(1===this._feature.type){for(var t=[],e=0,r=this._feature.geometry;e0&&(l[new s(t.overscaledZ,i,e.z,n,e.y-1).key]={backfilled:!1},l[new s(t.overscaledZ,t.wrap,e.z,e.x,e.y-1).key]={backfilled:!1},l[new s(t.overscaledZ,o,e.z,a,e.y-1).key]={backfilled:!1}),e.y+11||(Math.abs(r)>1&&(1===Math.abs(r+i)?r+=i:1===Math.abs(r-i)&&(r-=i)),e.dem&&t.dem&&(t.dem.backfillBorder(e.dem,r,n),t.neighboringTiles&&t.neighboringTiles[a]&&(t.neighboringTiles[a].backfilled=!0)))}for(var r=this.getRenderableIds(),n=0;ne)){var s=Math.pow(2,o.tileID.canonical.z-t.canonical.z);if(Math.floor(o.tileID.canonical.x/s)===t.canonical.x&&Math.floor(o.tileID.canonical.y/s)===t.canonical.y)for(r[a]=o.tileID,i=!0;o&&o.tileID.overscaledZ-1>t.overscaledZ;){var l=o.tileID.scaledTo(o.tileID.overscaledZ-1);if(!l)break;(o=n._tiles[l.key])&&o.hasData()&&(delete r[a],r[l.key]=l)}}}return i},e.prototype.findLoadedParent=function(t,e,r){for(var n=this,i=t.overscaledZ-1;i>=e;i--){var a=t.scaledTo(i);if(!a)return;var o=String(a.key),s=n._tiles[o];if(s&&s.hasData())return r[o]=a,s;if(n._cache.has(o))return r[o]=a,n._cache.get(o)}},e.prototype.updateCacheSize=function(t){var e=(Math.ceil(t.width/this._source.tileSize)+1)*(Math.ceil(t.height/this._source.tileSize)+1),r=Math.floor(5*e),n="number"==typeof this._maxTileCacheSize?Math.min(this._maxTileCacheSize,r):r;this._cache.setMaxSize(n)},e.prototype.update=function(t){var r=this;if(this.transform=t,this._sourceLoaded&&!this._paused){var n;this.updateCacheSize(t),this._coveredTiles={},this.used?this._source.tileID?n=t.getVisibleUnwrappedCoordinates(this._source.tileID).map(function(t){return new d(t.canonical.z,t.wrap,t.canonical.z,t.canonical.x,t.canonical.y)}):(n=t.coveringTiles({tileSize:this._source.tileSize,minzoom:this._source.minzoom,maxzoom:this._source.maxzoom,roundZoom:this._source.roundZoom,reparseOverscaled:this._source.reparseOverscaled}),this._source.hasTile&&(n=n.filter(function(t){return r._source.hasTile(t)}))):n=[];var a,o=(this._source.roundZoom?Math.round:Math.floor)(this.getZoom(t)),s=Math.max(o-e.maxOverzooming,this._source.minzoom),l=Math.max(o+e.maxUnderzooming,this._source.minzoom),c=this._updateRetainedTiles(n,o),f={};if(i(this._source.type))for(var h=Object.keys(c),g=0;g=p.now())){r._findLoadedChildren(v,l,c)&&(c[m]=v);var x=r.findLoadedParent(v,s,f);x&&r._addTile(x.tileID)}}for(a in f)c[a]||(r._coveredTiles[a]=!0);for(a in f)c[a]=f[a];for(var b=u.keysDifference(this._tiles,c),_=0;_n._source.maxzoom){var p=c.children(n._source.maxzoom)[0],d=n.getTile(p);d&&d.hasData()?i[p.key]=p:h=!1}else{n._findLoadedChildren(c,s,i);for(var g=c.children(n._source.maxzoom),m=0;m=o;--v){var y=c.scaledTo(v);if(a[y.key])break;if(a[y.key]=!0,!(u=n.getTile(y))&&f&&(u=n._addTile(y)),u&&(i[y.key]=y,f=u.wasRequested(),u.hasData()))break}}}return i},e.prototype._addTile=function(t){var e=this._tiles[t.key];if(e)return e;(e=this._cache.getAndRemove(t.key))&&this._cacheTimers[t.key]&&(clearTimeout(this._cacheTimers[t.key]),delete this._cacheTimers[t.key],this._setTileReloadTimer(t.key,e));var r=Boolean(e);return r||(e=new o(t,this._source.tileSize*t.overscaleFactor()),this._loadTile(e,this._tileLoaded.bind(this,e,t.key,e.state))),e?(e.uses++,this._tiles[t.key]=e,r||this._source.fire("dataloading",{tile:e,coord:e.tileID,dataType:"source"}),e):null},e.prototype._setTileReloadTimer=function(t,e){var r=this;t in this._timers&&(clearTimeout(this._timers[t]),delete this._timers[t]);var n=e.getExpiryTimeout();n&&(this._timers[t]=setTimeout(function(){r._reloadTile(t,"expired"),delete r._timers[t]},n))},e.prototype._setCacheInvalidationTimer=function(t,e){var r=this;t in this._cacheTimers&&(clearTimeout(this._cacheTimers[t]),delete this._cacheTimers[t]);var n=e.getExpiryTimeout();n&&(this._cacheTimers[t]=setTimeout(function(){r._cache.remove(t),delete r._cacheTimers[t]},n))},e.prototype._removeTile=function(t){var e=this._tiles[t];if(e&&(e.uses--,delete this._tiles[t],this._timers[t]&&(clearTimeout(this._timers[t]),delete this._timers[t]),!(e.uses>0)))if(e.hasData()){e.tileID=e.tileID.wrapped();var r=e.tileID.key;this._cache.add(r,e),this._setCacheInvalidationTimer(r,e)}else e.aborted=!0,this._abortTile(e),this._unloadTile(e)},e.prototype.clearTiles=function(){for(var t in this._shouldReloadOnResume=!1,this._paused=!1,this._tiles)this._removeTile(t);this._resetCache()},e.prototype._resetCache=function(){for(var t in this._cacheTimers)clearTimeout(this._cacheTimers[t]);this._cacheTimers={},this._cache.reset()},e.prototype.tilesIn=function(t){for(var e=[],r=this.getIds(),i=1/0,a=1/0,o=-1/0,s=-1/0,l=t[0].zoom,u=0;u=0&&m[1].y>=0){for(var v=[],y=0;y=p.now())return!0}return!1},e}(s);g.maxOverzooming=10,g.maxUnderzooming=3,e.exports=g},{"../data/extent":53,"../geo/coordinate":61,"../gl/context":66,"../util/browser":252,"../util/evented":260,"../util/lru_cache":266,"../util/util":275,"./source":110,"./tile":112,"./tile_id":114,"@mapbox/point-geometry":4}],112:[function(t,e,r){var n=t("../util/util"),i=t("../data/bucket").deserialize,a=(t("../data/feature_index"),t("@mapbox/vector-tile")),o=t("pbf"),s=t("../util/vectortile_to_geojson"),l=t("../style-spec/feature_filter"),c=(t("../symbol/collision_index"),t("../data/bucket/symbol_bucket")),u=t("../data/array_types"),f=u.RasterBoundsArray,h=u.CollisionBoxArray,p=t("../data/raster_bounds_attributes"),d=t("../data/extent"),g=t("@mapbox/point-geometry"),m=t("../render/texture"),v=t("../data/segment").SegmentVector,y=t("../data/index_array_type").TriangleIndexArray,x=t("../util/browser"),b=function(t,e){this.tileID=t,this.uid=n.uniqueId(),this.uses=0,this.tileSize=e,this.buckets={},this.expirationTime=null,this.expiredRequestCount=0,this.state="loading"};b.prototype.registerFadeDuration=function(t){var e=t+this.timeAdded;e>s.z,c=new g(s.x*l,s.y*l),u=new g(c.x+l,c.y+l),h=this.segments.prepareSegment(4,r,i);r.emplaceBack(c.x,c.y,c.x,c.y),r.emplaceBack(u.x,c.y,u.x,c.y),r.emplaceBack(c.x,u.y,c.x,u.y),r.emplaceBack(u.x,u.y,u.x,u.y);var m=h.vertexLength;i.emplaceBack(m,m+1,m+2),i.emplaceBack(m+1,m+2,m+3),h.vertexLength+=4,h.primitiveLength+=2}this.maskedBoundsBuffer=e.createVertexBuffer(r,p.members),this.maskedIndexBuffer=e.createIndexBuffer(i)}},b.prototype.hasData=function(){return"loaded"===this.state||"reloading"===this.state||"expired"===this.state},b.prototype.setExpiryData=function(t){var e=this.expirationTime;if(t.cacheControl){var r=n.parseCacheControl(t.cacheControl);r["max-age"]&&(this.expirationTime=Date.now()+1e3*r["max-age"])}else t.expires&&(this.expirationTime=new Date(t.expires).getTime());if(this.expirationTime){var i=Date.now(),a=!1;if(this.expirationTime>i)a=!1;else if(e)if(this.expirationTime=e&&t.x=r&&t.y0;a--)i+=(e&(n=1<this.canonical.z?new c(t,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y):new c(t,this.wrap,t,this.canonical.x>>e,this.canonical.y>>e)},c.prototype.isChildOf=function(t){var e=this.canonical.z-t.canonical.z;return 0===t.overscaledZ||t.overscaledZ>e&&t.canonical.y===this.canonical.y>>e},c.prototype.children=function(t){if(this.overscaledZ>=t)return[new c(this.overscaledZ+1,this.wrap,this.canonical.z,this.canonical.x,this.canonical.y)];var e=this.canonical.z+1,r=2*this.canonical.x,n=2*this.canonical.y;return[new c(e,this.wrap,e,r,n),new c(e,this.wrap,e,r+1,n),new c(e,this.wrap,e,r,n+1),new c(e,this.wrap,e,r+1,n+1)]},c.prototype.isLessThan=function(t){return this.wrapt.wrap)&&(this.overscaledZt.overscaledZ)&&(this.canonical.xt.canonical.x)&&this.canonical.y=E.maxzoom||"none"===E.visibility||(n(C,d.zoom),(v[E.id]=E.createBucket({index:m.bucketLayerIDs.length,layers:C,zoom:d.zoom,pixelRatio:d.pixelRatio,overscaling:d.overscaling,collisionBoxArray:d.collisionBoxArray})).populate(k,y),m.bucketLayerIDs.push(C.map(function(t){return t.id})))}}}var L,z,P,D=c.mapObject(y.glyphDependencies,function(t){return Object.keys(t).map(Number)});Object.keys(D).length?r.send("getGlyphs",{uid:this.uid,stacks:D},function(t,e){L||(L=t,z=e,p.call(d))}):z={};var O=Object.keys(y.iconDependencies);O.length?r.send("getImages",{icons:O},function(t,e){L||(L=t,P=e,p.call(d))}):P={},p.call(this)},e.exports=d},{"../data/array_types":39,"../data/bucket/symbol_bucket":51,"../data/feature_index":54,"../render/glyph_atlas":85,"../render/image_atlas":87,"../style/evaluation_parameters":182,"../symbol/symbol_layout":227,"../util/dictionary_coder":257,"../util/util":275,"./tile_id":114}],120:[function(t,e,r){function n(t,e){var r={};for(var n in t)"ref"!==n&&(r[n]=t[n]);return i.forEach(function(t){t in e&&(r[t]=e[t])}),r}var i=t("./util/ref_properties");e.exports=function(t){t=t.slice();for(var e=Object.create(null),r=0;r4)return e.error("Expected 1, 2, or 3 arguments, but found "+(t.length-1)+" instead.");var r,n;if(t.length>2){var i=t[1];if("string"!=typeof i||!(i in p))return e.error('The item type argument of "array" must be one of string, number, boolean',1);r=p[i]}else r=o;if(t.length>3){if("number"!=typeof t[2]||t[2]<0||t[2]!==Math.floor(t[2]))return e.error('The length argument to "array" must be a positive integer literal',2);n=t[2]}var s=a(r,n),l=e.parse(t[t.length-1],t.length-1,o);return l?new d(s,l):null},d.prototype.evaluate=function(t){var e=this.input.evaluate(t);if(u(this.type,f(e)))throw new h("Expected value to be of type "+i(this.type)+", but found "+i(f(e))+" instead.");return e},d.prototype.eachChild=function(t){t(this.input)},d.prototype.possibleOutputs=function(){return this.input.possibleOutputs()},e.exports=d},{"../runtime_error":143,"../types":146,"../values":147}],125:[function(t,e,r){var n=t("../types"),i=n.ObjectType,a=n.ValueType,o=n.StringType,s=n.NumberType,l=n.BooleanType,c=t("../runtime_error"),u=t("../types"),f=u.checkSubtype,h=u.toString,p=t("../values").typeOf,d={string:o,number:s,boolean:l,object:i},g=function(t,e){this.type=t,this.args=e};g.parse=function(t,e){if(t.length<2)return e.error("Expected at least one argument.");for(var r=t[0],n=d[r],i=[],o=1;o=r.length)throw new s("Array index out of bounds: "+e+" > "+r.length+".");if(e!==Math.floor(e))throw new s("Array index must be an integer, but found "+e+" instead.");return r[e]},l.prototype.eachChild=function(t){t(this.index),t(this.input)},l.prototype.possibleOutputs=function(){return[void 0]},e.exports=l},{"../runtime_error":143,"../types":146}],127:[function(t,e,r){var n=t("../types").BooleanType,i=function(t,e,r){this.type=t,this.branches=e,this.otherwise=r};i.parse=function(t,e){if(t.length<4)return e.error("Expected at least 3 arguments, but found only "+(t.length-1)+".");if(t.length%2!=0)return e.error("Expected an odd number of arguments.");var r;e.expectedType&&"value"!==e.expectedType.kind&&(r=e.expectedType);for(var a=[],o=1;o4?"Invalid rbga value "+JSON.stringify(e)+": expected an array containing either three or four numeric values.":c(e[0],e[1],e[2],e[3])))return new l(e[0]/255,e[1]/255,e[2]/255,e[3]);throw new u(r||"Could not parse color from value '"+("string"==typeof e?e:JSON.stringify(e))+"'")}for(var o=null,s=0,f=this.args;sn.evaluate(t)}function c(t,e){var r=e[0],n=e[1];return r.evaluate(t)<=n.evaluate(t)}function u(t,e){var r=e[0],n=e[1];return r.evaluate(t)>=n.evaluate(t)}var f=t("../types"),h=f.NumberType,p=f.StringType,d=f.BooleanType,g=f.ColorType,m=f.ObjectType,v=f.ValueType,y=f.ErrorType,x=f.array,b=f.toString,_=t("../values"),w=_.typeOf,k=_.Color,M=_.validateRGBA,A=t("../compound_expression"),T=A.CompoundExpression,S=A.varargs,C=t("../runtime_error"),E=t("./let"),L=t("./var"),z=t("./literal"),P=t("./assertion"),D=t("./array"),O=t("./coercion"),I=t("./at"),R=t("./match"),B=t("./case"),F=t("./step"),N=t("./interpolate"),j=t("./coalesce"),V=t("./equals"),U={"==":V.Equals,"!=":V.NotEquals,array:D,at:I,boolean:P,case:B,coalesce:j,interpolate:N,let:E,literal:z,match:R,number:P,object:P,step:F,string:P,"to-color":O,"to-number":O,var:L};T.register(U,{error:[y,[p],function(t,e){var r=e[0];throw new C(r.evaluate(t))}],typeof:[p,[v],function(t,e){var r=e[0];return b(w(r.evaluate(t)))}],"to-string":[p,[v],function(t,e){var r=e[0],n=typeof(r=r.evaluate(t));return null===r||"string"===n||"number"===n||"boolean"===n?String(r):r instanceof k?r.toString():JSON.stringify(r)}],"to-boolean":[d,[v],function(t,e){var r=e[0];return Boolean(r.evaluate(t))}],"to-rgba":[x(h,4),[g],function(t,e){var r=e[0].evaluate(t),n=r.r,i=r.g,a=r.b,o=r.a;return[255*n/o,255*i/o,255*a/o,o]}],rgb:[g,[h,h,h],n],rgba:[g,[h,h,h,h],n],length:{type:h,overloads:[[[p],o],[[x(v)],o]]},has:{type:d,overloads:[[[p],function(t,e){return i(e[0].evaluate(t),t.properties())}],[[p,m],function(t,e){var r=e[0],n=e[1];return i(r.evaluate(t),n.evaluate(t))}]]},get:{type:v,overloads:[[[p],function(t,e){return a(e[0].evaluate(t),t.properties())}],[[p,m],function(t,e){var r=e[0],n=e[1];return a(r.evaluate(t),n.evaluate(t))}]]},properties:[m,[],function(t){return t.properties()}],"geometry-type":[p,[],function(t){return t.geometryType()}],id:[v,[],function(t){return t.id()}],zoom:[h,[],function(t){return t.globals.zoom}],"heatmap-density":[h,[],function(t){return t.globals.heatmapDensity||0}],"+":[h,S(h),function(t,e){for(var r=0,n=0,i=e;n":[d,[p,v],function(t,e){var r=e[0],n=e[1],i=t.properties()[r.value],a=n.value;return typeof i==typeof a&&i>a}],"filter-id->":[d,[v],function(t,e){var r=e[0],n=t.id(),i=r.value;return typeof n==typeof i&&n>i}],"filter-<=":[d,[p,v],function(t,e){var r=e[0],n=e[1],i=t.properties()[r.value],a=n.value;return typeof i==typeof a&&i<=a}],"filter-id-<=":[d,[v],function(t,e){var r=e[0],n=t.id(),i=r.value;return typeof n==typeof i&&n<=i}],"filter->=":[d,[p,v],function(t,e){var r=e[0],n=e[1],i=t.properties()[r.value],a=n.value;return typeof i==typeof a&&i>=a}],"filter-id->=":[d,[v],function(t,e){var r=e[0],n=t.id(),i=r.value;return typeof n==typeof i&&n>=i}],"filter-has":[d,[v],function(t,e){return e[0].value in t.properties()}],"filter-has-id":[d,[],function(t){return null!==t.id()}],"filter-type-in":[d,[x(p)],function(t,e){return e[0].value.indexOf(t.geometryType())>=0}],"filter-id-in":[d,[x(v)],function(t,e){return e[0].value.indexOf(t.id())>=0}],"filter-in-small":[d,[p,x(v)],function(t,e){var r=e[0];return e[1].value.indexOf(t.properties()[r.value])>=0}],"filter-in-large":[d,[p,x(v)],function(t,e){var r=e[0],n=e[1];return function(t,e,r,n){for(;r<=n;){var i=r+n>>1;if(e[i]===t)return!0;e[i]>t?n=i-1:r=i+1}return!1}(t.properties()[r.value],n.value,0,n.value.length-1)}],">":{type:d,overloads:[[[h,h],l],[[p,p],l]]},"<":{type:d,overloads:[[[h,h],s],[[p,p],s]]},">=":{type:d,overloads:[[[h,h],u],[[p,p],u]]},"<=":{type:d,overloads:[[[h,h],c],[[p,p],c]]},all:{type:d,overloads:[[[d,d],function(t,e){var r=e[0],n=e[1];return r.evaluate(t)&&n.evaluate(t)}],[S(d),function(t,e){for(var r=0,n=e;r1}))return e.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.",1);r={name:"cubic-bezier",controlPoints:o}}if(t.length-1<4)return e.error("Expected at least 4 arguments, but found only "+(t.length-1)+".");if((t.length-1)%2!=0)return e.error("Expected an even number of arguments.");if(!(n=e.parse(n,2,l)))return null;var c=[],f=null;e.expectedType&&"value"!==e.expectedType.kind&&(f=e.expectedType);for(var h=0;h=p)return e.error('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.',g);var v=e.parse(d,m,f);if(!v)return null;f=f||v.type,c.push([p,v])}return"number"===f.kind||"color"===f.kind||"array"===f.kind&&"number"===f.itemType.kind&&"number"==typeof f.N?new u(f,r,n,c):e.error("Type "+s(f)+" is not interpolatable.")},u.prototype.evaluate=function(t){var e=this.labels,r=this.outputs;if(1===e.length)return r[0].evaluate(t);var n=this.input.evaluate(t);if(n<=e[0])return r[0].evaluate(t);var i=e.length;if(n>=e[i-1])return r[i-1].evaluate(t);var o=c(e,n),s=e[o],l=e[o+1],f=u.interpolationFactor(this.interpolation,n,s,l),h=r[o].evaluate(t),p=r[o+1].evaluate(t);return a[this.type.kind.toLowerCase()](h,p,f)},u.prototype.eachChild=function(t){t(this.input);for(var e=0,r=this.outputs;eNumber.MAX_SAFE_INTEGER)return f.error("Branch labels must be integers no larger than "+Number.MAX_SAFE_INTEGER+".");if("number"==typeof d&&Math.floor(d)!==d)return f.error("Numeric branch labels must be integer values.");if(r){if(f.checkSubtype(r,n(d)))return null}else r=n(d);if(void 0!==o[String(d)])return f.error("Branch labels must be unique.");o[String(d)]=s.length}var g=e.parse(u,l,a);if(!g)return null;a=a||g.type,s.push(g)}var m=e.parse(t[1],1,r);if(!m)return null;var v=e.parse(t[t.length-1],t.length-1,a);return v?new i(r,a,m,o,s,v):null},i.prototype.evaluate=function(t){var e=this.input.evaluate(t);return(this.outputs[this.cases[e]]||this.otherwise).evaluate(t)},i.prototype.eachChild=function(t){t(this.input),this.outputs.forEach(t),t(this.otherwise)},i.prototype.possibleOutputs=function(){return(t=[]).concat.apply(t,this.outputs.map(function(t){return t.possibleOutputs()})).concat(this.otherwise.possibleOutputs());var t},e.exports=i},{"../values":147}],136:[function(t,e,r){var n=t("../types").NumberType,i=t("../stops").findStopLessThanOrEqualTo,a=function(t,e,r){this.type=t,this.input=e,this.labels=[],this.outputs=[];for(var n=0,i=r;n=c)return e.error('Input/output pairs for "step" expressions must be arranged with input values in strictly ascending order.',f);var p=e.parse(u,h,s);if(!p)return null;s=s||p.type,o.push([c,p])}return new a(s,r,o)},a.prototype.evaluate=function(t){var e=this.labels,r=this.outputs;if(1===e.length)return r[0].evaluate(t);var n=this.input.evaluate(t);if(n<=e[0])return r[0].evaluate(t);var a=e.length;return n>=e[a-1]?r[a-1].evaluate(t):r[i(e,n)].evaluate(t)},a.prototype.eachChild=function(t){t(this.input);for(var e=0,r=this.outputs;e0&&"string"==typeof t[0]&&t[0]in g}function i(t,e,r){void 0===r&&(r={});var n=new l(g,[],function(t){var e={color:z,string:P,number:D,enum:P,boolean:O};return"array"===t.type?R(e[t.value]||I,t.length):e[t.type]||null}(e)),i=n.parse(t);return i?x(!1===r.handleErrors?new _(i):new w(i,e)):b(n.errors)}function a(t,e,r){if(void 0===r&&(r={}),"error"===(t=i(t,e,r)).result)return t;var n=t.value.expression,a=m.isFeatureConstant(n);if(!a&&!e["property-function"])return b([new s("","property expressions not supported")]);var o=m.isGlobalPropertyConstant(n,["zoom"]);if(!o&&!1===e["zoom-function"])return b([new s("","zoom expressions not supported")]);var l=function t(e){var r=null;if(e instanceof d)r=t(e.result);else if(e instanceof p)for(var n=0,i=e.args;n=0)return!1;var i=!0;return e.eachChild(function(e){i&&!t(e,r)&&(i=!1)}),i}}},{"./compound_expression":123}],141:[function(t,e,r){var n=t("./scope"),i=t("./types").checkSubtype,a=t("./parsing_error"),o=t("./definitions/literal"),s=t("./definitions/assertion"),l=t("./definitions/array"),c=t("./definitions/coercion"),u=function(t,e,r,i,a){void 0===e&&(e=[]),void 0===i&&(i=new n),void 0===a&&(a=[]),this.registry=t,this.path=e,this.key=e.map(function(t){return"["+t+"]"}).join(""),this.scope=i,this.errors=a,this.expectedType=r};u.prototype.parse=function(e,r,n,i,a){void 0===a&&(a={});var u=this;if(r&&(u=u.concat(r,n,i)),null!==e&&"string"!=typeof e&&"boolean"!=typeof e&&"number"!=typeof e||(e=["literal",e]),Array.isArray(e)){if(0===e.length)return u.error('Expected an array with at least one element. If you wanted a literal array, use ["literal", []].');var f=e[0];if("string"!=typeof f)return u.error("Expression name must be a string, but found "+typeof f+' instead. If you wanted a literal array, use ["literal", [...]].',0),null;var h=u.registry[f];if(h){var p=h.parse(e,u);if(!p)return null;if(u.expectedType){var d=u.expectedType,g=p.type;if("string"!==d.kind&&"number"!==d.kind&&"boolean"!==d.kind||"value"!==g.kind)if("array"===d.kind&&"value"===g.kind)a.omitTypeAnnotations||(p=new l(d,p));else if("color"!==d.kind||"value"!==g.kind&&"string"!==g.kind){if(u.checkSubtype(u.expectedType,p.type))return null}else a.omitTypeAnnotations||(p=new c(d,[p]));else a.omitTypeAnnotations||(p=new s(d,[p]))}if(!(p instanceof o)&&function(e){var r=t("./compound_expression").CompoundExpression,n=t("./is_constant"),i=n.isGlobalPropertyConstant,a=n.isFeatureConstant;if(e instanceof t("./definitions/var"))return!1;if(e instanceof r&&"error"===e.name)return!1;var s=!0;return e.eachChild(function(t){t instanceof o||(s=!1)}),!!s&&a(e)&&i(e,["zoom","heatmap-density"])}(p)){var m=new(t("./evaluation_context"));try{p=new o(p.type,p.evaluate(m))}catch(e){return u.error(e.message),null}}return p}return u.error('Unknown expression "'+f+'". If you wanted a literal array, use ["literal", [...]].',0)}return void 0===e?u.error("'undefined' value invalid. Use null instead."):"object"==typeof e?u.error('Bare objects invalid. Use ["literal", {...}] instead.'):u.error("Expected an array, but found "+typeof e+" instead.")},u.prototype.concat=function(t,e,r){var n="number"==typeof t?this.path.concat(t):this.path,i=r?this.scope.concat(r):this.scope;return new u(this.registry,n,e||null,i,this.errors)},u.prototype.error=function(t){for(var e=arguments,r=[],n=arguments.length-1;n-- >0;)r[n]=e[n+1];var i=""+this.key+r.map(function(t){return"["+t+"]"}).join("");this.errors.push(new a(i,t))},u.prototype.checkSubtype=function(t,e){var r=i(t,e);return r&&this.error(r),r},e.exports=u},{"./compound_expression":123,"./definitions/array":124,"./definitions/assertion":125,"./definitions/coercion":129,"./definitions/literal":134,"./definitions/var":137,"./evaluation_context":138,"./is_constant":140,"./parsing_error":142,"./scope":144,"./types":146}],142:[function(t,e,r){var n=function(t){function e(e,r){t.call(this,r),this.message=r,this.key=e}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(Error);e.exports=n},{}],143:[function(t,e,r){var n=function(t){this.name="ExpressionEvaluationError",this.message=t};n.prototype.toJSON=function(){return this.message},e.exports=n},{}],144:[function(t,e,r){var n=function(t,e){void 0===e&&(e=[]),this.parent=t,this.bindings={};for(var r=0,n=e;rr&&ee))throw new n("Input is not a number.");o=s-1}}return Math.max(s-1,0)}}},{"./runtime_error":143}],146:[function(t,e,r){function n(t,e){return{kind:"array",itemType:t,N:e}}function i(t){if("array"===t.kind){var e=i(t.itemType);return"number"==typeof t.N?"array<"+e+", "+t.N+">":"value"===t.itemType.kind?"array":"array<"+e+">"}return t.kind}var a={kind:"null"},o={kind:"number"},s={kind:"string"},l={kind:"boolean"},c={kind:"color"},u={kind:"object"},f={kind:"value"},h=[a,o,s,l,c,u,n(f)];e.exports={NullType:a,NumberType:o,StringType:s,BooleanType:l,ColorType:c,ObjectType:u,ValueType:f,array:n,ErrorType:{kind:"error"},toString:i,checkSubtype:function t(e,r){if("error"===r.kind)return null;if("array"===e.kind){if("array"===r.kind&&!t(e.itemType,r.itemType)&&("number"!=typeof e.N||e.N===r.N))return null}else{if(e.kind===r.kind)return null;if("value"===e.kind)for(var n=0,a=h;n=0&&t<=255&&"number"==typeof e&&e>=0&&e<=255&&"number"==typeof r&&r>=0&&r<=255?void 0===n||"number"==typeof n&&n>=0&&n<=1?null:"Invalid rgba value ["+[t,e,r,n].join(", ")+"]: 'a' must be between 0 and 1.":"Invalid rgba value ["+("number"==typeof n?[t,e,r,n]:[t,e,r]).join(", ")+"]: 'r', 'g', and 'b' must be between 0 and 255."},isValue:function t(e){if(null===e)return!0;if("string"==typeof e)return!0;if("boolean"==typeof e)return!0;if("number"==typeof e)return!0;if(e instanceof n)return!0;if(Array.isArray(e)){for(var r=0,i=e;r=2&&"$id"!==t[1]&&"$type"!==t[1];case"in":case"!in":case"!has":case"none":return!1;case"==":case"!=":case">":case">=":case"<":case"<=":return 3===t.length&&(Array.isArray(t[1])||Array.isArray(t[2]));case"any":case"all":for(var e=0,r=t.slice(1);ee?1:0}function a(t){if(!t)return!0;var e=t[0];return t.length<=1?"any"!==e:"=="===e?o(t[1],t[2],"=="):"!="===e?c(o(t[1],t[2],"==")):"<"===e||">"===e||"<="===e||">="===e?o(t[1],t[2],e):"any"===e?function(t){return["any"].concat(t.map(a))}(t.slice(1)):"all"===e?["all"].concat(t.slice(1).map(a)):"none"===e?["all"].concat(t.slice(1).map(a).map(c)):"in"===e?s(t[1],t.slice(2)):"!in"===e?c(s(t[1],t.slice(2))):"has"===e?l(t[1]):"!has"!==e||c(l(t[1]))}function o(t,e,r){switch(t){case"$type":return["filter-type-"+r,e];case"$id":return["filter-id-"+r,e];default:return["filter-"+r,t,e]}}function s(t,e){if(0===e.length)return!1;switch(t){case"$type":return["filter-type-in",["literal",e]];case"$id":return["filter-id-in",["literal",e]];default:return e.length>200&&!e.some(function(t){return typeof t!=typeof e[0]})?["filter-in-large",t,["literal",e.sort(i)]]:["filter-in-small",t,["literal",e]]}}function l(t){switch(t){case"$type":return!0;case"$id":return["filter-has-id"];default:return["filter-has",t]}}function c(t){return["!",t]}var u=t("../expression").createExpression;e.exports=function(t){if(!t)return function(){return!0};n(t)||(t=a(t));var e=u(t,f);if("error"===e.result)throw new Error(e.value.map(function(t){return t.key+": "+t.message}).join(", "));return function(t,r){return e.value.evaluate(t,r)}},e.exports.isExpressionFilter=n;var f={type:"boolean",default:!1,function:!0,"property-function":!0,"zoom-function":!0}},{"../expression":139}],149:[function(t,e,r){function n(t){return t}function i(t,e,r){return void 0!==t?t:void 0!==e?e:void 0!==r?r:void 0}function a(t,e,r,n,a){return i(typeof r===a?n[r]:void 0,t.default,e.default)}function o(t,e,r){if("number"!==p(r))return i(t.default,e.default);var n=t.stops.length;if(1===n)return t.stops[0][1];if(r<=t.stops[0][0])return t.stops[0][1];if(r>=t.stops[n-1][0])return t.stops[n-1][1];var a=c(t.stops,r);return t.stops[a][1]}function s(t,e,r){var a=void 0!==t.base?t.base:1;if("number"!==p(r))return i(t.default,e.default);var o=t.stops.length;if(1===o)return t.stops[0][1];if(r<=t.stops[0][0])return t.stops[0][1];if(r>=t.stops[o-1][0])return t.stops[o-1][1];var s=c(t.stops,r),l=function(t,e,r,n){var i=n-r,a=t-r;return 0===i?0:1===e?a/i:(Math.pow(e,a)-1)/(Math.pow(e,i)-1)}(r,a,t.stops[s][0],t.stops[s+1][0]),f=t.stops[s][1],h=t.stops[s+1][1],g=d[e.type]||n;if(t.colorSpace&&"rgb"!==t.colorSpace){var m=u[t.colorSpace];g=function(t,e){return m.reverse(m.interpolate(m.forward(t),m.forward(e),l))}}return"function"==typeof f.evaluate?{evaluate:function(){for(var t=arguments,e=[],r=arguments.length;r--;)e[r]=t[r];var n=f.evaluate.apply(void 0,e),i=h.evaluate.apply(void 0,e);if(void 0!==n&&void 0!==i)return g(n,i,l)}}:g(f,h,l)}function l(t,e,r){return"color"===e.type?r=f.parse(r):p(r)===e.type||"enum"===e.type&&e.values[r]||(r=void 0),i(r,t.default,e.default)}function c(t,e){for(var r,n,i=0,a=t.length-1,o=0;i<=a;){if(r=t[o=Math.floor((i+a)/2)][0],n=t[o+1][0],e===r||e>r&&ee&&(a=o-1)}return Math.max(o-1,0)}var u=t("../util/color_spaces"),f=t("../util/color"),h=t("../util/extend"),p=t("../util/get_type"),d=t("../util/interpolate"),g=t("../expression/definitions/interpolate");e.exports={createFunction:function t(e,r){var n,c,p,d="color"===r.type,m=e.stops&&"object"==typeof e.stops[0][0],v=m||void 0!==e.property,y=m||!v,x=e.type||("interpolated"===r.function?"exponential":"interval");if(d&&((e=h({},e)).stops&&(e.stops=e.stops.map(function(t){return[t[0],f.parse(t[1])]})),e.default?e.default=f.parse(e.default):e.default=f.parse(r.default)),e.colorSpace&&"rgb"!==e.colorSpace&&!u[e.colorSpace])throw new Error("Unknown color space: "+e.colorSpace);if("exponential"===x)n=s;else if("interval"===x)n=o;else if("categorical"===x){n=a,c=Object.create(null);for(var b=0,_=e.stops;b<_.length;b+=1){var w=_[b];c[w[0]]=w[1]}p=typeof e.stops[0][0]}else{if("identity"!==x)throw new Error('Unknown function type "'+x+'"');n=l}if(m){for(var k={},M=[],A=0;A":{},">=":{},"<":{},"<=":{},in:{},"!in":{},all:{},any:{},none:{},has:{},"!has":{}}},geometry_type:{type:"enum",values:{Point:{},LineString:{},Polygon:{}}},function:{expression:{type:"expression"},stops:{type:"array",value:"function_stop"},base:{type:"number",default:1,minimum:0},property:{type:"string",default:"$zoom"},type:{type:"enum",values:{identity:{},exponential:{},interval:{},categorical:{}},default:"exponential"},colorSpace:{type:"enum",values:{rgb:{},lab:{},hcl:{}},default:"rgb"},default:{type:"*",required:!1}},function_stop:{type:"array",minimum:0,maximum:22,value:["number","color"],length:2},expression:{type:"array",value:"*",minimum:1},expression_name:{type:"enum",values:{let:{group:"Variable binding"},var:{group:"Variable binding"},literal:{group:"Types"},array:{group:"Types"},at:{group:"Lookup"},case:{group:"Decision"},match:{group:"Decision"},coalesce:{group:"Decision"},step:{group:"Ramps, scales, curves"},interpolate:{group:"Ramps, scales, curves"},ln2:{group:"Math"},pi:{group:"Math"},e:{group:"Math"},typeof:{group:"Types"},string:{group:"Types"},number:{group:"Types"},boolean:{group:"Types"},object:{group:"Types"},"to-string":{group:"Types"},"to-number":{group:"Types"},"to-boolean":{group:"Types"},"to-rgba":{group:"Color"},"to-color":{group:"Types"},rgb:{group:"Color"},rgba:{group:"Color"},get:{group:"Lookup"},has:{group:"Lookup"},length:{group:"Lookup"},properties:{group:"Feature data"},"geometry-type":{group:"Feature data"},id:{group:"Feature data"},zoom:{group:"Zoom"},"heatmap-density":{group:"Heatmap"},"+":{group:"Math"},"*":{group:"Math"},"-":{group:"Math"},"/":{group:"Math"},"%":{group:"Math"},"^":{group:"Math"},sqrt:{group:"Math"},log10:{group:"Math"},ln:{group:"Math"},log2:{group:"Math"},sin:{group:"Math"},cos:{group:"Math"},tan:{group:"Math"},asin:{group:"Math"},acos:{group:"Math"},atan:{group:"Math"},min:{group:"Math"},max:{group:"Math"},"==":{group:"Decision"},"!=":{group:"Decision"},">":{group:"Decision"},"<":{group:"Decision"},">=":{group:"Decision"},"<=":{group:"Decision"},all:{group:"Decision"},any:{group:"Decision"},"!":{group:"Decision"},upcase:{group:"String"},downcase:{group:"String"},concat:{group:"String"}}},light:{anchor:{type:"enum",default:"viewport",values:{map:{},viewport:{}},transition:!1,"zoom-function":!0,"property-function":!1,function:"piecewise-constant"},position:{type:"array",default:[1.15,210,30],length:3,value:"number",transition:!0,function:"interpolated","zoom-function":!0,"property-function":!1},color:{type:"color",default:"#ffffff",function:"interpolated","zoom-function":!0,"property-function":!1,transition:!0},intensity:{type:"number",default:.5,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,"property-function":!1,transition:!0}},paint:["paint_fill","paint_line","paint_circle","paint_heatmap","paint_fill-extrusion","paint_symbol","paint_raster","paint_hillshade","paint_background"],paint_fill:{"fill-antialias":{type:"boolean",function:"piecewise-constant","zoom-function":!0,default:!0},"fill-opacity":{type:"number",function:"interpolated","zoom-function":!0,"property-function":!0,default:1,minimum:0,maximum:1,transition:!0},"fill-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:[{"!":"fill-pattern"}]},"fill-outline-color":{type:"color",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:[{"!":"fill-pattern"},{"fill-antialias":!0}]},"fill-translate":{type:"array",value:"number",length:2,default:[0,0],function:"interpolated","zoom-function":!0,transition:!0,units:"pixels"},"fill-translate-anchor":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"map",requires:["fill-translate"]},"fill-pattern":{type:"string",function:"piecewise-constant","zoom-function":!0,transition:!0}},"paint_fill-extrusion":{"fill-extrusion-opacity":{type:"number",function:"interpolated","zoom-function":!0,"property-function":!1,default:1,minimum:0,maximum:1,transition:!0},"fill-extrusion-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:[{"!":"fill-extrusion-pattern"}]},"fill-extrusion-translate":{type:"array",value:"number",length:2,default:[0,0],function:"interpolated","zoom-function":!0,transition:!0,units:"pixels"},"fill-extrusion-translate-anchor":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"map",requires:["fill-extrusion-translate"]},"fill-extrusion-pattern":{type:"string",function:"piecewise-constant","zoom-function":!0,transition:!0},"fill-extrusion-height":{type:"number",function:"interpolated","zoom-function":!0,"property-function":!0,default:0,minimum:0,units:"meters",transition:!0},"fill-extrusion-base":{type:"number",function:"interpolated","zoom-function":!0,"property-function":!0,default:0,minimum:0,units:"meters",transition:!0,requires:["fill-extrusion-height"]}},paint_line:{"line-opacity":{type:"number",function:"interpolated","zoom-function":!0,"property-function":!0,default:1,minimum:0,maximum:1,transition:!0},"line-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:[{"!":"line-pattern"}]},"line-translate":{type:"array",value:"number",length:2,default:[0,0],function:"interpolated","zoom-function":!0,transition:!0,units:"pixels"},"line-translate-anchor":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"map",requires:["line-translate"]},"line-width":{type:"number",default:1,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels"},"line-gap-width":{type:"number",default:0,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels"},"line-offset":{type:"number",default:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels"},"line-blur":{type:"number",default:0,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels"},"line-dasharray":{type:"array",value:"number",function:"piecewise-constant","zoom-function":!0,minimum:0,transition:!0,units:"line widths",requires:[{"!":"line-pattern"}]},"line-pattern":{type:"string",function:"piecewise-constant","zoom-function":!0,transition:!0}},paint_circle:{"circle-radius":{type:"number",default:5,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels"},"circle-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0},"circle-blur":{type:"number",default:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0},"circle-opacity":{type:"number",default:1,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0},"circle-translate":{type:"array",value:"number",length:2,default:[0,0],function:"interpolated","zoom-function":!0,transition:!0,units:"pixels"},"circle-translate-anchor":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"map",requires:["circle-translate"]},"circle-pitch-scale":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"map"},"circle-pitch-alignment":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"viewport"},"circle-stroke-width":{type:"number",default:0,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels"},"circle-stroke-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0},"circle-stroke-opacity":{type:"number",default:1,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0}},paint_heatmap:{"heatmap-radius":{type:"number",default:30,minimum:1,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels"},"heatmap-weight":{type:"number",default:1,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!1},"heatmap-intensity":{type:"number",default:1,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!1,transition:!0},"heatmap-color":{type:"color",default:["interpolate",["linear"],["heatmap-density"],0,"rgba(0, 0, 255, 0)",.1,"royalblue",.3,"cyan",.5,"lime",.7,"yellow",1,"red"],function:"interpolated","zoom-function":!1,"property-function":!1,transition:!1},"heatmap-opacity":{type:"number",default:1,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,"property-function":!1,transition:!0}},paint_symbol:{"icon-opacity":{type:"number",default:1,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:["icon-image"]},"icon-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:["icon-image"]},"icon-halo-color":{type:"color",default:"rgba(0, 0, 0, 0)",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:["icon-image"]},"icon-halo-width":{type:"number",default:0,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels",requires:["icon-image"]},"icon-halo-blur":{type:"number",default:0,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels",requires:["icon-image"]},"icon-translate":{type:"array",value:"number",length:2,default:[0,0],function:"interpolated","zoom-function":!0,transition:!0,units:"pixels",requires:["icon-image"]},"icon-translate-anchor":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"map",requires:["icon-image","icon-translate"]},"text-opacity":{type:"number",default:1,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:["text-field"]},"text-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:["text-field"]},"text-halo-color":{type:"color",default:"rgba(0, 0, 0, 0)",function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,requires:["text-field"]},"text-halo-width":{type:"number",default:0,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels",requires:["text-field"]},"text-halo-blur":{type:"number",default:0,minimum:0,function:"interpolated","zoom-function":!0,"property-function":!0,transition:!0,units:"pixels",requires:["text-field"]},"text-translate":{type:"array",value:"number",length:2,default:[0,0],function:"interpolated","zoom-function":!0,transition:!0,units:"pixels",requires:["text-field"]},"text-translate-anchor":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"map",requires:["text-field","text-translate"]}},paint_raster:{"raster-opacity":{type:"number",default:1,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,transition:!0},"raster-hue-rotate":{type:"number",default:0,period:360,function:"interpolated","zoom-function":!0,transition:!0,units:"degrees"},"raster-brightness-min":{type:"number",function:"interpolated","zoom-function":!0,default:0,minimum:0,maximum:1,transition:!0},"raster-brightness-max":{type:"number",function:"interpolated","zoom-function":!0,default:1,minimum:0,maximum:1,transition:!0},"raster-saturation":{type:"number",default:0,minimum:-1,maximum:1,function:"interpolated","zoom-function":!0,transition:!0},"raster-contrast":{type:"number",default:0,minimum:-1,maximum:1,function:"interpolated","zoom-function":!0,transition:!0},"raster-fade-duration":{type:"number",default:300,minimum:0,function:"interpolated","zoom-function":!0,transition:!1,units:"milliseconds"}},paint_hillshade:{"hillshade-illumination-direction":{type:"number",default:335,minimum:0,maximum:359,function:"interpolated","zoom-function":!0,transition:!1},"hillshade-illumination-anchor":{type:"enum",function:"piecewise-constant","zoom-function":!0,values:{map:{},viewport:{}},default:"viewport"},"hillshade-exaggeration":{type:"number",default:.5,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,transition:!0},"hillshade-shadow-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,transition:!0},"hillshade-highlight-color":{type:"color",default:"#FFFFFF",function:"interpolated","zoom-function":!0,transition:!0},"hillshade-accent-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,transition:!0}},paint_background:{"background-color":{type:"color",default:"#000000",function:"interpolated","zoom-function":!0,transition:!0,requires:[{"!":"background-pattern"}]},"background-pattern":{type:"string",function:"piecewise-constant","zoom-function":!0,transition:!0},"background-opacity":{type:"number",default:1,minimum:0,maximum:1,function:"interpolated","zoom-function":!0,transition:!0}},transition:{duration:{type:"number",default:300,minimum:0,units:"milliseconds"},delay:{type:"number",default:0,minimum:0,units:"milliseconds"}}}},{}],153:[function(t,e,r){var n=t("csscolorparser").parseCSSColor,i=function(t,e,r,n){void 0===n&&(n=1),this.r=t,this.g=e,this.b=r,this.a=n};i.parse=function(t){if(t){if(t instanceof i)return t;if("string"==typeof t){var e=n(t);if(e)return new i(e[0]/255*e[3],e[1]/255*e[3],e[2]/255*e[3],e[3])}}},i.prototype.toString=function(){var t=this;return"rgba("+[this.r,this.g,this.b].map(function(e){return Math.round(255*e/t.a)}).concat(this.a).join(",")+")"},i.black=new i(0,0,0,1),i.white=new i(1,1,1,1),i.transparent=new i(0,0,0,0),e.exports=i},{csscolorparser:13}],154:[function(t,e,r){function n(t){return t>v?Math.pow(t,1/3):t/m+d}function i(t){return t>g?t*t*t:m*(t-d)}function a(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function o(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function s(t){var e=o(t.r),r=o(t.g),i=o(t.b),a=n((.4124564*e+.3575761*r+.1804375*i)/f),s=n((.2126729*e+.7151522*r+.072175*i)/h);return{l:116*s-16,a:500*(a-s),b:200*(s-n((.0193339*e+.119192*r+.9503041*i)/p)),alpha:t.a}}function l(t){var e=(t.l+16)/116,r=isNaN(t.a)?e:e+t.a/500,n=isNaN(t.b)?e:e-t.b/200;return e=h*i(e),r=f*i(r),n=p*i(n),new c(a(3.2404542*r-1.5371385*e-.4985314*n),a(-.969266*r+1.8760108*e+.041556*n),a(.0556434*r-.2040259*e+1.0572252*n),t.alpha)}var c=t("./color"),u=t("./interpolate").number,f=.95047,h=1,p=1.08883,d=4/29,g=6/29,m=3*g*g,v=g*g*g,y=Math.PI/180,x=180/Math.PI;e.exports={lab:{forward:s,reverse:l,interpolate:function(t,e,r){return{l:u(t.l,e.l,r),a:u(t.a,e.a,r),b:u(t.b,e.b,r),alpha:u(t.alpha,e.alpha,r)}}},hcl:{forward:function(t){var e=s(t),r=e.l,n=e.a,i=e.b,a=Math.atan2(i,n)*x;return{h:a<0?a+360:a,c:Math.sqrt(n*n+i*i),l:r,alpha:t.a}},reverse:function(t){var e=t.h*y,r=t.c;return l({l:t.l,a:Math.cos(e)*r,b:Math.sin(e)*r,alpha:t.alpha})},interpolate:function(t,e,r){return{h:function(t,e,r){var n=e-t;return t+r*(n>180||n<-180?n-360*Math.round(n/360):n)}(t.h,e.h,r),c:u(t.c,e.c,r),l:u(t.l,e.l,r),alpha:u(t.alpha,e.alpha,r)}}}}},{"./color":153,"./interpolate":158}],155:[function(t,e,r){e.exports=function t(e,r){if(Array.isArray(e)){if(!Array.isArray(r)||e.length!==r.length)return!1;for(var n=0;n0;)r[n]=e[n+1];for(var i=0,a=r;i":case">=":r.length>=2&&"$type"===s(r[1])&&u.push(new n(i,r,'"$type" cannot be use with operator "'+r[0]+'"'));case"==":case"!=":3!==r.length&&u.push(new n(i,r,'filter array for operator "'+r[0]+'" must have 3 elements'));case"in":case"!in":r.length>=2&&"string"!==(l=o(r[1]))&&u.push(new n(i+"[1]",r[1],"string expected, "+l+" found"));for(var f=2;fc(s[0].zoom))return[new n(u,s[0].zoom,"stop zoom values must appear in ascending order")];c(s[0].zoom)!==h&&(h=c(s[0].zoom),f=void 0,g={}),e=e.concat(o({key:u+"[0]",value:s[0],valueSpec:{zoom:{}},style:t.style,styleSpec:t.styleSpec,objectElementValidators:{zoom:l,value:r}}))}else e=e.concat(r({key:u+"[0]",value:s[0],valueSpec:{},style:t.style,styleSpec:t.styleSpec},s));return e.concat(a({key:u+"[1]",value:s[1],valueSpec:p,style:t.style,styleSpec:t.styleSpec}))}function r(t,e){var r=i(t.value),a=c(t.value),o=null!==t.value?t.value:e;if(u){if(r!==u)return[new n(t.key,o,r+" stop domain type must match previous stop domain type "+u)]}else u=r;if("number"!==r&&"string"!==r&&"boolean"!==r)return[new n(t.key,o,"stop domain value must be a number, string, or boolean")];if("number"!==r&&"categorical"!==d){var s="number expected, "+r+" found";return p["property-function"]&&void 0===d&&(s+='\nIf you intended to use a categorical function, specify `"type": "categorical"`.'),[new n(t.key,o,s)]}return"categorical"!==d||"number"!==r||isFinite(a)&&Math.floor(a)===a?"categorical"!==d&&"number"===r&&void 0!==f&&a=8&&(v&&!t.valueSpec["property-function"]?x.push(new n(t.key,t.value,"property functions not supported")):m&&!t.valueSpec["zoom-function"]&&"heatmap-color"!==t.objectKey&&x.push(new n(t.key,t.value,"zoom functions not supported"))),"categorical"!==d&&!y||void 0!==t.value.property||x.push(new n(t.key,t.value,'"property" property is required')),x}},{"../error/validation_error":122,"../util/get_type":157,"../util/unbundle_jsonlint":161,"./validate":162,"./validate_array":163,"./validate_number":175,"./validate_object":176}],171:[function(t,e,r){var n=t("../error/validation_error"),i=t("./validate_string");e.exports=function(t){var e=t.value,r=t.key,a=i(t);return a.length?a:(-1===e.indexOf("{fontstack}")&&a.push(new n(r,e,'"glyphs" url must include a "{fontstack}" token')),-1===e.indexOf("{range}")&&a.push(new n(r,e,'"glyphs" url must include a "{range}" token')),a)}},{"../error/validation_error":122,"./validate_string":180}],172:[function(t,e,r){var n=t("../error/validation_error"),i=t("../util/unbundle_jsonlint"),a=t("./validate_object"),o=t("./validate_filter"),s=t("./validate_paint_property"),l=t("./validate_layout_property"),c=t("./validate"),u=t("../util/extend");e.exports=function(t){var e=[],r=t.value,f=t.key,h=t.style,p=t.styleSpec;r.type||r.ref||e.push(new n(f,r,'either "type" or "ref" is required'));var d,g=i(r.type),m=i(r.ref);if(r.id)for(var v=i(r.id),y=0;ya.maximum?[new i(e,r,r+" is greater than the maximum value "+a.maximum)]:[]}},{"../error/validation_error":122,"../util/get_type":157}],176:[function(t,e,r){var n=t("../error/validation_error"),i=t("../util/get_type"),a=t("./validate");e.exports=function(t){var e=t.key,r=t.value,o=t.valueSpec||{},s=t.objectElementValidators||{},l=t.style,c=t.styleSpec,u=[],f=i(r);if("object"!==f)return[new n(e,r,"object expected, "+f+" found")];for(var h in r){var p=h.split(".")[0],d=o[p]||o["*"],g=void 0;if(s[p])g=s[p];else if(o[p])g=a;else if(s["*"])g=s["*"];else{if(!o["*"]){u.push(new n(e,r[h],'unknown property "'+h+'"'));continue}g=a}u=u.concat(g({key:(e?e+".":e)+h,value:r[h],valueSpec:d,style:l,styleSpec:c,object:r,objectKey:h},r))}for(var m in o)s[m]||o[m].required&&void 0===o[m].default&&void 0===r[m]&&u.push(new n(e,r,'missing required property "'+m+'"'));return u}},{"../error/validation_error":122,"../util/get_type":157,"./validate":162}],177:[function(t,e,r){var n=t("./validate_property");e.exports=function(t){return n(t,"paint")}},{"./validate_property":178}],178:[function(t,e,r){var n=t("./validate"),i=t("../error/validation_error"),a=t("../util/get_type"),o=t("../function").isFunction,s=t("../util/unbundle_jsonlint");e.exports=function(t,e){var r=t.key,l=t.style,c=t.styleSpec,u=t.value,f=t.objectKey,h=c[e+"_"+t.layerType];if(!h)return[];var p=f.match(/^(.*)-transition$/);if("paint"===e&&p&&h[p[1]]&&h[p[1]].transition)return n({key:r,value:u,valueSpec:c.transition,style:l,styleSpec:c});var d,g=t.valueSpec||h[f];if(!g)return[new i(r,u,'unknown property "'+f+'"')];if("string"===a(u)&&g["property-function"]&&!g.tokens&&(d=/^{([^}]+)}$/.exec(u)))return[new i(r,u,'"'+f+'" does not support interpolation syntax\nUse an identity property function instead: `{ "type": "identity", "property": '+JSON.stringify(d[1])+" }`.")];var m=[];return"symbol"===t.layerType&&("text-field"===f&&l&&!l.glyphs&&m.push(new i(r,u,'use of "text-field" requires a style "glyphs" property')),"text-font"===f&&o(s.deep(u))&&"identity"===s(u.type)&&m.push(new i(r,u,'"text-font" does not support identity functions'))),m.concat(n({key:t.key,value:u,valueSpec:g,style:l,styleSpec:c,expressionContext:"property",propertyKey:f}))}},{"../error/validation_error":122,"../function":149,"../util/get_type":157,"../util/unbundle_jsonlint":161,"./validate":162}],179:[function(t,e,r){var n=t("../error/validation_error"),i=t("../util/unbundle_jsonlint"),a=t("./validate_object"),o=t("./validate_enum");e.exports=function(t){var e=t.value,r=t.key,s=t.styleSpec,l=t.style;if(!e.type)return[new n(r,e,'"type" is required')];var c=i(e.type),u=[];switch(c){case"vector":case"raster":case"raster-dem":if(u=u.concat(a({key:r,value:e,valueSpec:s["source_"+c.replace("-","_")],style:t.style,styleSpec:s})),"url"in e)for(var f in e)["type","url","tileSize"].indexOf(f)<0&&u.push(new n(r+"."+f,e[f],'a source with a "url" property may not include a "'+f+'" property'));return u;case"geojson":return a({key:r,value:e,valueSpec:s.source_geojson,style:l,styleSpec:s});case"video":return a({key:r,value:e,valueSpec:s.source_video,style:l,styleSpec:s});case"image":return a({key:r,value:e,valueSpec:s.source_image,style:l,styleSpec:s});case"canvas":return a({key:r,value:e,valueSpec:s.source_canvas,style:l,styleSpec:s});default:return o({key:r+".type",value:e.type,valueSpec:{values:["vector","raster","raster-dem","geojson","video","image","canvas"]},style:l,styleSpec:s})}}},{"../error/validation_error":122,"../util/unbundle_jsonlint":161,"./validate_enum":167,"./validate_object":176}],180:[function(t,e,r){var n=t("../util/get_type"),i=t("../error/validation_error");e.exports=function(t){var e=t.value,r=t.key,a=n(e);return"string"!==a?[new i(r,e,"string expected, "+a+" found")]:[]}},{"../error/validation_error":122,"../util/get_type":157}],181:[function(t,e,r){function n(t,e){e=e||l;var r=[];return r=r.concat(s({key:"",value:t,valueSpec:e.$root,styleSpec:e,style:t,objectElementValidators:{glyphs:c,"*":function(){return[]}}})),t.constants&&(r=r.concat(o({key:"constants",value:t.constants,style:t,styleSpec:e}))),i(r)}function i(t){return[].concat(t).sort(function(t,e){return t.line-e.line})}function a(t){return function(){return i(t.apply(this,arguments))}}var o=t("./validate/validate_constants"),s=t("./validate/validate"),l=t("./reference/latest"),c=t("./validate/validate_glyphs_url");n.source=a(t("./validate/validate_source")),n.light=a(t("./validate/validate_light")),n.layer=a(t("./validate/validate_layer")),n.filter=a(t("./validate/validate_filter")),n.paintProperty=a(t("./validate/validate_paint_property")),n.layoutProperty=a(t("./validate/validate_layout_property")),e.exports=n},{"./reference/latest":151,"./validate/validate":162,"./validate/validate_constants":166,"./validate/validate_filter":169,"./validate/validate_glyphs_url":171,"./validate/validate_layer":172,"./validate/validate_layout_property":173,"./validate/validate_light":174,"./validate/validate_paint_property":177,"./validate/validate_source":179}],182:[function(t,e,r){var n=t("./zoom_history"),i=function(t,e){this.zoom=t,e?(this.now=e.now,this.fadeDuration=e.fadeDuration,this.zoomHistory=e.zoomHistory,this.transition=e.transition):(this.now=0,this.fadeDuration=0,this.zoomHistory=new n,this.transition={})};i.prototype.crossFadingFactor=function(){return 0===this.fadeDuration?1:Math.min((this.now-this.zoomHistory.lastIntegerZoomTime)/this.fadeDuration,1)},e.exports=i},{"./zoom_history":212}],183:[function(t,e,r){var n=t("../style-spec/reference/latest"),i=t("../util/util"),a=t("../util/evented"),o=t("./validate_style"),s=t("../util/util").sphericalToCartesian,l=(t("../style-spec/util/color"),t("../style-spec/util/interpolate")),c=t("./properties"),u=c.Properties,f=c.Transitionable,h=(c.Transitioning,c.PossiblyEvaluated,c.DataConstantProperty),p=function(){this.specification=n.light.position};p.prototype.possiblyEvaluate=function(t,e){return s(t.expression.evaluate(e))},p.prototype.interpolate=function(t,e,r){return{x:l.number(t.x,e.x,r),y:l.number(t.y,e.y,r),z:l.number(t.z,e.z,r)}};var d=new u({anchor:new h(n.light.anchor),position:new p,color:new h(n.light.color),intensity:new h(n.light.intensity)}),g=function(t){function e(e){t.call(this),this._transitionable=new f(d),this.setLight(e),this._transitioning=this._transitionable.untransitioned()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.getLight=function(){return this._transitionable.serialize()},e.prototype.setLight=function(t){if(!this._validate(o.light,t))for(var e in t){var r=t[e];i.endsWith(e,"-transition")?this._transitionable.setTransition(e.slice(0,-"-transition".length),r):this._transitionable.setValue(e,r)}},e.prototype.updateTransitions=function(t){this._transitioning=this._transitionable.transitioned(t,this._transitioning)},e.prototype.hasTransition=function(){return this._transitioning.hasTransition()},e.prototype.recalculate=function(t){this.properties=this._transitioning.possiblyEvaluate(t)},e.prototype._validate=function(t,e){return o.emitErrors(this,t.call(o,i.extend({value:e,style:{glyphs:!0,sprite:!0},styleSpec:n})))},e}(a);e.exports=g},{"../style-spec/reference/latest":151,"../style-spec/util/color":153,"../style-spec/util/interpolate":158,"../util/evented":260,"../util/util":275,"./properties":188,"./validate_style":211}],184:[function(t,e,r){var n=t("../util/mapbox").normalizeGlyphsURL,i=t("../util/ajax"),a=t("./parse_glyph_pbf");e.exports=function(t,e,r,o,s){var l=256*e,c=l+255,u=o(n(r).replace("{fontstack}",t).replace("{range}",l+"-"+c),i.ResourceType.Glyphs);i.getArrayBuffer(u,function(t,e){if(t)s(t);else if(e){for(var r={},n=0,i=a(e.data);n1?"@2x":"";n.getJSON(e(a(t,f,".json"),n.ResourceType.SpriteJSON),function(t,e){u||(u=t,l=e,s())}),n.getImage(e(a(t,f,".png"),n.ResourceType.SpriteImage),function(t,e){u||(u=t,c=e,s())})}},{"../util/ajax":251,"../util/browser":252,"../util/image":263,"../util/mapbox":267}],186:[function(t,e,r){function n(t,e,r){1===t&&r.readMessage(i,e)}function i(t,e,r){if(3===t){var n=r.readMessage(a,{}),i=n.id,s=n.bitmap,c=n.width,u=n.height,f=n.left,h=n.top,p=n.advance;e.push({id:i,bitmap:new o({width:c+2*l,height:u+2*l},s),metrics:{width:c,height:u,left:f,top:h,advance:p}})}}function a(t,e,r){1===t?e.id=r.readVarint():2===t?e.bitmap=r.readBytes():3===t?e.width=r.readVarint():4===t?e.height=r.readVarint():5===t?e.left=r.readSVarint():6===t?e.top=r.readSVarint():7===t&&(e.advance=r.readVarint())}var o=t("../util/image").AlphaImage,s=t("pbf"),l=3;e.exports=function(t){return new s(t).readFields(n,[])},e.exports.GLYPH_PBF_BORDER=l},{"../util/image":263,pbf:30}],187:[function(t,e,r){var n=t("../util/browser"),i=t("../symbol/placement"),a=function(){this._currentTileIndex=0,this._seenCrossTileIDs={}};a.prototype.continuePlacement=function(t,e,r,n,i){for(var a=this;this._currentTileIndex2};this._currentPlacementIndex>=0;){var l=e[t[i._currentPlacementIndex]],c=i.placement.collisionIndex.transform.zoom;if("symbol"===l.type&&(!l.minzoom||l.minzoom<=c)&&(!l.maxzoom||l.maxzoom>c)){if(i._inProgressLayer||(i._inProgressLayer=new a),i._inProgressLayer.continuePlacement(r[l.source],i.placement,i._showCollisionBoxes,l,s))return;delete i._inProgressLayer}i._currentPlacementIndex--}this._done=!0},o.prototype.commit=function(t,e){return this.placement.commit(t,e),this.placement},e.exports=o},{"../symbol/placement":223,"../util/browser":252}],188:[function(t,e,r){var n=t("../util/util"),i=n.clone,a=n.extend,o=n.easeCubicInOut,s=t("../style-spec/util/interpolate"),l=t("../style-spec/expression").normalizePropertyExpression,c=(t("../style-spec/util/color"),t("../util/web_worker_transfer").register),u=function(t,e){this.property=t,this.value=e,this.expression=l(void 0===e?t.specification.default:e,t.specification)};u.prototype.isDataDriven=function(){return"source"===this.expression.kind||"composite"===this.expression.kind},u.prototype.possiblyEvaluate=function(t){return this.property.possiblyEvaluate(this,t)};var f=function(t){this.property=t,this.value=new u(t,void 0)};f.prototype.transitioned=function(t,e){return new p(this.property,this.value,e,a({},t.transition,this.transition),t.now)},f.prototype.untransitioned=function(){return new p(this.property,this.value,null,{},0)};var h=function(t){this._properties=t,this._values=Object.create(t.defaultTransitionablePropertyValues)};h.prototype.getValue=function(t){return i(this._values[t].value.value)},h.prototype.setValue=function(t,e){this._values.hasOwnProperty(t)||(this._values[t]=new f(this._values[t].property)),this._values[t].value=new u(this._values[t].property,null===e?void 0:i(e))},h.prototype.getTransition=function(t){return i(this._values[t].transition)},h.prototype.setTransition=function(t,e){this._values.hasOwnProperty(t)||(this._values[t]=new f(this._values[t].property)),this._values[t].transition=i(e)||void 0},h.prototype.serialize=function(){for(var t=this,e={},r=0,n=Object.keys(t._values);rthis.end)return this.prior=null,r;if(this.value.isDataDriven())return this.prior=null,r;if(en.zoomHistory.lastIntegerZoom?{from:t,to:e,fromScale:2,toScale:1,t:a+(1-a)*o}:{from:r,to:e,fromScale:.5,toScale:1,t:1-(1-o)*a}},b.prototype.interpolate=function(t){return t};var _=function(t){this.specification=t};_.prototype.possiblyEvaluate=function(){},_.prototype.interpolate=function(){};c("DataDrivenProperty",x),c("DataConstantProperty",y),c("CrossFadedProperty",b),c("HeatmapColorProperty",_),e.exports={PropertyValue:u,Transitionable:h,Transitioning:d,Layout:g,PossiblyEvaluatedPropertyValue:m,PossiblyEvaluated:v,DataConstantProperty:y,DataDrivenProperty:x,CrossFadedProperty:b,HeatmapColorProperty:_,Properties:function(t){var e=this;for(var r in this.properties=t,this.defaultPropertyValues={},this.defaultTransitionablePropertyValues={},this.defaultTransitioningPropertyValues={},this.defaultPossiblyEvaluatedValues={},t){var n=t[r],i=e.defaultPropertyValues[r]=new u(n,void 0),a=e.defaultTransitionablePropertyValues[r]=new f(n);e.defaultTransitioningPropertyValues[r]=a.untransitioned(),e.defaultPossiblyEvaluatedValues[r]=i.possiblyEvaluate({})}}}},{"../style-spec/expression":139,"../style-spec/util/color":153,"../style-spec/util/interpolate":158,"../util/util":275,"../util/web_worker_transfer":278}],189:[function(t,e,r){var n=t("@mapbox/point-geometry");e.exports={getMaximumPaintValue:function(t,e,r){var n=e.paint.get(t).value;return"constant"===n.kind?n.value:r.programConfigurations.get(e.id).binders[t].statistics.max},translateDistance:function(t){return Math.sqrt(t[0]*t[0]+t[1]*t[1])},translate:function(t,e,r,i,a){if(!e[0]&&!e[1])return t;var o=n.convert(e);"viewport"===r&&o._rotate(-i);for(var s=[],l=0;l0)throw new Error("Unimplemented: "+n.map(function(t){return t.command}).join(", ")+".");return r.forEach(function(t){"setTransition"!==t.command&&e[t.command].apply(e,t.args)}),this.stylesheet=t,!0},e.prototype.addImage=function(t,e){if(this.getImage(t))return this.fire("error",{error:new Error("An image with this name already exists.")});this.imageManager.addImage(t,e),this.fire("data",{dataType:"style"})},e.prototype.getImage=function(t){return this.imageManager.getImage(t)},e.prototype.removeImage=function(t){if(!this.getImage(t))return this.fire("error",{error:new Error("No image with this name exists.")});this.imageManager.removeImage(t),this.fire("data",{dataType:"style"})},e.prototype.addSource=function(t,e,r){var n=this;if(this._checkLoaded(),void 0!==this.sourceCaches[t])throw new Error("There is already a source with this ID");if(!e.type)throw new Error("The type property must be defined, but the only the following properties were given: "+Object.keys(e).join(", ")+".");if(!(["vector","raster","geojson","video","image","canvas"].indexOf(e.type)>=0&&this._validate(g.source,"sources."+t,e,null,r))){this.map&&this.map._collectResourceTiming&&(e.collectResourceTiming=!0);var i=this.sourceCaches[t]=new x(t,e,this.dispatcher);i.style=this,i.setEventedParent(this,function(){return{isSourceLoaded:n.loaded(),source:i.serialize(),sourceId:t}}),i.onAdd(this.map),this._changed=!0}},e.prototype.removeSource=function(t){var e=this;if(this._checkLoaded(),void 0===this.sourceCaches[t])throw new Error("There is no source with this ID");for(var r in e._layers)if(e._layers[r].source===t)return e.fire("error",{error:new Error('Source "'+t+'" cannot be removed while layer "'+r+'" is using it.')});var n=this.sourceCaches[t];delete this.sourceCaches[t],delete this._updatedSources[t],n.fire("data",{sourceDataType:"metadata",dataType:"source",sourceId:t}),n.setEventedParent(null),n.clearTiles(),n.onRemove&&n.onRemove(this.map),this._changed=!0},e.prototype.setGeoJSONSourceData=function(t,e){this._checkLoaded(),this.sourceCaches[t].getSource().setData(e),this._changed=!0},e.prototype.getSource=function(t){return this.sourceCaches[t]&&this.sourceCaches[t].getSource()},e.prototype.addLayer=function(t,e,r){this._checkLoaded();var n=t.id;if("object"==typeof t.source&&(this.addSource(n,t.source),t=u.clone(t),t=u.extend(t,{source:n})),!this._validate(g.layer,"layers."+n,t,{arrayIndex:-1},r)){var a=i.create(t);this._validateLayer(a),a.setEventedParent(this,{layer:{id:n}});var o=e?this._order.indexOf(e):this._order.length;if(e&&-1===o)return void this.fire("error",{error:new Error('Layer with id "'+e+'" does not exist on this map.')});if(this._order.splice(o,0,n),this._layerOrderChanged=!0,this._layers[n]=a,this._removedLayers[n]&&a.source){var s=this._removedLayers[n];delete this._removedLayers[n],s.type!==a.type?this._updatedSources[a.source]="clear":(this._updatedSources[a.source]="reload",this.sourceCaches[a.source].pause())}this._updateLayer(a)}},e.prototype.moveLayer=function(t,e){if(this._checkLoaded(),this._changed=!0,this._layers[t]){var r=this._order.indexOf(t);this._order.splice(r,1);var n=e?this._order.indexOf(e):this._order.length;e&&-1===n?this.fire("error",{error:new Error('Layer with id "'+e+'" does not exist on this map.')}):(this._order.splice(n,0,t),this._layerOrderChanged=!0)}else this.fire("error",{error:new Error("The layer '"+t+"' does not exist in the map's style and cannot be moved.")})},e.prototype.removeLayer=function(t){this._checkLoaded();var e=this._layers[t];if(e){e.setEventedParent(null);var r=this._order.indexOf(t);this._order.splice(r,1),this._layerOrderChanged=!0,this._changed=!0,this._removedLayers[t]=e,delete this._layers[t],delete this._updatedLayers[t],delete this._updatedPaintProps[t]}else this.fire("error",{error:new Error("The layer '"+t+"' does not exist in the map's style and cannot be removed.")})},e.prototype.getLayer=function(t){return this._layers[t]},e.prototype.setLayerZoomRange=function(t,e,r){this._checkLoaded();var n=this.getLayer(t);n?n.minzoom===e&&n.maxzoom===r||(null!=e&&(n.minzoom=e),null!=r&&(n.maxzoom=r),this._updateLayer(n)):this.fire("error",{error:new Error("The layer '"+t+"' does not exist in the map's style and cannot have zoom extent.")})},e.prototype.setFilter=function(t,e){this._checkLoaded();var r=this.getLayer(t);if(r)return u.deepEqual(r.filter,e)?void 0:null==e?(r.filter=void 0,void this._updateLayer(r)):void(this._validate(g.filter,"layers."+r.id+".filter",e)||(r.filter=u.clone(e),this._updateLayer(r)));this.fire("error",{error:new Error("The layer '"+t+"' does not exist in the map's style and cannot be filtered.")})},e.prototype.getFilter=function(t){return u.clone(this.getLayer(t).filter)},e.prototype.setLayoutProperty=function(t,e,r){this._checkLoaded();var n=this.getLayer(t);n?u.deepEqual(n.getLayoutProperty(e),r)||(n.setLayoutProperty(e,r),this._updateLayer(n)):this.fire("error",{error:new Error("The layer '"+t+"' does not exist in the map's style and cannot be styled.")})},e.prototype.getLayoutProperty=function(t,e){return this.getLayer(t).getLayoutProperty(e)},e.prototype.setPaintProperty=function(t,e,r){this._checkLoaded();var n=this.getLayer(t);if(n){if(!u.deepEqual(n.getPaintProperty(e),r)){var i=n._transitionablePaint._values[e].value.isDataDriven();n.setPaintProperty(e,r),(n._transitionablePaint._values[e].value.isDataDriven()||i)&&this._updateLayer(n),this._changed=!0,this._updatedPaintProps[t]=!0}}else this.fire("error",{error:new Error("The layer '"+t+"' does not exist in the map's style and cannot be styled.")})},e.prototype.getPaintProperty=function(t,e){return this.getLayer(t).getPaintProperty(e)},e.prototype.getTransition=function(){return u.extend({duration:300,delay:0},this.stylesheet&&this.stylesheet.transition)},e.prototype.serialize=function(){var t=this;return u.filterObject({version:this.stylesheet.version,name:this.stylesheet.name,metadata:this.stylesheet.metadata,light:this.stylesheet.light,center:this.stylesheet.center,zoom:this.stylesheet.zoom,bearing:this.stylesheet.bearing,pitch:this.stylesheet.pitch,sprite:this.stylesheet.sprite,glyphs:this.stylesheet.glyphs,transition:this.stylesheet.transition,sources:u.mapObject(this.sourceCaches,function(t){return t.serialize()}),layers:this._order.map(function(e){return t._layers[e].serialize()})},function(t){return void 0!==t})},e.prototype._updateLayer=function(t){this._updatedLayers[t.id]=!0,t.source&&!this._updatedSources[t.source]&&(this._updatedSources[t.source]="reload",this.sourceCaches[t.source].pause()),this._changed=!0},e.prototype._flattenRenderedFeatures=function(t){for(var e=[],r=this._order.length-1;r>=0;r--)for(var n=this._order[r],i=0,a=t;i=this.maxzoom)||"none"===this.visibility},e.prototype.updateTransitions=function(t){this._transitioningPaint=this._transitionablePaint.transitioned(t,this._transitioningPaint)},e.prototype.hasTransition=function(){return this._transitioningPaint.hasTransition()},e.prototype.recalculate=function(t){this._unevaluatedLayout&&(this.layout=this._unevaluatedLayout.possiblyEvaluate(t)),this.paint=this._transitioningPaint.possiblyEvaluate(t)},e.prototype.serialize=function(){var t={id:this.id,type:this.type,source:this.source,"source-layer":this.sourceLayer,metadata:this.metadata,minzoom:this.minzoom,maxzoom:this.maxzoom,filter:this.filter,layout:this._unevaluatedLayout&&this._unevaluatedLayout.serialize(),paint:this._transitionablePaint&&this._transitionablePaint.serialize()};return"none"===this.visibility&&(t.layout=t.layout||{},t.layout.visibility="none"),n.filterObject(t,function(t,e){return!(void 0===t||"layout"===e&&!Object.keys(t).length||"paint"===e&&!Object.keys(t).length)})},e.prototype._validate=function(t,e,r,n,o){return(!o||!1!==o.validate)&&a.emitErrors(this,t.call(a,{key:e,layerType:this.type,objectKey:r,value:n,styleSpec:i,style:{glyphs:!0,sprite:!0}}))},e.prototype.hasOffscreenPass=function(){return!1},e.prototype.resize=function(){},e}(o));e.exports=u;var f={circle:t("./style_layer/circle_style_layer"),heatmap:t("./style_layer/heatmap_style_layer"),hillshade:t("./style_layer/hillshade_style_layer"),fill:t("./style_layer/fill_style_layer"),"fill-extrusion":t("./style_layer/fill_extrusion_style_layer"),line:t("./style_layer/line_style_layer"),symbol:t("./style_layer/symbol_style_layer"),background:t("./style_layer/background_style_layer"),raster:t("./style_layer/raster_style_layer")};u.create=function(t){return new f[t.type](t)}},{"../style-spec/reference/latest":151,"../util/evented":260,"../util/util":275,"./properties":188,"./style_layer/background_style_layer":192,"./style_layer/circle_style_layer":194,"./style_layer/fill_extrusion_style_layer":196,"./style_layer/fill_style_layer":198,"./style_layer/heatmap_style_layer":200,"./style_layer/hillshade_style_layer":202,"./style_layer/line_style_layer":204,"./style_layer/raster_style_layer":206,"./style_layer/symbol_style_layer":208,"./validate_style":211}],192:[function(t,e,r){var n=t("../style_layer"),i=t("./background_style_layer_properties"),a=t("../properties"),o=(a.Transitionable,a.Transitioning,a.PossiblyEvaluated,function(t){function e(e){t.call(this,e,i)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(n));e.exports=o},{"../properties":188,"../style_layer":191,"./background_style_layer_properties":193}],193:[function(t,e,r){var n=t("../../style-spec/reference/latest"),i=t("../properties"),a=i.Properties,o=i.DataConstantProperty,s=(i.DataDrivenProperty,i.CrossFadedProperty),l=(i.HeatmapColorProperty,new a({"background-color":new o(n.paint_background["background-color"]),"background-pattern":new s(n.paint_background["background-pattern"]),"background-opacity":new o(n.paint_background["background-opacity"])}));e.exports={paint:l}},{"../../style-spec/reference/latest":151,"../properties":188}],194:[function(t,e,r){var n=t("../style_layer"),i=t("../../data/bucket/circle_bucket"),a=t("../../util/intersection_tests").multiPolygonIntersectsBufferedMultiPoint,o=t("../query_utils"),s=o.getMaximumPaintValue,l=o.translateDistance,c=o.translate,u=t("./circle_style_layer_properties"),f=t("../properties"),h=(f.Transitionable,f.Transitioning,f.PossiblyEvaluated,function(t){function e(e){t.call(this,e,u)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.createBucket=function(t){return new i(t)},e.prototype.queryRadius=function(t){var e=t;return s("circle-radius",this,e)+s("circle-stroke-width",this,e)+l(this.paint.get("circle-translate"))},e.prototype.queryIntersectsFeature=function(t,e,r,n,i,o){var s=c(t,this.paint.get("circle-translate"),this.paint.get("circle-translate-anchor"),i,o),l=this.paint.get("circle-radius").evaluate(e)*o,u=this.paint.get("circle-stroke-width").evaluate(e)*o;return a(s,r,l+u)},e}(n));e.exports=h},{"../../data/bucket/circle_bucket":42,"../../util/intersection_tests":264,"../properties":188,"../query_utils":189,"../style_layer":191,"./circle_style_layer_properties":195}],195:[function(t,e,r){var n=t("../../style-spec/reference/latest"),i=t("../properties"),a=i.Properties,o=i.DataConstantProperty,s=i.DataDrivenProperty,l=(i.CrossFadedProperty,i.HeatmapColorProperty,new a({"circle-radius":new s(n.paint_circle["circle-radius"]),"circle-color":new s(n.paint_circle["circle-color"]),"circle-blur":new s(n.paint_circle["circle-blur"]),"circle-opacity":new s(n.paint_circle["circle-opacity"]),"circle-translate":new o(n.paint_circle["circle-translate"]),"circle-translate-anchor":new o(n.paint_circle["circle-translate-anchor"]),"circle-pitch-scale":new o(n.paint_circle["circle-pitch-scale"]),"circle-pitch-alignment":new o(n.paint_circle["circle-pitch-alignment"]),"circle-stroke-width":new s(n.paint_circle["circle-stroke-width"]),"circle-stroke-color":new s(n.paint_circle["circle-stroke-color"]),"circle-stroke-opacity":new s(n.paint_circle["circle-stroke-opacity"])}));e.exports={paint:l}},{"../../style-spec/reference/latest":151,"../properties":188}],196:[function(t,e,r){var n=t("../style_layer"),i=t("../../data/bucket/fill_extrusion_bucket"),a=t("../../util/intersection_tests").multiPolygonIntersectsMultiPolygon,o=t("../query_utils"),s=o.translateDistance,l=o.translate,c=t("./fill_extrusion_style_layer_properties"),u=t("../properties"),f=(u.Transitionable,u.Transitioning,u.PossiblyEvaluated,function(t){function e(e){t.call(this,e,c)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.createBucket=function(t){return new i(t)},e.prototype.queryRadius=function(){return s(this.paint.get("fill-extrusion-translate"))},e.prototype.queryIntersectsFeature=function(t,e,r,n,i,o){var s=l(t,this.paint.get("fill-extrusion-translate"),this.paint.get("fill-extrusion-translate-anchor"),i,o);return a(s,r)},e.prototype.hasOffscreenPass=function(){return 0!==this.paint.get("fill-extrusion-opacity")&&"none"!==this.visibility},e.prototype.resize=function(){this.viewportFrame&&(this.viewportFrame.destroy(),this.viewportFrame=null)},e}(n));e.exports=f},{"../../data/bucket/fill_extrusion_bucket":46,"../../util/intersection_tests":264,"../properties":188,"../query_utils":189,"../style_layer":191,"./fill_extrusion_style_layer_properties":197}],197:[function(t,e,r){var n=t("../../style-spec/reference/latest"),i=t("../properties"),a=i.Properties,o=i.DataConstantProperty,s=i.DataDrivenProperty,l=i.CrossFadedProperty,c=(i.HeatmapColorProperty,new a({"fill-extrusion-opacity":new o(n["paint_fill-extrusion"]["fill-extrusion-opacity"]),"fill-extrusion-color":new s(n["paint_fill-extrusion"]["fill-extrusion-color"]),"fill-extrusion-translate":new o(n["paint_fill-extrusion"]["fill-extrusion-translate"]),"fill-extrusion-translate-anchor":new o(n["paint_fill-extrusion"]["fill-extrusion-translate-anchor"]),"fill-extrusion-pattern":new l(n["paint_fill-extrusion"]["fill-extrusion-pattern"]),"fill-extrusion-height":new s(n["paint_fill-extrusion"]["fill-extrusion-height"]),"fill-extrusion-base":new s(n["paint_fill-extrusion"]["fill-extrusion-base"])}));e.exports={paint:c}},{"../../style-spec/reference/latest":151,"../properties":188}],198:[function(t,e,r){var n=t("../style_layer"),i=t("../../data/bucket/fill_bucket"),a=t("../../util/intersection_tests").multiPolygonIntersectsMultiPolygon,o=t("../query_utils"),s=o.translateDistance,l=o.translate,c=t("./fill_style_layer_properties"),u=t("../properties"),f=(u.Transitionable,u.Transitioning,u.PossiblyEvaluated,function(t){function e(e){t.call(this,e,c)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.recalculate=function(t){this.paint=this._transitioningPaint.possiblyEvaluate(t),void 0===this._transitionablePaint.getValue("fill-outline-color")&&(this.paint._values["fill-outline-color"]=this.paint._values["fill-color"])},e.prototype.createBucket=function(t){return new i(t)},e.prototype.queryRadius=function(){return s(this.paint.get("fill-translate"))},e.prototype.queryIntersectsFeature=function(t,e,r,n,i,o){var s=l(t,this.paint.get("fill-translate"),this.paint.get("fill-translate-anchor"),i,o);return a(s,r)},e}(n));e.exports=f},{"../../data/bucket/fill_bucket":44,"../../util/intersection_tests":264,"../properties":188,"../query_utils":189,"../style_layer":191,"./fill_style_layer_properties":199}],199:[function(t,e,r){var n=t("../../style-spec/reference/latest"),i=t("../properties"),a=i.Properties,o=i.DataConstantProperty,s=i.DataDrivenProperty,l=i.CrossFadedProperty,c=(i.HeatmapColorProperty,new a({"fill-antialias":new o(n.paint_fill["fill-antialias"]),"fill-opacity":new s(n.paint_fill["fill-opacity"]),"fill-color":new s(n.paint_fill["fill-color"]),"fill-outline-color":new s(n.paint_fill["fill-outline-color"]),"fill-translate":new o(n.paint_fill["fill-translate"]),"fill-translate-anchor":new o(n.paint_fill["fill-translate-anchor"]),"fill-pattern":new l(n.paint_fill["fill-pattern"])}));e.exports={paint:c}},{"../../style-spec/reference/latest":151,"../properties":188}],200:[function(t,e,r){var n=t("../style_layer"),i=t("../../data/bucket/heatmap_bucket"),a=t("../../util/image").RGBAImage,o=t("./heatmap_style_layer_properties"),s=t("../properties"),l=(s.Transitionable,s.Transitioning,s.PossiblyEvaluated,function(t){function e(e){t.call(this,e,o),this._updateColorRamp()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.createBucket=function(t){return new i(t)},e.prototype.setPaintProperty=function(e,r,n){t.prototype.setPaintProperty.call(this,e,r,n),"heatmap-color"===e&&this._updateColorRamp()},e.prototype._updateColorRamp=function(){for(var t=this._transitionablePaint._values["heatmap-color"].value.expression,e=new Uint8Array(1024),r=e.length,n=4;n0?e+2*t:t}var i=t("@mapbox/point-geometry"),a=t("../style_layer"),o=t("../../data/bucket/line_bucket"),s=t("../../util/intersection_tests").multiPolygonIntersectsBufferedMultiLine,l=t("../query_utils"),c=l.getMaximumPaintValue,u=l.translateDistance,f=l.translate,h=t("./line_style_layer_properties"),p=t("../../util/util").extend,d=t("../evaluation_parameters"),g=t("../properties"),m=(g.Transitionable,g.Transitioning,g.Layout,g.PossiblyEvaluated,new(function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.possiblyEvaluate=function(e,r){return r=new d(Math.floor(r.zoom),{now:r.now,fadeDuration:r.fadeDuration,zoomHistory:r.zoomHistory,transition:r.transition}),t.prototype.possiblyEvaluate.call(this,e,r)},e.prototype.evaluate=function(e,r,n){return r=p({},r,{zoom:Math.floor(r.zoom)}),t.prototype.evaluate.call(this,e,r,n)},e}(g.DataDrivenProperty))(h.paint.properties["line-width"].specification));m.useIntegerZoom=!0;var v=function(t){function e(e){t.call(this,e,h)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.recalculate=function(e){t.prototype.recalculate.call(this,e),this.paint._values["line-floorwidth"]=m.possiblyEvaluate(this._transitioningPaint._values["line-width"].value,e)},e.prototype.createBucket=function(t){return new o(t)},e.prototype.queryRadius=function(t){var e=t,r=n(c("line-width",this,e),c("line-gap-width",this,e)),i=c("line-offset",this,e);return r/2+Math.abs(i)+u(this.paint.get("line-translate"))},e.prototype.queryIntersectsFeature=function(t,e,r,a,o,l){var c=f(t,this.paint.get("line-translate"),this.paint.get("line-translate-anchor"),o,l),u=l/2*n(this.paint.get("line-width").evaluate(e),this.paint.get("line-gap-width").evaluate(e)),h=this.paint.get("line-offset").evaluate(e);return h&&(r=function(t,e){for(var r=[],n=new i(0,0),a=0;ar?(this.lastIntegerZoom=r+1,this.lastIntegerZoomTime=e):this.lastFloorZoom-r/2;){if(--o<0)return!1;s-=t[o].dist(a),a=t[o]}s+=t[o].dist(t[o+1]),o++;for(var l=[],c=0;sn;)c-=l.shift().angleDelta;if(c>i)return!1;o++,s+=f.dist(h)}return!0}},{}],215:[function(t,e,r){var n=t("@mapbox/point-geometry");e.exports=function(t,e,r,i,a){for(var o=[],s=0;s=i&&h.x>=i||(f.x>=i?f=new n(i,f.y+(h.y-f.y)*((i-f.x)/(h.x-f.x)))._round():h.x>=i&&(h=new n(i,f.y+(h.y-f.y)*((i-f.x)/(h.x-f.x)))._round()),f.y>=a&&h.y>=a||(f.y>=a?f=new n(f.x+(h.x-f.x)*((a-f.y)/(h.y-f.y)),a)._round():h.y>=a&&(h=new n(f.x+(h.x-f.x)*((a-f.y)/(h.y-f.y)),a)._round()),c&&f.equals(c[c.length-1])||(c=[f],o.push(c)),c.push(h)))))}return o}},{"@mapbox/point-geometry":4}],216:[function(t,e,r){var n=function(t,e,r,n,i,a,o,s,l,c,u){var f=o.top*s-l,h=o.bottom*s+l,p=o.left*s-l,d=o.right*s+l;if(this.boxStartIndex=t.length,c){var g=h-f,m=d-p;g>0&&(g=Math.max(10*s,g),this._addLineCollisionCircles(t,e,r,r.segment,m,g,n,i,a,u))}else t.emplaceBack(r.x,r.y,p,f,d,h,n,i,a,0,0);this.boxEndIndex=t.length};n.prototype._addLineCollisionCircles=function(t,e,r,n,i,a,o,s,l,c){var u=a/2,f=Math.floor(i/u),h=1+.4*Math.log(c)/Math.LN2,p=Math.floor(f*h/2),d=-a/2,g=r,m=n+1,v=d,y=-i/2,x=y-i/4;do{if(--m<0){if(v>y)return;m=0;break}v-=e[m].dist(g),g=e[m]}while(v>x);for(var b=e[m].dist(e[m+1]),_=-p;_i&&(k+=w-i),!(k=e.length)return;b=e[m].dist(e[m+1])}var M=k-v,A=e[m],T=e[m+1].sub(A)._unit()._mult(M)._add(A)._round(),S=Math.abs(k-d)L)n(t,z,!1);else{var R=m.projectPoint(h,P,D),B=O*S;if(v.length>0){var F=R.x-v[v.length-4],N=R.y-v[v.length-3];if(B*B*2>F*F+N*N&&z+8-E&&j=this.screenRightBoundary||n<100||e>this.screenBottomBoundary},e.exports=l},{"../symbol/projection":224,"../util/intersection_tests":264,"./grid_index":220,"@mapbox/gl-matrix":2,"@mapbox/point-geometry":4}],218:[function(t,e,r){var n=t("../data/extent"),i=512/n/2,a=function(t,e,r){var n=this;this.tileID=t,this.indexedSymbolInstances={},this.bucketInstanceId=r;for(var i=0,a=e;it.overscaledZ)for(var c in l){var u=l[c];u.tileID.isChildOf(t)&&u.findMatches(e.symbolInstances,t,o)}else{var f=l[t.scaledTo(Number(s)).key];f&&f.findMatches(e.symbolInstances,t,o)}}for(var h=0,p=e.symbolInstances;h=0&&A=0&&T=0&&v+p<=d){var S=new i(A,T,k,x);S._round(),s&&!a(e,S,c,s,l)||y.push(S)}}m+=w}return f||y.length||u||(y=t(e,m/2,o,s,l,c,u,!0,h)),y}(t,d?e/2*u%e:(p/2+2*l)*c*u%e,e,h,r,p*c,d,!1,f)}},{"../style-spec/util/interpolate":158,"../symbol/anchor":213,"./check_max_angle":214}],220:[function(t,e,r){var n=function(t,e,r){var n=this.boxCells=[],i=this.circleCells=[];this.xCellCount=Math.ceil(t/r),this.yCellCount=Math.ceil(e/r);for(var a=0;athis.width||n<0||e>this.height)return!i&&[];var a=[];if(t<=0&&e<=0&&this.width<=r&&this.height<=n)a=Array.prototype.slice.call(this.boxKeys).concat(this.circleKeys);else{var o={hitTest:i,seenUids:{box:{},circle:{}}};this._forEachCell(t,e,r,n,this._queryCell,a,o)}return i?a.length>0:a},n.prototype._queryCircle=function(t,e,r,n){var i=t-r,a=t+r,o=e-r,s=e+r;if(a<0||i>this.width||s<0||o>this.height)return!n&&[];var l=[],c={hitTest:n,circle:{x:t,y:e,radius:r},seenUids:{box:{},circle:{}}};return this._forEachCell(i,o,a,s,this._queryCellCircle,l,c),n?l.length>0:l},n.prototype.query=function(t,e,r,n){return this._query(t,e,r,n,!1)},n.prototype.hitTest=function(t,e,r,n){return this._query(t,e,r,n,!0)},n.prototype.hitTestCircle=function(t,e,r){return this._queryCircle(t,e,r,!0)},n.prototype._queryCell=function(t,e,r,n,i,a,o){var s=this,l=o.seenUids,c=this.boxCells[i];if(null!==c)for(var u=this.bboxes,f=0,h=c;f=u[d+0]&&n>=u[d+1]){if(o.hitTest)return a.push(!0),!0;a.push(s.boxKeys[p])}}}var g=this.circleCells[i];if(null!==g)for(var m=this.circles,v=0,y=g;vo*o+s*s},n.prototype._circleAndRectCollide=function(t,e,r,n,i,a,o){var s=(a-n)/2,l=Math.abs(t-(n+s));if(l>s+r)return!1;var c=(o-i)/2,u=Math.abs(e-(i+c));if(u>c+r)return!1;if(l<=s||u<=c)return!0;var f=l-s,h=u-c;return f*f+h*h<=r*r},e.exports=n},{}],221:[function(t,e,r){e.exports=function(t){function e(e){s.push(t[e]),l++}function r(t,e,r){var n=o[t];return delete o[t],o[e]=n,s[n].geometry[0].pop(),s[n].geometry[0]=s[n].geometry[0].concat(r[0]),n}function n(t,e,r){var n=a[e];return delete a[e],a[t]=n,s[n].geometry[0].shift(),s[n].geometry[0]=r[0].concat(s[n].geometry[0]),n}function i(t,e,r){var n=r?e[0][e[0].length-1]:e[0][0];return t+":"+n.x+":"+n.y}for(var a={},o={},s=[],l=0,c=0;c0,M=M&&A.offscreen);var C=_.collisionArrays.textCircles;if(C){var E=t.text.placedSymbolArray.get(_.placedTextSymbolIndices[0]),L=s.evaluateSizeForFeature(t.textSizeData,m,E);T=d.collisionIndex.placeCollisionCircles(C,g.get("text-allow-overlap"),i,a,_.key,E,t.lineVertexArray,t.glyphOffsetArray,L,e,r,o,"map"===g.get("text-pitch-alignment")),w=g.get("text-allow-overlap")||T.circles.length>0,M=M&&T.offscreen}_.collisionArrays.iconBox&&(k=(S=d.collisionIndex.placeCollisionBox(_.collisionArrays.iconBox,g.get("icon-allow-overlap"),a,e)).box.length>0,M=M&&S.offscreen),v||y?y?v||(k=k&&w):w=k&&w:k=w=k&&w,w&&A&&d.collisionIndex.insertCollisionBox(A.box,g.get("text-ignore-placement"),f,h,t.bucketInstanceId,_.textBoxStartIndex),k&&S&&d.collisionIndex.insertCollisionBox(S.box,g.get("icon-ignore-placement"),f,h,t.bucketInstanceId,_.iconBoxStartIndex),w&&T&&d.collisionIndex.insertCollisionCircles(T.circles,g.get("text-ignore-placement"),f,h,t.bucketInstanceId,_.textBoxStartIndex),d.placements[_.crossTileID]=new p(w,k,M||t.justReloaded),l[_.crossTileID]=!0}}t.justReloaded=!1},d.prototype.commit=function(t,e){var r=this;this.commitTime=e;var n=!1,i=t&&0!==this.fadeDuration?(this.commitTime-t.commitTime)/this.fadeDuration:1,a=t?t.opacities:{};for(var o in r.placements){var s=r.placements[o],l=a[o];l?(r.opacities[o]=new h(l,i,s.text,s.icon),n=n||s.text!==l.text.placed||s.icon!==l.icon.placed):(r.opacities[o]=new h(null,i,s.text,s.icon,s.skipFade),n=n||s.text||s.icon)}for(var c in a){var u=a[c];if(!r.opacities[c]){var f=new h(u,i,!1,!1);f.isHidden()||(r.opacities[c]=f,n=n||u.text.placed||u.icon.placed)}}n?this.lastPlacementChangeTime=e:"number"!=typeof this.lastPlacementChangeTime&&(this.lastPlacementChangeTime=t?t.lastPlacementChangeTime:e)},d.prototype.updateLayerOpacities=function(t,e){for(var r={},n=0,i=e;n0||l.numVerticalGlyphVertices>0,p=l.numIconVertices>0;if(f){for(var d=i(u.text),g=(l.numGlyphVertices+l.numVerticalGlyphVertices)/4,m=0;mt},d.prototype.setStale=function(){this.stale=!0};var g=Math.pow(2,25),m=Math.pow(2,24),v=Math.pow(2,17),y=Math.pow(2,16),x=Math.pow(2,9),b=Math.pow(2,8),_=Math.pow(2,1);e.exports=d},{"../data/extent":53,"../source/pixels_to_tile_units":104,"../style/style_layer/symbol_style_layer_properties":209,"./collision_index":217,"./projection":224,"./symbol_size":228}],224:[function(t,e,r){function n(t,e){var r=[t.x,t.y,0,1];f(r,r,e);var n=r[3];return{point:new h(r[0]/n,r[1]/n),signedDistanceFromCamera:n}}function i(t,e){var r=t[0]/t[3],n=t[1]/t[3];return r>=-e[0]&&r<=e[0]&&n>=-e[1]&&n<=e[1]}function a(t,e,r,n,i,a,o,s,l,u,f,h){var p=s.glyphStartIndex+s.numGlyphs,d=s.lineStartIndex,g=s.lineStartIndex+s.lineLength,m=e.getoffsetX(s.glyphStartIndex),v=e.getoffsetX(p-1),y=c(t*m,r,n,i,a,o,s.segment,d,g,l,u,f,h);if(!y)return null;var x=c(t*v,r,n,i,a,o,s.segment,d,g,l,u,f,h);return x?{first:y,last:x}:null}function o(t,e,r,n){return t===x.horizontal&&Math.abs(r.y-e.y)>Math.abs(r.x-e.x)*n?{useVertical:!0}:(t===x.vertical?e.yr.x)?{needsFlipping:!0}:null}function s(t,e,r,i,s,u,f,p,d,g,m,y,x,b){var _,w=e/24,k=t.lineOffsetX*e,M=t.lineOffsetY*e;if(t.numGlyphs>1){var A=t.glyphStartIndex+t.numGlyphs,T=t.lineStartIndex,S=t.lineStartIndex+t.lineLength,C=a(w,p,k,M,r,m,y,t,d,u,x,!1);if(!C)return{notEnoughRoom:!0};var E=n(C.first.point,f).point,L=n(C.last.point,f).point;if(i&&!r){var z=o(t.writingMode,E,L,b);if(z)return z}_=[C.first];for(var P=t.glyphStartIndex+1;P0?R.point:l(y,I,D,1,s),F=o(t.writingMode,D,B,b);if(F)return F}var N=c(w*p.getoffsetX(t.glyphStartIndex),k,M,r,m,y,t.segment,t.lineStartIndex,t.lineStartIndex+t.lineLength,d,u,x,!1);if(!N)return{notEnoughRoom:!0};_=[N]}for(var j=0,V=_;j0?1:-1,y=0;i&&(v*=-1,y=Math.PI),v<0&&(y+=Math.PI);for(var x=v>0?c+s:c+s+1,b=x,_=a,w=a,k=0,M=0,A=Math.abs(m);k+M<=A;){if((x+=v)=u)return null;if(w=_,void 0===(_=d[x])){var T=new h(f.getx(x),f.gety(x)),S=n(T,p);if(S.signedDistanceFromCamera>0)_=d[x]=S.point;else{var C=x-v;_=l(0===k?o:new h(f.getx(C),f.gety(C)),T,w,A-k+1,p)}}k+=M,M=w.dist(_)}var E=(A-k)/M,L=_.sub(w),z=L.mult(E)._add(w);return z._add(L._unit()._perp()._mult(r*v)),{point:z,angle:y+Math.atan2(_.y-w.y,_.x-w.x),tileDistance:g?{prevTileDistance:x-v===b?0:f.gettileUnitDistanceFromAnchor(x-v),lastSegmentViewportDistance:A-k}:null}}function u(t,e){for(var r=0;r=w||o.y<0||o.y>=w||t.symbolInstances.push(function(t,e,r,n,a,o,s,l,u,f,h,d,g,x,b,_,w,M,A,T,S,C){var E,L,z=t.addToLineVertexArray(e,r),P=0,D=0,O=0,I=n.horizontal?n.horizontal.text:"",R=[];n.horizontal&&(E=new v(s,r,e,l,u,f,n.horizontal,h,d,g,t.overscaling),D+=i(t,e,n.horizontal,o,g,A,T,x,z,n.vertical?p.horizontal:p.horizontalOnly,R,S,C),n.vertical&&(O+=i(t,e,n.vertical,o,g,A,T,x,z,p.vertical,R,S,C)));var B=E?E.boxStartIndex:t.collisionBoxArray.length,F=E?E.boxEndIndex:t.collisionBoxArray.length;if(a){var N=m(e,a,o,w,n.horizontal,A,T);L=new v(s,r,e,l,u,f,a,b,_,!1,t.overscaling),P=4*N.length;var j=t.iconSizeData,V=null;"source"===j.functionType?V=[10*o.layout.get("icon-size").evaluate(T)]:"composite"===j.functionType&&(V=[10*C.compositeIconSizes[0].evaluate(T),10*C.compositeIconSizes[1].evaluate(T)]),t.addSymbols(t.icon,N,V,M,w,T,!1,e,z.lineStartIndex,z.lineLength)}var U=L?L.boxStartIndex:t.collisionBoxArray.length,q=L?L.boxEndIndex:t.collisionBoxArray.length;return t.glyphOffsetArray.length>=k.MAX_GLYPHS&&y.warnOnce("Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907"),{key:I,textBoxStartIndex:B,textBoxEndIndex:F,iconBoxStartIndex:U,iconBoxEndIndex:q,textOffset:x,iconOffset:M,anchor:e,line:r,featureIndex:l,feature:T,numGlyphVertices:D,numVerticalGlyphVertices:O,numIconVertices:P,textOpacityState:new c,iconOpacityState:new c,isDuplicate:!1,placedTextSymbolIndices:R,crossTileID:0}}(t,o,a,r,n,t.layers[0],t.collisionBoxArray,e.index,e.sourceLayerIndex,t.index,S,z,O,M,E,P,I,A,{zoom:t.zoom},e,u,f))};if("line"===x.get("symbol-placement"))for(var F=0,N=l(e.geometry,0,0,w,w);F=0;o--)if(n.dist(a[o])1||(h?(clearTimeout(h),h=null,o("dblclick",e)):h=setTimeout(r,300))},!1),l.addEventListener("touchend",function(t){s("touchend",t)},!1),l.addEventListener("touchmove",function(t){s("touchmove",t)},!1),l.addEventListener("touchcancel",function(t){s("touchcancel",t)},!1),l.addEventListener("click",function(t){n.mousePos(l,t).equals(f)&&o("click",t)},!1),l.addEventListener("dblclick",function(t){o("dblclick",t),t.preventDefault()},!1),l.addEventListener("contextmenu",function(e){var r=t.dragRotate&&t.dragRotate.isActive();u||r?u&&(c=e):o("contextmenu",e),e.preventDefault()},!1)}},{"../util/dom":259,"./handler/box_zoom":239,"./handler/dblclick_zoom":240,"./handler/drag_pan":241,"./handler/drag_rotate":242,"./handler/keyboard":243,"./handler/scroll_zoom":244,"./handler/touch_zoom_rotate":245,"@mapbox/point-geometry":4}],231:[function(t,e,r){var n=t("../util/util"),i=t("../style-spec/util/interpolate").number,a=t("../util/browser"),o=t("../geo/lng_lat"),s=t("../geo/lng_lat_bounds"),l=t("@mapbox/point-geometry"),c=function(t){function e(e,r){t.call(this),this.moving=!1,this.transform=e,this._bearingSnap=r.bearingSnap}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.getCenter=function(){return this.transform.center},e.prototype.setCenter=function(t,e){return this.jumpTo({center:t},e)},e.prototype.panBy=function(t,e,r){return t=l.convert(t).mult(-1),this.panTo(this.transform.center,n.extend({offset:t},e),r)},e.prototype.panTo=function(t,e,r){return this.easeTo(n.extend({center:t},e),r)},e.prototype.getZoom=function(){return this.transform.zoom},e.prototype.setZoom=function(t,e){return this.jumpTo({zoom:t},e),this},e.prototype.zoomTo=function(t,e,r){return this.easeTo(n.extend({zoom:t},e),r)},e.prototype.zoomIn=function(t,e){return this.zoomTo(this.getZoom()+1,t,e),this},e.prototype.zoomOut=function(t,e){return this.zoomTo(this.getZoom()-1,t,e),this},e.prototype.getBearing=function(){return this.transform.bearing},e.prototype.setBearing=function(t,e){return this.jumpTo({bearing:t},e),this},e.prototype.rotateTo=function(t,e,r){return this.easeTo(n.extend({bearing:t},e),r)},e.prototype.resetNorth=function(t,e){return this.rotateTo(0,n.extend({duration:1e3},t),e),this},e.prototype.snapToNorth=function(t,e){return Math.abs(this.getBearing())e?1:0}),["bottom","left","right","top"]))return n.warnOnce("options.padding must be a positive number, or an Object with keys 'bottom', 'left', 'right', 'top'"),this;t=s.convert(t);var a=[(e.padding.left-e.padding.right)/2,(e.padding.top-e.padding.bottom)/2],o=Math.min(e.padding.right,e.padding.left),c=Math.min(e.padding.top,e.padding.bottom);e.offset=[e.offset[0]+a[0],e.offset[1]+a[1]];var u=l.convert(e.offset),f=this.transform,h=f.project(t.getNorthWest()),p=f.project(t.getSouthEast()),d=p.sub(h),g=(f.width-2*o-2*Math.abs(u.x))/d.x,m=(f.height-2*c-2*Math.abs(u.y))/d.y;return m<0||g<0?(n.warnOnce("Map cannot fit within canvas with the given bounds, padding, and/or offset."),this):(e.center=f.unproject(h.add(p).div(2)),e.zoom=Math.min(f.scaleZoom(f.scale*Math.min(g,m)),e.maxZoom),e.bearing=0,e.linear?this.easeTo(e,r):this.flyTo(e,r))},e.prototype.jumpTo=function(t,e){this.stop();var r=this.transform,n=!1,i=!1,a=!1;return"zoom"in t&&r.zoom!==+t.zoom&&(n=!0,r.zoom=+t.zoom),void 0!==t.center&&(r.center=o.convert(t.center)),"bearing"in t&&r.bearing!==+t.bearing&&(i=!0,r.bearing=+t.bearing),"pitch"in t&&r.pitch!==+t.pitch&&(a=!0,r.pitch=+t.pitch),this.fire("movestart",e).fire("move",e),n&&this.fire("zoomstart",e).fire("zoom",e).fire("zoomend",e),i&&this.fire("rotate",e),a&&this.fire("pitchstart",e).fire("pitch",e).fire("pitchend",e),this.fire("moveend",e)},e.prototype.easeTo=function(t,e){var r=this;this.stop(),!1===(t=n.extend({offset:[0,0],duration:500,easing:n.ease},t)).animate&&(t.duration=0);var a=this.transform,s=this.getZoom(),c=this.getBearing(),u=this.getPitch(),f="zoom"in t?+t.zoom:s,h="bearing"in t?this._normalizeBearing(t.bearing,c):c,p="pitch"in t?+t.pitch:u,d=a.centerPoint.add(l.convert(t.offset)),g=a.pointLocation(d),m=o.convert(t.center||g);this._normalizeCenter(m);var v,y,x=a.project(g),b=a.project(m).sub(x),_=a.zoomScale(f-s);return t.around&&(v=o.convert(t.around),y=a.locationPoint(v)),this.zooming=f!==s,this.rotating=c!==h,this.pitching=p!==u,this._prepareEase(e,t.noMoveStart),clearTimeout(this._onEaseEnd),this._ease(function(t){if(r.zooming&&(a.zoom=i(s,f,t)),r.rotating&&(a.bearing=i(c,h,t)),r.pitching&&(a.pitch=i(u,p,t)),v)a.setLocationAtPoint(v,y);else{var n=a.zoomScale(a.zoom-s),o=f>s?Math.min(2,_):Math.max(.5,_),l=Math.pow(o,1-t),g=a.unproject(x.add(b.mult(t*l)).mult(n));a.setLocationAtPoint(a.renderWorldCopies?g.wrap():g,d)}r._fireMoveEvents(e)},function(){t.delayEndEvents?r._onEaseEnd=setTimeout(function(){return r._afterEase(e)},t.delayEndEvents):r._afterEase(e)},t),this},e.prototype._prepareEase=function(t,e){this.moving=!0,e||this.fire("movestart",t),this.zooming&&this.fire("zoomstart",t),this.pitching&&this.fire("pitchstart",t)},e.prototype._fireMoveEvents=function(t){this.fire("move",t),this.zooming&&this.fire("zoom",t),this.rotating&&this.fire("rotate",t),this.pitching&&this.fire("pitch",t)},e.prototype._afterEase=function(t){var e=this.zooming,r=this.pitching;this.moving=!1,this.zooming=!1,this.rotating=!1,this.pitching=!1,e&&this.fire("zoomend",t),r&&this.fire("pitchend",t),this.fire("moveend",t)},e.prototype.flyTo=function(t,e){function r(t){var e=(A*A-M*M+(t?-1:1)*E*E*T*T)/(2*(t?A:M)*E*T);return Math.log(Math.sqrt(e*e+1)-e)}function a(t){return(Math.exp(t)-Math.exp(-t))/2}function s(t){return(Math.exp(t)+Math.exp(-t))/2}var c=this;this.stop(),t=n.extend({offset:[0,0],speed:1.2,curve:1.42,easing:n.ease},t);var u=this.transform,f=this.getZoom(),h=this.getBearing(),p=this.getPitch(),d="zoom"in t?n.clamp(+t.zoom,u.minZoom,u.maxZoom):f,g="bearing"in t?this._normalizeBearing(t.bearing,h):h,m="pitch"in t?+t.pitch:p,v=u.zoomScale(d-f),y=u.centerPoint.add(l.convert(t.offset)),x=u.pointLocation(y),b=o.convert(t.center||x);this._normalizeCenter(b);var _=u.project(x),w=u.project(b).sub(_),k=t.curve,M=Math.max(u.width,u.height),A=M/v,T=w.mag();if("minZoom"in t){var S=n.clamp(Math.min(t.minZoom,f,d),u.minZoom,u.maxZoom),C=M/u.zoomScale(S-f);k=Math.sqrt(C/T*2)}var E=k*k,L=r(0),z=function(t){return s(L)/s(L+k*t)},P=function(t){return M*((s(L)*function(t){return a(t)/s(t)}(L+k*t)-a(L))/E)/T},D=(r(1)-L)/k;if(Math.abs(T)<1e-6||!isFinite(D)){if(Math.abs(M-A)<1e-6)return this.easeTo(t,e);var O=At.maxDuration&&(t.duration=0),this.zooming=!0,this.rotating=h!==g,this.pitching=m!==p,this._prepareEase(e,!1),this._ease(function(t){var r=t*D,n=1/z(r);u.zoom=f+u.scaleZoom(n),c.rotating&&(u.bearing=i(h,g,t)),c.pitching&&(u.pitch=i(p,m,t));var a=u.unproject(_.add(w.mult(P(r))).mult(n));u.setLocationAtPoint(u.renderWorldCopies?a.wrap():a,y),c._fireMoveEvents(e)},function(){return c._afterEase(e)},t),this},e.prototype.isEasing=function(){return!!this._isEasing},e.prototype.isMoving=function(){return this.moving},e.prototype.stop=function(){return this._onFrame&&this._finishAnimation(),this},e.prototype._ease=function(t,e,r){var n=this;!1===r.animate||0===r.duration?(t(1),e()):(this._easeStart=a.now(),this._isEasing=!0,this._easeOptions=r,this._startAnimation(function(e){var r=Math.min((a.now()-n._easeStart)/n._easeOptions.duration,1);t(n._easeOptions.easing(r)),1===r&&n.stop()},function(){n._isEasing=!1,e()}))},e.prototype._updateCamera=function(){this._onFrame&&this._onFrame(this.transform)},e.prototype._startAnimation=function(t,e){return void 0===e&&(e=function(){}),this.stop(),this._onFrame=t,this._finishFn=e,this._update(),this},e.prototype._finishAnimation=function(){delete this._onFrame;var t=this._finishFn;delete this._finishFn,t.call(this)},e.prototype._normalizeBearing=function(t,e){t=n.wrap(t,-180,180);var r=Math.abs(t-e);return Math.abs(t-360-e)180?-360:r<-180?360:0}},e}(t("../util/evented"));e.exports=c},{"../geo/lng_lat":62,"../geo/lng_lat_bounds":63,"../style-spec/util/interpolate":158,"../util/browser":252,"../util/evented":260,"../util/util":275,"@mapbox/point-geometry":4}],232:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=t("../../util/config"),o=function(t){this.options=t,i.bindAll(["_updateEditLink","_updateData","_updateCompact"],this)};o.prototype.getDefaultPosition=function(){return"bottom-right"},o.prototype.onAdd=function(t){var e=this.options&&this.options.compact;return this._map=t,this._container=n.create("div","mapboxgl-ctrl mapboxgl-ctrl-attrib"),e&&this._container.classList.add("mapboxgl-compact"),this._updateAttributions(),this._updateEditLink(),this._map.on("sourcedata",this._updateData),this._map.on("moveend",this._updateEditLink),void 0===e&&(this._map.on("resize",this._updateCompact),this._updateCompact()),this._container},o.prototype.onRemove=function(){n.remove(this._container),this._map.off("sourcedata",this._updateData),this._map.off("moveend",this._updateEditLink),this._map.off("resize",this._updateCompact),this._map=void 0},o.prototype._updateEditLink=function(){var t=this._editLink;t||(t=this._editLink=this._container.querySelector(".mapbox-improve-map"));var e=[{key:"owner",value:this.styleOwner},{key:"id",value:this.styleId},{key:"access_token",value:a.ACCESS_TOKEN}];if(t){var r=e.reduce(function(t,r,n){return r.value&&(t+=r.key+"="+r.value+(n=0)return!1;return!0})).length?(this._container.innerHTML=t.join(" | "),this._container.classList.remove("mapboxgl-attrib-empty")):this._container.classList.add("mapboxgl-attrib-empty"),this._editLink=null}},o.prototype._updateCompact=function(){this._map.getCanvasContainer().offsetWidth<=640?this._container.classList.add("mapboxgl-compact"):this._container.classList.remove("mapboxgl-compact")},e.exports=o},{"../../util/config":256,"../../util/dom":259,"../../util/util":275}],233:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=t("../../util/window"),o=function(){this._fullscreen=!1,i.bindAll(["_onClickFullscreen","_changeIcon"],this),"onfullscreenchange"in a.document?this._fullscreenchange="fullscreenchange":"onmozfullscreenchange"in a.document?this._fullscreenchange="mozfullscreenchange":"onwebkitfullscreenchange"in a.document?this._fullscreenchange="webkitfullscreenchange":"onmsfullscreenchange"in a.document&&(this._fullscreenchange="MSFullscreenChange"),this._className="mapboxgl-ctrl"};o.prototype.onAdd=function(t){return this._map=t,this._mapContainer=this._map.getContainer(),this._container=n.create("div",this._className+" mapboxgl-ctrl-group"),this._checkFullscreenSupport()?this._setupUI():(this._container.style.display="none",i.warnOnce("This device does not support fullscreen mode.")),this._container},o.prototype.onRemove=function(){n.remove(this._container),this._map=null,a.document.removeEventListener(this._fullscreenchange,this._changeIcon)},o.prototype._checkFullscreenSupport=function(){return!!(a.document.fullscreenEnabled||a.document.mozFullScreenEnabled||a.document.msFullscreenEnabled||a.document.webkitFullscreenEnabled)},o.prototype._setupUI=function(){var t=this._fullscreenButton=n.create("button",this._className+"-icon "+this._className+"-fullscreen",this._container);t.setAttribute("aria-label","Toggle fullscreen"),t.type="button",this._fullscreenButton.addEventListener("click",this._onClickFullscreen),a.document.addEventListener(this._fullscreenchange,this._changeIcon)},o.prototype._isFullscreen=function(){return this._fullscreen},o.prototype._changeIcon=function(){(a.document.fullscreenElement||a.document.mozFullScreenElement||a.document.webkitFullscreenElement||a.document.msFullscreenElement)===this._mapContainer!==this._fullscreen&&(this._fullscreen=!this._fullscreen,this._fullscreenButton.classList.toggle(this._className+"-shrink"),this._fullscreenButton.classList.toggle(this._className+"-fullscreen"))},o.prototype._onClickFullscreen=function(){this._isFullscreen()?a.document.exitFullscreen?a.document.exitFullscreen():a.document.mozCancelFullScreen?a.document.mozCancelFullScreen():a.document.msExitFullscreen?a.document.msExitFullscreen():a.document.webkitCancelFullScreen&&a.document.webkitCancelFullScreen():this._mapContainer.requestFullscreen?this._mapContainer.requestFullscreen():this._mapContainer.mozRequestFullScreen?this._mapContainer.mozRequestFullScreen():this._mapContainer.msRequestFullscreen?this._mapContainer.msRequestFullscreen():this._mapContainer.webkitRequestFullscreen&&this._mapContainer.webkitRequestFullscreen()},e.exports=o},{"../../util/dom":259,"../../util/util":275,"../../util/window":254}],234:[function(t,e,r){var n,i=t("../../util/evented"),a=t("../../util/dom"),o=t("../../util/window"),s=t("../../util/util"),l=t("../../geo/lng_lat"),c=t("../marker"),u={positionOptions:{enableHighAccuracy:!1,timeout:6e3},fitBoundsOptions:{maxZoom:15},trackUserLocation:!1,showUserLocation:!0},f=function(t){function e(e){t.call(this),this.options=s.extend({},u,e),s.bindAll(["_onSuccess","_onError","_finish","_setupUI","_updateCamera","_updateMarker","_onClickGeolocate"],this)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.onAdd=function(t){return this._map=t,this._container=a.create("div","mapboxgl-ctrl mapboxgl-ctrl-group"),function(t){void 0!==n?t(n):void 0!==o.navigator.permissions?o.navigator.permissions.query({name:"geolocation"}).then(function(e){n="denied"!==e.state,t(n)}):(n=!!o.navigator.geolocation,t(n))}(this._setupUI),this._container},e.prototype.onRemove=function(){void 0!==this._geolocationWatchID&&(o.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0),this.options.showUserLocation&&this._userLocationDotMarker.remove(),a.remove(this._container),this._map=void 0},e.prototype._onSuccess=function(t){if(this.options.trackUserLocation)switch(this._lastKnownPosition=t,this._watchState){case"WAITING_ACTIVE":case"ACTIVE_LOCK":case"ACTIVE_ERROR":this._watchState="ACTIVE_LOCK",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-active");break;case"BACKGROUND":case"BACKGROUND_ERROR":this._watchState="BACKGROUND",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-background-error"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-background")}this.options.showUserLocation&&"OFF"!==this._watchState&&this._updateMarker(t),this.options.trackUserLocation&&"ACTIVE_LOCK"!==this._watchState||this._updateCamera(t),this.options.showUserLocation&&this._dotElement.classList.remove("mapboxgl-user-location-dot-stale"),this.fire("geolocate",t),this._finish()},e.prototype._updateCamera=function(t){var e=new l(t.coords.longitude,t.coords.latitude),r=t.coords.accuracy;this._map.fitBounds(e.toBounds(r),this.options.fitBoundsOptions,{geolocateSource:!0})},e.prototype._updateMarker=function(t){t?this._userLocationDotMarker.setLngLat([t.coords.longitude,t.coords.latitude]).addTo(this._map):this._userLocationDotMarker.remove()},e.prototype._onError=function(t){if(this.options.trackUserLocation)if(1===t.code)this._watchState="OFF",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-background-error"),void 0!==this._geolocationWatchID&&this._clearWatch();else switch(this._watchState){case"WAITING_ACTIVE":this._watchState="ACTIVE_ERROR",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-active-error");break;case"ACTIVE_LOCK":this._watchState="ACTIVE_ERROR",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-waiting");break;case"BACKGROUND":this._watchState="BACKGROUND_ERROR",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-background"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-background-error"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-waiting")}"OFF"!==this._watchState&&this.options.showUserLocation&&this._dotElement.classList.add("mapboxgl-user-location-dot-stale"),this.fire("error",t),this._finish()},e.prototype._finish=function(){this._timeoutId&&clearTimeout(this._timeoutId),this._timeoutId=void 0},e.prototype._setupUI=function(t){var e=this;!1!==t&&(this._container.addEventListener("contextmenu",function(t){return t.preventDefault()}),this._geolocateButton=a.create("button","mapboxgl-ctrl-icon mapboxgl-ctrl-geolocate",this._container),this._geolocateButton.type="button",this._geolocateButton.setAttribute("aria-label","Geolocate"),this.options.trackUserLocation&&(this._geolocateButton.setAttribute("aria-pressed","false"),this._watchState="OFF"),this.options.showUserLocation&&(this._dotElement=a.create("div","mapboxgl-user-location-dot"),this._userLocationDotMarker=new c(this._dotElement),this.options.trackUserLocation&&(this._watchState="OFF")),this._geolocateButton.addEventListener("click",this._onClickGeolocate.bind(this)),this.options.trackUserLocation&&this._map.on("movestart",function(t){t.geolocateSource||"ACTIVE_LOCK"!==e._watchState||(e._watchState="BACKGROUND",e._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-background"),e._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active"),e.fire("trackuserlocationend"))}))},e.prototype._onClickGeolocate=function(){if(this.options.trackUserLocation){switch(this._watchState){case"OFF":this._watchState="WAITING_ACTIVE",this.fire("trackuserlocationstart");break;case"WAITING_ACTIVE":case"ACTIVE_LOCK":case"ACTIVE_ERROR":case"BACKGROUND_ERROR":this._watchState="OFF",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-active-error"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-background"),this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-background-error"),this.fire("trackuserlocationend");break;case"BACKGROUND":this._watchState="ACTIVE_LOCK",this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-background"),this._lastKnownPosition&&this._updateCamera(this._lastKnownPosition),this.fire("trackuserlocationstart")}switch(this._watchState){case"WAITING_ACTIVE":this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-active");break;case"ACTIVE_LOCK":this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-active");break;case"ACTIVE_ERROR":this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-active-error");break;case"BACKGROUND":this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-background");break;case"BACKGROUND_ERROR":this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-background-error")}"OFF"===this._watchState&&void 0!==this._geolocationWatchID?this._clearWatch():void 0===this._geolocationWatchID&&(this._geolocateButton.classList.add("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.setAttribute("aria-pressed","true"),this._geolocationWatchID=o.navigator.geolocation.watchPosition(this._onSuccess,this._onError,this.options.positionOptions))}else o.navigator.geolocation.getCurrentPosition(this._onSuccess,this._onError,this.options.positionOptions),this._timeoutId=setTimeout(this._finish,1e4)},e.prototype._clearWatch=function(){o.navigator.geolocation.clearWatch(this._geolocationWatchID),this._geolocationWatchID=void 0,this._geolocateButton.classList.remove("mapboxgl-ctrl-geolocate-waiting"),this._geolocateButton.setAttribute("aria-pressed","false"),this.options.showUserLocation&&this._updateMarker(null)},e}(i);e.exports=f},{"../../geo/lng_lat":62,"../../util/dom":259,"../../util/evented":260,"../../util/util":275,"../../util/window":254,"../marker":248}],235:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=function(){i.bindAll(["_updateLogo"],this)};a.prototype.onAdd=function(t){this._map=t,this._container=n.create("div","mapboxgl-ctrl");var e=n.create("a","mapboxgl-ctrl-logo");return e.target="_blank",e.href="https://www.mapbox.com/",e.setAttribute("aria-label","Mapbox logo"),this._container.appendChild(e),this._container.style.display="none",this._map.on("sourcedata",this._updateLogo),this._updateLogo(),this._container},a.prototype.onRemove=function(){n.remove(this._container),this._map.off("sourcedata",this._updateLogo)},a.prototype.getDefaultPosition=function(){return"bottom-left"},a.prototype._updateLogo=function(t){t&&"metadata"!==t.sourceDataType||(this._container.style.display=this._logoRequired()?"block":"none")},a.prototype._logoRequired=function(){if(this._map.style){var t=this._map.style.sourceCaches;for(var e in t)if(t[e].getSource().mapbox_logo)return!0;return!1}},e.exports=a},{"../../util/dom":259,"../../util/util":275}],236:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=t("../handler/drag_rotate"),o={showCompass:!0,showZoom:!0},s=function(t){var e=this;this.options=i.extend({},o,t),this._container=n.create("div","mapboxgl-ctrl mapboxgl-ctrl-group"),this._container.addEventListener("contextmenu",function(t){return t.preventDefault()}),this.options.showZoom&&(this._zoomInButton=this._createButton("mapboxgl-ctrl-icon mapboxgl-ctrl-zoom-in","Zoom In",function(){return e._map.zoomIn()}),this._zoomOutButton=this._createButton("mapboxgl-ctrl-icon mapboxgl-ctrl-zoom-out","Zoom Out",function(){return e._map.zoomOut()})),this.options.showCompass&&(i.bindAll(["_rotateCompassArrow"],this),this._compass=this._createButton("mapboxgl-ctrl-icon mapboxgl-ctrl-compass","Reset North",function(){return e._map.resetNorth()}),this._compassArrow=n.create("span","mapboxgl-ctrl-compass-arrow",this._compass))};s.prototype._rotateCompassArrow=function(){var t="rotate("+this._map.transform.angle*(180/Math.PI)+"deg)";this._compassArrow.style.transform=t},s.prototype.onAdd=function(t){return this._map=t,this.options.showCompass&&(this._map.on("rotate",this._rotateCompassArrow),this._rotateCompassArrow(),this._handler=new a(t,{button:"left",element:this._compass}),this._handler.enable()),this._container},s.prototype.onRemove=function(){n.remove(this._container),this.options.showCompass&&(this._map.off("rotate",this._rotateCompassArrow),this._handler.disable(),delete this._handler),delete this._map},s.prototype._createButton=function(t,e,r){var i=n.create("button",t,this._container);return i.type="button",i.setAttribute("aria-label",e),i.addEventListener("click",r),i},e.exports=s},{"../../util/dom":259,"../../util/util":275,"../handler/drag_rotate":242}],237:[function(t,e,r){function n(t,e,r){var n=r&&r.maxWidth||100,a=t._container.clientHeight/2,o=function(t,e){var r=Math.PI/180,n=t.lat*r,i=e.lat*r,a=Math.sin(n)*Math.sin(i)+Math.cos(n)*Math.cos(i)*Math.cos((e.lng-t.lng)*r);return 6371e3*Math.acos(Math.min(a,1))}(t.unproject([0,a]),t.unproject([n,a]));if(r&&"imperial"===r.unit){var s=3.2808*o;s>5280?i(e,n,s/5280,"mi"):i(e,n,s,"ft")}else if(r&&"nautical"===r.unit){i(e,n,o/1852,"nm")}else i(e,n,o,"m")}function i(t,e,r,n){var i=function(t){var e=Math.pow(10,(""+Math.floor(t)).length-1),r=t/e;return e*(r=r>=10?10:r>=5?5:r>=3?3:r>=2?2:1)}(r),a=i/r;"m"===n&&i>=1e3&&(i/=1e3,n="km"),t.style.width=e*a+"px",t.innerHTML=i+n}var a=t("../../util/dom"),o=t("../../util/util"),s=function(t){this.options=t,o.bindAll(["_onMove"],this)};s.prototype.getDefaultPosition=function(){return"bottom-left"},s.prototype._onMove=function(){n(this._map,this._container,this.options)},s.prototype.onAdd=function(t){return this._map=t,this._container=a.create("div","mapboxgl-ctrl mapboxgl-ctrl-scale",t.getContainer()),this._map.on("move",this._onMove),this._onMove(),this._container},s.prototype.onRemove=function(){a.remove(this._container),this._map.off("move",this._onMove),this._map=void 0},e.exports=s},{"../../util/dom":259,"../../util/util":275}],238:[function(t,e,r){},{}],239:[function(t,e,r){var n=t("../../util/dom"),i=t("../../geo/lng_lat_bounds"),a=t("../../util/util"),o=t("../../util/window"),s=function(t){this._map=t,this._el=t.getCanvasContainer(),this._container=t.getContainer(),a.bindAll(["_onMouseDown","_onMouseMove","_onMouseUp","_onKeyDown"],this)};s.prototype.isEnabled=function(){return!!this._enabled},s.prototype.isActive=function(){return!!this._active},s.prototype.enable=function(){this.isEnabled()||(this._map.dragPan&&this._map.dragPan.disable(),this._el.addEventListener("mousedown",this._onMouseDown,!1),this._map.dragPan&&this._map.dragPan.enable(),this._enabled=!0)},s.prototype.disable=function(){this.isEnabled()&&(this._el.removeEventListener("mousedown",this._onMouseDown),this._enabled=!1)},s.prototype._onMouseDown=function(t){t.shiftKey&&0===t.button&&(o.document.addEventListener("mousemove",this._onMouseMove,!1),o.document.addEventListener("keydown",this._onKeyDown,!1),o.document.addEventListener("mouseup",this._onMouseUp,!1),n.disableDrag(),this._startPos=n.mousePos(this._el,t),this._active=!0)},s.prototype._onMouseMove=function(t){var e=this._startPos,r=n.mousePos(this._el,t);this._box||(this._box=n.create("div","mapboxgl-boxzoom",this._container),this._container.classList.add("mapboxgl-crosshair"),this._fireEvent("boxzoomstart",t));var i=Math.min(e.x,r.x),a=Math.max(e.x,r.x),o=Math.min(e.y,r.y),s=Math.max(e.y,r.y);n.setTransform(this._box,"translate("+i+"px,"+o+"px)"),this._box.style.width=a-i+"px",this._box.style.height=s-o+"px"},s.prototype._onMouseUp=function(t){if(0===t.button){var e=this._startPos,r=n.mousePos(this._el,t),a=(new i).extend(this._map.unproject(e)).extend(this._map.unproject(r));this._finish(),e.x===r.x&&e.y===r.y?this._fireEvent("boxzoomcancel",t):this._map.fitBounds(a,{linear:!0}).fire("boxzoomend",{originalEvent:t,boxZoomBounds:a})}},s.prototype._onKeyDown=function(t){27===t.keyCode&&(this._finish(),this._fireEvent("boxzoomcancel",t))},s.prototype._finish=function(){this._active=!1,o.document.removeEventListener("mousemove",this._onMouseMove,!1),o.document.removeEventListener("keydown",this._onKeyDown,!1),o.document.removeEventListener("mouseup",this._onMouseUp,!1),this._container.classList.remove("mapboxgl-crosshair"),this._box&&(n.remove(this._box),this._box=null),n.enableDrag()},s.prototype._fireEvent=function(t,e){return this._map.fire(t,{originalEvent:e})},e.exports=s},{"../../geo/lng_lat_bounds":63,"../../util/dom":259,"../../util/util":275,"../../util/window":254}],240:[function(t,e,r){var n=t("../../util/util"),i=function(t){this._map=t,n.bindAll(["_onDblClick","_onZoomEnd"],this)};i.prototype.isEnabled=function(){return!!this._enabled},i.prototype.isActive=function(){return!!this._active},i.prototype.enable=function(){this.isEnabled()||(this._map.on("dblclick",this._onDblClick),this._enabled=!0)},i.prototype.disable=function(){this.isEnabled()&&(this._map.off("dblclick",this._onDblClick),this._enabled=!1)},i.prototype._onDblClick=function(t){this._active=!0,this._map.on("zoomend",this._onZoomEnd),this._map.zoomTo(this._map.getZoom()+(t.originalEvent.shiftKey?-1:1),{around:t.lngLat},t)},i.prototype._onZoomEnd=function(){this._active=!1,this._map.off("zoomend",this._onZoomEnd)},e.exports=i},{"../../util/util":275}],241:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=t("../../util/window"),o=t("../../util/browser"),s=i.bezier(0,0,.3,1),l=function(t){this._map=t,this._el=t.getCanvasContainer(),i.bindAll(["_onDown","_onMove","_onUp","_onTouchEnd","_onMouseUp","_onDragFrame","_onDragFinished"],this)};l.prototype.isEnabled=function(){return!!this._enabled},l.prototype.isActive=function(){return!!this._active},l.prototype.enable=function(){this.isEnabled()||(this._el.classList.add("mapboxgl-touch-drag-pan"),this._el.addEventListener("mousedown",this._onDown),this._el.addEventListener("touchstart",this._onDown),this._enabled=!0)},l.prototype.disable=function(){this.isEnabled()&&(this._el.classList.remove("mapboxgl-touch-drag-pan"),this._el.removeEventListener("mousedown",this._onDown),this._el.removeEventListener("touchstart",this._onDown),this._enabled=!1)},l.prototype._onDown=function(t){this._ignoreEvent(t)||this.isActive()||(t.touches?(a.document.addEventListener("touchmove",this._onMove),a.document.addEventListener("touchend",this._onTouchEnd)):(a.document.addEventListener("mousemove",this._onMove),a.document.addEventListener("mouseup",this._onMouseUp)),a.addEventListener("blur",this._onMouseUp),this._active=!1,this._previousPos=n.mousePos(this._el,t),this._inertia=[[o.now(),this._previousPos]])},l.prototype._onMove=function(t){if(!this._ignoreEvent(t)){this._lastMoveEvent=t,t.preventDefault();var e=n.mousePos(this._el,t);if(this._drainInertiaBuffer(),this._inertia.push([o.now(),e]),!this._previousPos)return void(this._previousPos=e);this._pos=e,this.isActive()||(this._active=!0,this._map.moving=!0,this._fireEvent("dragstart",t),this._fireEvent("movestart",t),this._map._startAnimation(this._onDragFrame,this._onDragFinished)),this._map._update()}},l.prototype._onDragFrame=function(t){var e=this._lastMoveEvent;e&&(t.setLocationAtPoint(t.pointLocation(this._previousPos),this._pos),this._fireEvent("drag",e),this._fireEvent("move",e),this._previousPos=this._pos,delete this._lastMoveEvent)},l.prototype._onDragFinished=function(t){var e=this;if(this.isActive()){this._active=!1,delete this._lastMoveEvent,delete this._previousPos,delete this._pos,this._fireEvent("dragend",t),this._drainInertiaBuffer();var r=function(){e._map.moving=!1,e._fireEvent("moveend",t)},n=this._inertia;if(n.length<2)return void r();var i=n[n.length-1],a=n[0],o=i[1].sub(a[1]),l=(i[0]-a[0])/1e3;if(0===l||i[1].equals(a[1]))return void r();var c=o.mult(.3/l),u=c.mag();u>1400&&(u=1400,c._unit()._mult(u));var f=u/750,h=c.mult(-f/2);this._map.panBy(h,{duration:1e3*f,easing:s,noMoveStart:!0},{originalEvent:t})}},l.prototype._onUp=function(t){this._onDragFinished(t)},l.prototype._onMouseUp=function(t){this._ignoreEvent(t)||(this._onUp(t),a.document.removeEventListener("mousemove",this._onMove),a.document.removeEventListener("mouseup",this._onMouseUp),a.removeEventListener("blur",this._onMouseUp))},l.prototype._onTouchEnd=function(t){this._ignoreEvent(t)||(this._onUp(t),a.document.removeEventListener("touchmove",this._onMove),a.document.removeEventListener("touchend",this._onTouchEnd))},l.prototype._fireEvent=function(t,e){return this._map.fire(t,e?{originalEvent:e}:{})},l.prototype._ignoreEvent=function(t){var e=this._map;return!(!e.boxZoom||!e.boxZoom.isActive())||!(!e.dragRotate||!e.dragRotate.isActive())||(t.touches?t.touches.length>1:!!t.ctrlKey||"mousemove"!==t.type&&t.button&&0!==t.button)},l.prototype._drainInertiaBuffer=function(){for(var t=this._inertia,e=o.now();t.length>0&&e-t[0][0]>160;)t.shift()},e.exports=l},{"../../util/browser":252,"../../util/dom":259,"../../util/util":275,"../../util/window":254}],242:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=t("../../util/window"),o=t("../../util/browser"),s=i.bezier(0,0,.25,1),l=function(t,e){this._map=t,this._el=e.element||t.getCanvasContainer(),this._button=e.button||"right",this._bearingSnap=e.bearingSnap||0,this._pitchWithRotate=!1!==e.pitchWithRotate,i.bindAll(["_onDown","_onMove","_onUp","_onDragFrame","_onDragFinished"],this)};l.prototype.isEnabled=function(){return!!this._enabled},l.prototype.isActive=function(){return!!this._active},l.prototype.enable=function(){this.isEnabled()||(this._el.addEventListener("mousedown",this._onDown),this._enabled=!0)},l.prototype.disable=function(){this.isEnabled()&&(this._el.removeEventListener("mousedown",this._onDown),this._enabled=!1)},l.prototype._onDown=function(t){if(!(this._map.boxZoom&&this._map.boxZoom.isActive()||this._map.dragPan&&this._map.dragPan.isActive()||this.isActive())){if("right"===this._button){var e=t.ctrlKey?0:2,r=t.button;if(void 0!==a.InstallTrigger&&2===t.button&&t.ctrlKey&&a.navigator.platform.toUpperCase().indexOf("MAC")>=0&&(r=0),r!==e)return}else if(t.ctrlKey||0!==t.button)return;n.disableDrag(),a.document.addEventListener("mousemove",this._onMove,{capture:!0}),a.document.addEventListener("mouseup",this._onUp),a.addEventListener("blur",this._onUp),this._active=!1,this._inertia=[[o.now(),this._map.getBearing()]],this._previousPos=n.mousePos(this._el,t),this._center=this._map.transform.centerPoint,t.preventDefault()}},l.prototype._onMove=function(t){this._lastMoveEvent=t;var e=n.mousePos(this._el,t);this._previousPos?(this._pos=e,this.isActive()||(this._active=!0,this._map.moving=!0,this._fireEvent("rotatestart",t),this._fireEvent("movestart",t),this._pitchWithRotate&&this._fireEvent("pitchstart",t),this._map._startAnimation(this._onDragFrame,this._onDragFinished)),this._map._update()):this._previousPos=e},l.prototype._onUp=function(t){a.document.removeEventListener("mousemove",this._onMove,{capture:!0}),a.document.removeEventListener("mouseup",this._onUp),a.removeEventListener("blur",this._onUp),n.enableDrag(),this._onDragFinished(t)},l.prototype._onDragFrame=function(t){var e=this._lastMoveEvent;if(e){var r=this._previousPos,n=this._pos,i=.8*(r.x-n.x),a=-.5*(r.y-n.y),s=t.bearing-i,l=t.pitch-a,c=this._inertia,u=c[c.length-1];this._drainInertiaBuffer(),c.push([o.now(),this._map._normalizeBearing(s,u[1])]),t.bearing=s,this._pitchWithRotate&&(this._fireEvent("pitch",e),t.pitch=l),this._fireEvent("rotate",e),this._fireEvent("move",e),delete this._lastMoveEvent,this._previousPos=this._pos}},l.prototype._onDragFinished=function(t){var e=this;if(this.isActive()){this._active=!1,delete this._lastMoveEvent,delete this._previousPos,this._fireEvent("rotateend",t),this._drainInertiaBuffer();var r=this._map,n=r.getBearing(),i=this._inertia,a=function(){Math.abs(n)180&&(d=180);var g=d/180;u+=h*d*(g/2),Math.abs(r._normalizeBearing(u,0))0&&e-t[0][0]>160;)t.shift()},e.exports=l},{"../../util/browser":252,"../../util/dom":259,"../../util/util":275,"../../util/window":254}],243:[function(t,e,r){function n(t){return t*(2-t)}var i=t("../../util/util"),a=function(t){this._map=t,this._el=t.getCanvasContainer(),i.bindAll(["_onKeyDown"],this)};a.prototype.isEnabled=function(){return!!this._enabled},a.prototype.enable=function(){this.isEnabled()||(this._el.addEventListener("keydown",this._onKeyDown,!1),this._enabled=!0)},a.prototype.disable=function(){this.isEnabled()&&(this._el.removeEventListener("keydown",this._onKeyDown),this._enabled=!1)},a.prototype._onKeyDown=function(t){if(!(t.altKey||t.ctrlKey||t.metaKey)){var e=0,r=0,i=0,a=0,o=0;switch(t.keyCode){case 61:case 107:case 171:case 187:e=1;break;case 189:case 109:case 173:e=-1;break;case 37:t.shiftKey?r=-1:(t.preventDefault(),a=-1);break;case 39:t.shiftKey?r=1:(t.preventDefault(),a=1);break;case 38:t.shiftKey?i=1:(t.preventDefault(),o=-1);break;case 40:t.shiftKey?i=-1:(o=1,t.preventDefault());break;default:return}var s=this._map,l=s.getZoom(),c={duration:300,delayEndEvents:500,easing:n,zoom:e?Math.round(l)+e*(t.shiftKey?2:1):l,bearing:s.getBearing()+15*r,pitch:s.getPitch()+10*i,offset:[100*-a,100*-o],center:s.getCenter()};s.easeTo(c,{originalEvent:t})}},e.exports=a},{"../../util/util":275}],244:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=t("../../util/browser"),o=t("../../util/window"),s=t("../../style-spec/util/interpolate").number,l=t("../../geo/lng_lat"),c=o.navigator.userAgent.toLowerCase(),u=-1!==c.indexOf("firefox"),f=-1!==c.indexOf("safari")&&-1===c.indexOf("chrom"),h=function(t){this._map=t,this._el=t.getCanvasContainer(),this._delta=0,i.bindAll(["_onWheel","_onTimeout","_onScrollFrame","_onScrollFinished"],this)};h.prototype.isEnabled=function(){return!!this._enabled},h.prototype.isActive=function(){return!!this._active},h.prototype.enable=function(t){this.isEnabled()||(this._el.addEventListener("wheel",this._onWheel,!1),this._el.addEventListener("mousewheel",this._onWheel,!1),this._enabled=!0,this._aroundCenter=t&&"center"===t.around)},h.prototype.disable=function(){this.isEnabled()&&(this._el.removeEventListener("wheel",this._onWheel),this._el.removeEventListener("mousewheel",this._onWheel),this._enabled=!1)},h.prototype._onWheel=function(t){var e=0;"wheel"===t.type?(e=t.deltaY,u&&t.deltaMode===o.WheelEvent.DOM_DELTA_PIXEL&&(e/=a.devicePixelRatio),t.deltaMode===o.WheelEvent.DOM_DELTA_LINE&&(e*=40)):"mousewheel"===t.type&&(e=-t.wheelDeltaY,f&&(e/=3));var r=a.now(),n=r-(this._lastWheelEventTime||0);this._lastWheelEventTime=r,0!==e&&e%4.000244140625==0?this._type="wheel":0!==e&&Math.abs(e)<4?this._type="trackpad":n>400?(this._type=null,this._lastValue=e,this._timeout=setTimeout(this._onTimeout,40,t)):this._type||(this._type=Math.abs(n*e)<200?"trackpad":"wheel",this._timeout&&(clearTimeout(this._timeout),this._timeout=null,e+=this._lastValue)),t.shiftKey&&e&&(e/=4),this._type&&(this._lastWheelEvent=t,this._delta-=e,this.isActive()||this._start(t)),t.preventDefault()},h.prototype._onTimeout=function(t){this._type="wheel",this._delta-=this._lastValue,this.isActive()||this._start(t)},h.prototype._start=function(t){if(this._delta){this._active=!0,this._map.moving=!0,this._map.zooming=!0,this._map.fire("movestart",{originalEvent:t}),this._map.fire("zoomstart",{originalEvent:t}),clearTimeout(this._finishTimeout);var e=n.mousePos(this._el,t);this._around=l.convert(this._aroundCenter?this._map.getCenter():this._map.unproject(e)),this._aroundPoint=this._map.transform.locationPoint(this._around),this._map._startAnimation(this._onScrollFrame,this._onScrollFinished)}},h.prototype._onScrollFrame=function(t){if(this.isActive()){if(0!==this._delta){var e="wheel"===this._type&&Math.abs(this._delta)>4.000244140625?1/450:.01,r=2/(1+Math.exp(-Math.abs(this._delta*e)));this._delta<0&&0!==r&&(r=1/r);var n="number"==typeof this._targetZoom?t.zoomScale(this._targetZoom):t.scale;this._targetZoom=Math.min(t.maxZoom,Math.max(t.minZoom,t.scaleZoom(n*r))),"wheel"===this._type&&(this._startZoom=t.zoom,this._easing=this._smoothOutEasing(200)),this._delta=0}if("wheel"===this._type){var i=Math.min((a.now()-this._lastWheelEventTime)/200,1),o=this._easing(i);t.zoom=s(this._startZoom,this._targetZoom,o),1===i&&this._map.stop()}else t.zoom=this._targetZoom,this._map.stop();t.setLocationAtPoint(this._around,this._aroundPoint),this._map.fire("move",{originalEvent:this._lastWheelEvent}),this._map.fire("zoom",{originalEvent:this._lastWheelEvent})}},h.prototype._onScrollFinished=function(){var t=this;this.isActive()&&(this._active=!1,this._finishTimeout=setTimeout(function(){t._map.moving=!1,t._map.zooming=!1,t._map.fire("zoomend"),t._map.fire("moveend"),delete t._targetZoom},200))},h.prototype._smoothOutEasing=function(t){var e=i.ease;if(this._prevEase){var r=this._prevEase,n=(a.now()-r.start)/r.duration,o=r.easing(n+.01)-r.easing(n),s=.27/Math.sqrt(o*o+1e-4)*.01,l=Math.sqrt(.0729-s*s);e=i.bezier(s,l,.25,1)}return this._prevEase={start:a.now(),duration:t,easing:e},e},e.exports=h},{"../../geo/lng_lat":62,"../../style-spec/util/interpolate":158,"../../util/browser":252,"../../util/dom":259,"../../util/util":275,"../../util/window":254}],245:[function(t,e,r){var n=t("../../util/dom"),i=t("../../util/util"),a=t("../../util/window"),o=t("../../util/browser"),s=i.bezier(0,0,.15,1),l=function(t){this._map=t,this._el=t.getCanvasContainer(),i.bindAll(["_onStart","_onMove","_onEnd"],this)};l.prototype.isEnabled=function(){return!!this._enabled},l.prototype.enable=function(t){this.isEnabled()||(this._el.classList.add("mapboxgl-touch-zoom-rotate"),this._el.addEventListener("touchstart",this._onStart,!1),this._enabled=!0,this._aroundCenter=t&&"center"===t.around)},l.prototype.disable=function(){this.isEnabled()&&(this._el.classList.remove("mapboxgl-touch-zoom-rotate"),this._el.removeEventListener("touchstart",this._onStart),this._enabled=!1)},l.prototype.disableRotation=function(){this._rotationDisabled=!0},l.prototype.enableRotation=function(){this._rotationDisabled=!1},l.prototype._onStart=function(t){if(2===t.touches.length){var e=n.mousePos(this._el,t.touches[0]),r=n.mousePos(this._el,t.touches[1]);this._startVec=e.sub(r),this._startScale=this._map.transform.scale,this._startBearing=this._map.transform.bearing,this._gestureIntent=void 0,this._inertia=[],a.document.addEventListener("touchmove",this._onMove,!1),a.document.addEventListener("touchend",this._onEnd,!1)}},l.prototype._onMove=function(t){if(2===t.touches.length){var e=n.mousePos(this._el,t.touches[0]),r=n.mousePos(this._el,t.touches[1]),i=e.add(r).div(2),a=e.sub(r),s=a.mag()/this._startVec.mag(),l=this._rotationDisabled?0:180*a.angleWith(this._startVec)/Math.PI,c=this._map;if(this._gestureIntent){var u={duration:0,around:c.unproject(i)};"rotate"===this._gestureIntent&&(u.bearing=this._startBearing+l),"zoom"!==this._gestureIntent&&"rotate"!==this._gestureIntent||(u.zoom=c.transform.scaleZoom(this._startScale*s)),c.stop(),this._drainInertiaBuffer(),this._inertia.push([o.now(),s,i]),c.easeTo(u,{originalEvent:t})}else{var f=Math.abs(1-s)>.15;Math.abs(l)>10?this._gestureIntent="rotate":f&&(this._gestureIntent="zoom"),this._gestureIntent&&(this._startVec=a,this._startScale=c.transform.scale,this._startBearing=c.transform.bearing)}t.preventDefault()}},l.prototype._onEnd=function(t){a.document.removeEventListener("touchmove",this._onMove),a.document.removeEventListener("touchend",this._onEnd),this._drainInertiaBuffer();var e=this._inertia,r=this._map;if(e.length<2)r.snapToNorth({},{originalEvent:t});else{var n=e[e.length-1],i=e[0],o=r.transform.scaleZoom(this._startScale*n[1]),l=r.transform.scaleZoom(this._startScale*i[1]),c=o-l,u=(n[0]-i[0])/1e3,f=n[2];if(0!==u&&o!==l){var h=.15*c/u;Math.abs(h)>2.5&&(h=h>0?2.5:-2.5);var p=1e3*Math.abs(h/(12*.15)),d=o+h*p/2e3;d<0&&(d=0),r.easeTo({zoom:d,duration:p,easing:s,around:this._aroundCenter?r.getCenter():r.unproject(f)},{originalEvent:t})}else r.snapToNorth({},{originalEvent:t})}},l.prototype._drainInertiaBuffer=function(){for(var t=this._inertia,e=o.now();t.length>2&&e-t[0][0]>160;)t.shift()},e.exports=l},{"../../util/browser":252,"../../util/dom":259,"../../util/util":275,"../../util/window":254}],246:[function(t,e,r){var n=t("../util/util"),i=t("../util/window"),a=t("../util/throttle"),o=function(){n.bindAll(["_onHashChange","_updateHash"],this),this._updateHash=a(this._updateHashUnthrottled.bind(this),300)};o.prototype.addTo=function(t){return this._map=t,i.addEventListener("hashchange",this._onHashChange,!1),this._map.on("moveend",this._updateHash),this},o.prototype.remove=function(){return i.removeEventListener("hashchange",this._onHashChange,!1),this._map.off("moveend",this._updateHash),delete this._map,this},o.prototype.getHashString=function(t){var e=this._map.getCenter(),r=Math.round(100*this._map.getZoom())/100,n=Math.ceil((r*Math.LN2+Math.log(512/360/.5))/Math.LN10),i=Math.pow(10,n),a=Math.round(e.lng*i)/i,o=Math.round(e.lat*i)/i,s=this._map.getBearing(),l=this._map.getPitch(),c="";return c+=t?"#/"+a+"/"+o+"/"+r:"#"+r+"/"+o+"/"+a,(s||l)&&(c+="/"+Math.round(10*s)/10),l&&(c+="/"+Math.round(l)),c},o.prototype._onHashChange=function(){var t=i.location.hash.replace("#","").split("/");return t.length>=3&&(this._map.jumpTo({center:[+t[2],+t[1]],zoom:+t[0],bearing:+(t[3]||0),pitch:+(t[4]||0)}),!0)},o.prototype._updateHashUnthrottled=function(){var t=this.getHashString();i.history.replaceState("","",t)},e.exports=o},{"../util/throttle":272,"../util/util":275,"../util/window":254}],247:[function(t,e,r){function n(t){t.parentNode&&t.parentNode.removeChild(t)}var i=t("../util/util"),a=t("../util/browser"),o=t("../util/window"),s=t("../util/window"),l=s.HTMLImageElement,c=s.HTMLElement,u=t("../util/dom"),f=t("../util/ajax"),h=t("../style/style"),p=t("../style/evaluation_parameters"),d=t("../render/painter"),g=t("../geo/transform"),m=t("./hash"),v=t("./bind_handlers"),y=t("./camera"),x=t("../geo/lng_lat"),b=t("../geo/lng_lat_bounds"),_=t("@mapbox/point-geometry"),w=t("./control/attribution_control"),k=t("./control/logo_control"),M=t("@mapbox/mapbox-gl-supported"),A=t("../util/image").RGBAImage;t("./events");var T={center:[0,0],zoom:0,bearing:0,pitch:0,minZoom:0,maxZoom:22,interactive:!0,scrollZoom:!0,boxZoom:!0,dragRotate:!0,dragPan:!0,keyboard:!0,doubleClickZoom:!0,touchZoomRotate:!0,bearingSnap:7,hash:!1,attributionControl:!0,failIfMajorPerformanceCaveat:!1,preserveDrawingBuffer:!1,trackResize:!0,renderWorldCopies:!0,refreshExpiredTiles:!0,maxTileCacheSize:null,transformRequest:null,fadeDuration:300},S=function(t){function e(e){if(null!=(e=i.extend({},T,e)).minZoom&&null!=e.maxZoom&&e.minZoom>e.maxZoom)throw new Error("maxZoom must be greater than minZoom");var r=new g(e.minZoom,e.maxZoom,e.renderWorldCopies);t.call(this,r,e),this._interactive=e.interactive,this._maxTileCacheSize=e.maxTileCacheSize,this._failIfMajorPerformanceCaveat=e.failIfMajorPerformanceCaveat,this._preserveDrawingBuffer=e.preserveDrawingBuffer,this._trackResize=e.trackResize,this._bearingSnap=e.bearingSnap,this._refreshExpiredTiles=e.refreshExpiredTiles,this._fadeDuration=e.fadeDuration,this._crossFadingFactor=1,this._collectResourceTiming=e.collectResourceTiming;var n=e.transformRequest;if(this._transformRequest=n?function(t,e){return n(t,e)||{url:t}}:function(t){return{url:t}},"string"==typeof e.container){var a=o.document.getElementById(e.container);if(!a)throw new Error("Container '"+e.container+"' not found.");this._container=a}else{if(!(e.container instanceof c))throw new Error("Invalid type: 'container' must be a String or HTMLElement.");this._container=e.container}e.maxBounds&&this.setMaxBounds(e.maxBounds),i.bindAll(["_onWindowOnline","_onWindowResize","_contextLost","_contextRestored","_update","_render","_onData","_onDataLoading"],this),this._setupContainer(),this._setupPainter(),this.on("move",this._update.bind(this,!1)),this.on("zoom",this._update.bind(this,!0)),void 0!==o&&(o.addEventListener("online",this._onWindowOnline,!1),o.addEventListener("resize",this._onWindowResize,!1)),v(this,e),this._hash=e.hash&&(new m).addTo(this),this._hash&&this._hash._onHashChange()||this.jumpTo({center:e.center,zoom:e.zoom,bearing:e.bearing,pitch:e.pitch}),this.resize(),e.style&&this.setStyle(e.style,{localIdeographFontFamily:e.localIdeographFontFamily}),e.attributionControl&&this.addControl(new w),this.addControl(new k,e.logoPosition),this.on("style.load",function(){this.transform.unmodified&&this.jumpTo(this.style.stylesheet)}),this.on("data",this._onData),this.on("dataloading",this._onDataLoading)}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var r={showTileBoundaries:{},showCollisionBoxes:{},showOverdrawInspector:{},repaint:{},vertices:{}};return e.prototype.addControl=function(t,e){void 0===e&&t.getDefaultPosition&&(e=t.getDefaultPosition()),void 0===e&&(e="top-right");var r=t.onAdd(this),n=this._controlPositions[e];return-1!==e.indexOf("bottom")?n.insertBefore(r,n.firstChild):n.appendChild(r),this},e.prototype.removeControl=function(t){return t.onRemove(this),this},e.prototype.resize=function(){var t=this._containerDimensions(),e=t[0],r=t[1];return this._resizeCanvas(e,r),this.transform.resize(e,r),this.painter.resize(e,r),this.fire("movestart").fire("move").fire("resize").fire("moveend")},e.prototype.getBounds=function(){var t=new b(this.transform.pointLocation(new _(0,this.transform.height)),this.transform.pointLocation(new _(this.transform.width,0)));return(this.transform.angle||this.transform.pitch)&&(t.extend(this.transform.pointLocation(new _(this.transform.size.x,0))),t.extend(this.transform.pointLocation(new _(0,this.transform.size.y)))),t},e.prototype.getMaxBounds=function(){return this.transform.latRange&&2===this.transform.latRange.length&&this.transform.lngRange&&2===this.transform.lngRange.length?new b([this.transform.lngRange[0],this.transform.latRange[0]],[this.transform.lngRange[1],this.transform.latRange[1]]):null},e.prototype.setMaxBounds=function(t){if(t){var e=b.convert(t);this.transform.lngRange=[e.getWest(),e.getEast()],this.transform.latRange=[e.getSouth(),e.getNorth()],this.transform._constrain(),this._update()}else null!=t||(this.transform.lngRange=null,this.transform.latRange=null,this._update());return this},e.prototype.setMinZoom=function(t){if((t=null==t?0:t)>=0&&t<=this.transform.maxZoom)return this.transform.minZoom=t,this._update(),this.getZoom()=this.transform.minZoom)return this.transform.maxZoom=t,this._update(),this.getZoom()>t&&this.setZoom(t),this;throw new Error("maxZoom must be greater than the current minZoom")},e.prototype.getMaxZoom=function(){return this.transform.maxZoom},e.prototype.project=function(t){return this.transform.locationPoint(x.convert(t))},e.prototype.unproject=function(t){return this.transform.pointLocation(_.convert(t))},e.prototype.on=function(e,r,n){var a=this;if(void 0===n)return t.prototype.on.call(this,e,r);var o=function(){if("mouseenter"===e||"mouseover"===e){var t=!1;return{layer:r,listener:n,delegates:{mousemove:function(o){var s=a.getLayer(r)?a.queryRenderedFeatures(o.point,{layers:[r]}):[];s.length?t||(t=!0,n.call(a,i.extend({features:s},o,{type:e}))):t=!1},mouseout:function(){t=!1}}}}if("mouseleave"===e||"mouseout"===e){var o=!1;return{layer:r,listener:n,delegates:{mousemove:function(t){(a.getLayer(r)?a.queryRenderedFeatures(t.point,{layers:[r]}):[]).length?o=!0:o&&(o=!1,n.call(a,i.extend({},t,{type:e})))},mouseout:function(t){o&&(o=!1,n.call(a,i.extend({},t,{type:e})))}}}}var s;return{layer:r,listener:n,delegates:(s={},s[e]=function(t){var e=a.getLayer(r)?a.queryRenderedFeatures(t.point,{layers:[r]}):[];e.length&&n.call(a,i.extend({features:e},t))},s)}}();for(var s in this._delegatedListeners=this._delegatedListeners||{},this._delegatedListeners[e]=this._delegatedListeners[e]||[],this._delegatedListeners[e].push(o),o.delegates)a.on(s,o.delegates[s]);return this},e.prototype.off=function(e,r,n){if(void 0===n)return t.prototype.off.call(this,e,r);if(this._delegatedListeners&&this._delegatedListeners[e])for(var i=this._delegatedListeners[e],a=0;athis._map.transform.height-i?["bottom"]:[],t.xthis._map.transform.width-n/2&&e.push("right"),e=0===e.length?"bottom":e.join("-")}var o=t.add(r[e]).round(),l={top:"translate(-50%,0)","top-left":"translate(0,0)","top-right":"translate(-100%,0)",bottom:"translate(-50%,-100%)","bottom-left":"translate(0,-100%)","bottom-right":"translate(-100%,-100%)",left:"translate(0,-50%)",right:"translate(-100%,-50%)"},u=this._container.classList;for(var f in l)u.remove("mapboxgl-popup-anchor-"+f);u.add("mapboxgl-popup-anchor-"+e),a.setTransform(this._container,l[e]+" translate("+o.x+"px,"+o.y+"px)")}},e.prototype._onClickClose=function(){this.remove()},e}(i);e.exports=f},{"../geo/lng_lat":62,"../util/dom":259,"../util/evented":260,"../util/smart_wrap":270,"../util/util":275,"../util/window":254,"@mapbox/point-geometry":4}],250:[function(t,e,r){var n=t("./util"),i=t("./web_worker_transfer"),a=i.serialize,o=i.deserialize,s=function(t,e,r){this.target=t,this.parent=e,this.mapId=r,this.callbacks={},this.callbackID=0,n.bindAll(["receive"],this),this.target.addEventListener("message",this.receive,!1)};s.prototype.send=function(t,e,r,n){var i=r?this.mapId+":"+this.callbackID++:null;r&&(this.callbacks[i]=r);var o=[];this.target.postMessage({targetMapId:n,sourceMapId:this.mapId,type:t,id:String(i),data:a(e,o)},o)},s.prototype.receive=function(t){var e,r=this,n=t.data,i=n.id;if(!n.targetMapId||this.mapId===n.targetMapId){var s=function(t,e){var n=[];r.target.postMessage({sourceMapId:r.mapId,type:"",id:String(i),error:t?String(t):null,data:a(e,n)},n)};if(""===n.type)e=this.callbacks[n.id],delete this.callbacks[n.id],e&&n.error?e(new Error(n.error)):e&&e(null,o(n.data));else if(void 0!==n.id&&this.parent[n.type])this.parent[n.type](n.sourceMapId,o(n.data),s);else if(void 0!==n.id&&this.parent.getWorkerSource){var l=n.type.split(".");this.parent.getWorkerSource(n.sourceMapId,l[0])[l[1]](o(n.data),s)}else this.parent[n.type](o(n.data))}},s.prototype.remove=function(){this.target.removeEventListener("message",this.receive,!1)},e.exports=s},{"./util":275,"./web_worker_transfer":278}],251:[function(t,e,r){function n(t){var e=new a.XMLHttpRequest;for(var r in e.open("GET",t.url,!0),t.headers)e.setRequestHeader(r,t.headers[r]);return e.withCredentials="include"===t.credentials,e}function i(t){var e=a.document.createElement("a");return e.href=t,e.protocol===a.document.location.protocol&&e.host===a.document.location.host}var a=t("./window"),o={Unknown:"Unknown",Style:"Style",Source:"Source",Tile:"Tile",Glyphs:"Glyphs",SpriteImage:"SpriteImage",SpriteJSON:"SpriteJSON",Image:"Image"};r.ResourceType=o,"function"==typeof Object.freeze&&Object.freeze(o);var s=function(t){function e(e,r){t.call(this,e),this.status=r}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(Error);r.getJSON=function(t,e){var r=n(t);return r.setRequestHeader("Accept","application/json"),r.onerror=function(){e(new Error(r.statusText))},r.onload=function(){if(r.status>=200&&r.status<300&&r.response){var t;try{t=JSON.parse(r.response)}catch(t){return e(t)}e(null,t)}else e(new s(r.statusText,r.status))},r.send(),r},r.getArrayBuffer=function(t,e){var r=n(t);return r.responseType="arraybuffer",r.onerror=function(){e(new Error(r.statusText))},r.onload=function(){var t=r.response;if(0===t.byteLength&&200===r.status)return e(new Error("http status 200 returned without content."));r.status>=200&&r.status<300&&r.response?e(null,{data:t,cacheControl:r.getResponseHeader("Cache-Control"),expires:r.getResponseHeader("Expires")}):e(new s(r.statusText,r.status))},r.send(),r};r.getImage=function(t,e){return r.getArrayBuffer(t,function(t,r){if(t)e(t);else if(r){var n=new a.Image,i=a.URL||a.webkitURL;n.onload=function(){e(null,n),i.revokeObjectURL(n.src)};var o=new a.Blob([new Uint8Array(r.data)],{type:"image/png"});n.cacheControl=r.cacheControl,n.expires=r.expires,n.src=r.data.byteLength?i.createObjectURL(o):"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII="}})},r.getVideo=function(t,e){var r=a.document.createElement("video");r.onloadstart=function(){e(null,r)};for(var n=0;n1)for(var f=0;f0||this._oneTimeListeners&&this._oneTimeListeners[t]&&this._oneTimeListeners[t].length>0||this._eventedParent&&this._eventedParent.listens(t)},o.prototype.setEventedParent=function(t,e){return this._eventedParent=t,this._eventedParentData=e,this},e.exports=o},{"./util":275}],261:[function(t,e,r){function n(t,e){return e.max-t.max}function i(t,e,r,n){this.p=new o(t,e),this.h=r,this.d=function(t,e){for(var r=!1,n=1/0,i=0;it.y!=f.y>t.y&&t.x<(f.x-u.x)*(t.y-u.y)/(f.y-u.y)+u.x&&(r=!r),n=Math.min(n,s(t,u,f))}return(r?1:-1)*Math.sqrt(n)}(this.p,n),this.max=this.d+this.h*Math.SQRT2}var a=t("tinyqueue"),o=t("@mapbox/point-geometry"),s=t("./intersection_tests").distToSegmentSquared;e.exports=function(t,e,r){void 0===e&&(e=1),void 0===r&&(r=!1);for(var s=1/0,l=1/0,c=-1/0,u=-1/0,f=t[0],h=0;hc)&&(c=p.x),(!h||p.y>u)&&(u=p.y)}var d=c-s,g=u-l,m=Math.min(d,g),v=m/2,y=new a(null,n);if(0===m)return new o(s,l);for(var x=s;x_.d||!_.d)&&(_=k,r&&console.log("found best %d after %d probes",Math.round(1e4*k.d)/1e4,w)),k.max-_.d<=e||(v=k.h/2,y.push(new i(k.p.x-v,k.p.y-v,v,t)),y.push(new i(k.p.x+v,k.p.y-v,v,t)),y.push(new i(k.p.x-v,k.p.y+v,v,t)),y.push(new i(k.p.x+v,k.p.y+v,v,t)),w+=4)}return r&&(console.log("num probes: "+w),console.log("best distance: "+_.d)),_.p}},{"./intersection_tests":264,"@mapbox/point-geometry":4,tinyqueue:33}],262:[function(t,e,r){var n,i=t("./worker_pool");e.exports=function(){return n||(n=new i),n}},{"./worker_pool":279}],263:[function(t,e,r){function n(t,e,r,n){var i=e.width,a=e.height;if(n){if(n.length!==i*a*r)throw new RangeError("mismatched image size")}else n=new Uint8Array(i*a*r);return t.width=i,t.height=a,t.data=n,t}function i(t,e,r){var i=e.width,o=e.height;if(i!==t.width||o!==t.height){var s=n({},{width:i,height:o},r);a(t,s,{x:0,y:0},{x:0,y:0},{width:Math.min(t.width,i),height:Math.min(t.height,o)},r),t.width=i,t.height=o,t.data=s.data}}function a(t,e,r,n,i,a){if(0===i.width||0===i.height)return e;if(i.width>t.width||i.height>t.height||r.x>t.width-i.width||r.y>t.height-i.height)throw new RangeError("out of range source coordinates for image copy");if(i.width>e.width||i.height>e.height||n.x>e.width-i.width||n.y>e.height-i.height)throw new RangeError("out of range destination coordinates for image copy");for(var o=t.data,s=e.data,l=0;l1){if(i(t,e))return!0;for(var n=0;n1?t.distSqr(r):t.distSqr(r.sub(e)._mult(i)._add(e))}function l(t,e){for(var r,n,i,a=!1,o=0;oe.y!=i.y>e.y&&e.x<(i.x-n.x)*(e.y-n.y)/(i.y-n.y)+n.x&&(a=!a);return a}function c(t,e){for(var r=!1,n=0,i=t.length-1;ne.y!=o.y>e.y&&e.x<(o.x-a.x)*(e.y-a.y)/(o.y-a.y)+a.x&&(r=!r)}return r}var u=t("./util").isCounterClockwise;e.exports={multiPolygonIntersectsBufferedMultiPoint:function(t,e,r){for(var n=0;n=3)for(var l=0;l=128&&t<=255},Arabic:function(t){return t>=1536&&t<=1791},"Arabic Supplement":function(t){return t>=1872&&t<=1919},"Arabic Extended-A":function(t){return t>=2208&&t<=2303},"Hangul Jamo":function(t){return t>=4352&&t<=4607},"Unified Canadian Aboriginal Syllabics":function(t){return t>=5120&&t<=5759},"Unified Canadian Aboriginal Syllabics Extended":function(t){return t>=6320&&t<=6399},"General Punctuation":function(t){return t>=8192&&t<=8303},"Letterlike Symbols":function(t){return t>=8448&&t<=8527},"Number Forms":function(t){return t>=8528&&t<=8591},"Miscellaneous Technical":function(t){return t>=8960&&t<=9215},"Control Pictures":function(t){return t>=9216&&t<=9279},"Optical Character Recognition":function(t){return t>=9280&&t<=9311},"Enclosed Alphanumerics":function(t){return t>=9312&&t<=9471},"Geometric Shapes":function(t){return t>=9632&&t<=9727},"Miscellaneous Symbols":function(t){return t>=9728&&t<=9983},"Miscellaneous Symbols and Arrows":function(t){return t>=11008&&t<=11263},"CJK Radicals Supplement":function(t){return t>=11904&&t<=12031},"Kangxi Radicals":function(t){return t>=12032&&t<=12255},"Ideographic Description Characters":function(t){return t>=12272&&t<=12287},"CJK Symbols and Punctuation":function(t){return t>=12288&&t<=12351},Hiragana:function(t){return t>=12352&&t<=12447},Katakana:function(t){return t>=12448&&t<=12543},Bopomofo:function(t){return t>=12544&&t<=12591},"Hangul Compatibility Jamo":function(t){return t>=12592&&t<=12687},Kanbun:function(t){return t>=12688&&t<=12703},"Bopomofo Extended":function(t){return t>=12704&&t<=12735},"CJK Strokes":function(t){return t>=12736&&t<=12783},"Katakana Phonetic Extensions":function(t){return t>=12784&&t<=12799},"Enclosed CJK Letters and Months":function(t){return t>=12800&&t<=13055},"CJK Compatibility":function(t){return t>=13056&&t<=13311},"CJK Unified Ideographs Extension A":function(t){return t>=13312&&t<=19903},"Yijing Hexagram Symbols":function(t){return t>=19904&&t<=19967},"CJK Unified Ideographs":function(t){return t>=19968&&t<=40959},"Yi Syllables":function(t){return t>=40960&&t<=42127},"Yi Radicals":function(t){return t>=42128&&t<=42191},"Hangul Jamo Extended-A":function(t){return t>=43360&&t<=43391},"Hangul Syllables":function(t){return t>=44032&&t<=55215},"Hangul Jamo Extended-B":function(t){return t>=55216&&t<=55295},"Private Use Area":function(t){return t>=57344&&t<=63743},"CJK Compatibility Ideographs":function(t){return t>=63744&&t<=64255},"Arabic Presentation Forms-A":function(t){return t>=64336&&t<=65023},"Vertical Forms":function(t){return t>=65040&&t<=65055},"CJK Compatibility Forms":function(t){return t>=65072&&t<=65103},"Small Form Variants":function(t){return t>=65104&&t<=65135},"Arabic Presentation Forms-B":function(t){return t>=65136&&t<=65279},"Halfwidth and Fullwidth Forms":function(t){return t>=65280&&t<=65519}}},{}],266:[function(t,e,r){var n=function(t,e){this.max=t,this.onRemove=e,this.reset()};n.prototype.reset=function(){var t=this;for(var e in t.data)t.onRemove(t.data[e]);return this.data={},this.order=[],this},n.prototype.add=function(t,e){if(this.has(t))this.order.splice(this.order.indexOf(t),1),this.data[t]=e,this.order.push(t);else if(this.data[t]=e,this.order.push(t),this.order.length>this.max){var r=this.getAndRemove(this.order[0]);r&&this.onRemove(r)}return this},n.prototype.has=function(t){return t in this.data},n.prototype.keys=function(){return this.order},n.prototype.getAndRemove=function(t){if(!this.has(t))return null;var e=this.data[t];return delete this.data[t],this.order.splice(this.order.indexOf(t),1),e},n.prototype.get=function(t){return this.has(t)?this.data[t]:null},n.prototype.remove=function(t){if(!this.has(t))return this;var e=this.data[t];return delete this.data[t],this.onRemove(e),this.order.splice(this.order.indexOf(t),1),this},n.prototype.setMaxSize=function(t){var e=this;for(this.max=t;this.order.length>this.max;){var r=e.getAndRemove(e.order[0]);r&&e.onRemove(r)}return this},e.exports=n},{}],267:[function(t,e,r){function n(t,e){var r=a(s.API_URL);if(t.protocol=r.protocol,t.authority=r.authority,"/"!==r.path&&(t.path=""+r.path+t.path),!s.REQUIRE_ACCESS_TOKEN)return o(t);if(!(e=e||s.ACCESS_TOKEN))throw new Error("An API access token is required to use Mapbox GL. "+c);if("s"===e[0])throw new Error("Use a public access token (pk.*) with Mapbox GL, not a secret access token (sk.*). "+c);return t.params.push("access_token="+e),o(t)}function i(t){return 0===t.indexOf("mapbox:")}function a(t){var e=t.match(f);if(!e)throw new Error("Unable to parse URL object");return{protocol:e[1],authority:e[2],path:e[3]||"/",params:e[4]?e[4].split("&"):[]}}function o(t){var e=t.params.length?"?"+t.params.join("&"):"";return t.protocol+"://"+t.authority+t.path+e}var s=t("./config"),l=t("./browser"),c="See https://www.mapbox.com/api-documentation/#access-tokens";r.isMapboxURL=i,r.normalizeStyleURL=function(t,e){if(!i(t))return t;var r=a(t);return r.path="/styles/v1"+r.path,n(r,e)},r.normalizeGlyphsURL=function(t,e){if(!i(t))return t;var r=a(t);return r.path="/fonts/v1"+r.path,n(r,e)},r.normalizeSourceURL=function(t,e){if(!i(t))return t;var r=a(t);return r.path="/v4/"+r.authority+".json",r.params.push("secure"),n(r,e)},r.normalizeSpriteURL=function(t,e,r,s){var l=a(t);return i(t)?(l.path="/styles/v1"+l.path+"/sprite"+e+r,n(l,s)):(l.path+=""+e+r,o(l))};var u=/(\.(png|jpg)\d*)(?=$)/;r.normalizeTileURL=function(t,e,r){if(!e||!i(e))return t;var n=a(t),c=l.devicePixelRatio>=2||512===r?"@2x":"",f=l.supportsWebp?".webp":"$1";return n.path=n.path.replace(u,""+c+f),function(t){for(var e=0;e=65097&&t<=65103)||n["CJK Compatibility Ideographs"](t)||n["CJK Compatibility"](t)||n["CJK Radicals Supplement"](t)||n["CJK Strokes"](t)||!(!n["CJK Symbols and Punctuation"](t)||t>=12296&&t<=12305||t>=12308&&t<=12319||12336===t)||n["CJK Unified Ideographs Extension A"](t)||n["CJK Unified Ideographs"](t)||n["Enclosed CJK Letters and Months"](t)||n["Hangul Compatibility Jamo"](t)||n["Hangul Jamo Extended-A"](t)||n["Hangul Jamo Extended-B"](t)||n["Hangul Jamo"](t)||n["Hangul Syllables"](t)||n.Hiragana(t)||n["Ideographic Description Characters"](t)||n.Kanbun(t)||n["Kangxi Radicals"](t)||n["Katakana Phonetic Extensions"](t)||n.Katakana(t)&&12540!==t||!(!n["Halfwidth and Fullwidth Forms"](t)||65288===t||65289===t||65293===t||t>=65306&&t<=65310||65339===t||65341===t||65343===t||t>=65371&&t<=65503||65507===t||t>=65512&&t<=65519)||!(!n["Small Form Variants"](t)||t>=65112&&t<=65118||t>=65123&&t<=65126)||n["Unified Canadian Aboriginal Syllabics"](t)||n["Unified Canadian Aboriginal Syllabics Extended"](t)||n["Vertical Forms"](t)||n["Yijing Hexagram Symbols"](t)||n["Yi Syllables"](t)||n["Yi Radicals"](t)))},r.charHasNeutralVerticalOrientation=function(t){return!!(n["Latin-1 Supplement"](t)&&(167===t||169===t||174===t||177===t||188===t||189===t||190===t||215===t||247===t)||n["General Punctuation"](t)&&(8214===t||8224===t||8225===t||8240===t||8241===t||8251===t||8252===t||8258===t||8263===t||8264===t||8265===t||8273===t)||n["Letterlike Symbols"](t)||n["Number Forms"](t)||n["Miscellaneous Technical"](t)&&(t>=8960&&t<=8967||t>=8972&&t<=8991||t>=8996&&t<=9e3||9003===t||t>=9085&&t<=9114||t>=9150&&t<=9165||9167===t||t>=9169&&t<=9179||t>=9186&&t<=9215)||n["Control Pictures"](t)&&9251!==t||n["Optical Character Recognition"](t)||n["Enclosed Alphanumerics"](t)||n["Geometric Shapes"](t)||n["Miscellaneous Symbols"](t)&&!(t>=9754&&t<=9759)||n["Miscellaneous Symbols and Arrows"](t)&&(t>=11026&&t<=11055||t>=11088&&t<=11097||t>=11192&&t<=11243)||n["CJK Symbols and Punctuation"](t)||n.Katakana(t)||n["Private Use Area"](t)||n["CJK Compatibility Forms"](t)||n["Small Form Variants"](t)||n["Halfwidth and Fullwidth Forms"](t)||8734===t||8756===t||8757===t||t>=9984&&t<=10087||t>=10102&&t<=10131||65532===t||65533===t)},r.charHasRotatedVerticalOrientation=function(t){return!(r.charHasUprightVerticalOrientation(t)||r.charHasNeutralVerticalOrientation(t))}},{"./is_char_in_unicode_block":265}],270:[function(t,e,r){var n=t("../geo/lng_lat");e.exports=function(t,e,r){if(t=new n(t.lng,t.lat),e){var i=new n(t.lng-360,t.lat),a=new n(t.lng+360,t.lat),o=r.locationPoint(t).distSqr(e);r.locationPoint(i).distSqr(e)180;){var s=r.locationPoint(t);if(s.x>=0&&s.y>=0&&s.x<=r.width&&s.y<=r.height)break;t.lng>r.center.lng?t.lng-=360:t.lng+=360}return t}},{"../geo/lng_lat":62}],271:[function(t,e,r){function n(t,e){return Math.ceil(t/e)*e}var i={Int8:Int8Array,Uint8:Uint8Array,Int16:Int16Array,Uint16:Uint16Array,Int32:Int32Array,Uint32:Uint32Array,Float32:Float32Array},a=function(){this.isTransferred=!1,this.capacity=-1,this.resize(0)};a.serialize=function(t,e){return t._trim(),e&&(t.isTransferred=!0,e.push(t.arrayBuffer)),{length:t.length,arrayBuffer:t.arrayBuffer}},a.deserialize=function(t){var e=Object.create(this.prototype);return e.arrayBuffer=t.arrayBuffer,e.length=t.length,e.capacity=t.arrayBuffer.byteLength/e.bytesPerElement,e._refreshViews(),e},a.prototype._trim=function(){this.length!==this.capacity&&(this.capacity=this.length,this.arrayBuffer=this.arrayBuffer.slice(0,this.length*this.bytesPerElement),this._refreshViews())},a.prototype.clear=function(){this.length=0},a.prototype.resize=function(t){this.reserve(t),this.length=t},a.prototype.reserve=function(t){if(t>this.capacity){this.capacity=Math.max(t,Math.floor(5*this.capacity),128),this.arrayBuffer=new ArrayBuffer(this.capacity*this.bytesPerElement);var e=this.uint8;this._refreshViews(),e&&this.uint8.set(e)}},a.prototype._refreshViews=function(){throw new Error("_refreshViews() must be implemented by each concrete StructArray layout")},e.exports.StructArray=a,e.exports.Struct=function(t,e){this._structArray=t,this._pos1=e*this.size,this._pos2=this._pos1/2,this._pos4=this._pos1/4,this._pos8=this._pos1/8},e.exports.viewTypes=i,e.exports.createLayout=function(t,e){void 0===e&&(e=1);var r=0,a=0;return{members:t.map(function(t){var o=function(t){return i[t].BYTES_PER_ELEMENT}(t.type),s=r=n(r,Math.max(e,o)),l=t.components||1;return a=Math.max(a,o),r+=o*l,{name:t.name,type:t.type,components:l,offset:s}}),size:n(r,Math.max(a,e)),alignment:e}}},{}],272:[function(t,e,r){e.exports=function(t,e){var r=!1,n=0,i=function(){n=0,r&&(t(),n=setTimeout(i,e),r=!1)};return function(){return r=!0,n||i(),n}}},{}],273:[function(t,e,r){function n(t,e){if(t.row>e.row){var r=t;t=e,e=r}return{x0:t.column,y0:t.row,x1:e.column,y1:e.row,dx:e.column-t.column,dy:e.row-t.row}}function i(t,e,r,n,i){var a=Math.max(r,Math.floor(e.y0)),o=Math.min(n,Math.ceil(e.y1));if(t.x0===e.x0&&t.y0===e.y0?t.x0+e.dy/t.dy*t.dx0,f=e.dx<0,h=a;hu.dy&&(l=c,c=u,u=l),c.dy>f.dy&&(l=c,c=f,f=l),u.dy>f.dy&&(l=u,u=f,f=l),c.dy&&i(f,c,a,o,s),u.dy&&i(f,u,a,o,s)}t("../geo/coordinate");var o=t("../source/tile_id").OverscaledTileID;e.exports=function(t,e,r,n){function i(e,i,a){var c,u,f;if(a>=0&&a<=s)for(c=e;c=1)return 1;var e=t*t,r=e*t;return 4*(t<.5?r:3*(t-e)+r-.75)},r.bezier=function(t,e,r,i){var a=new n(t,e,r,i);return function(t){return a.solve(t)}},r.ease=r.bezier(.25,.1,.25,1),r.clamp=function(t,e,r){return Math.min(r,Math.max(e,t))},r.wrap=function(t,e,r){var n=r-e,i=((t-e)%n+n)%n+e;return i===e?r:i},r.asyncAll=function(t,e,r){if(!t.length)return r(null,[]);var n=t.length,i=new Array(t.length),a=null;t.forEach(function(t,o){e(t,function(t,e){t&&(a=t),i[o]=e,0==--n&&r(a,i)})})},r.values=function(t){var e=[];for(var r in t)e.push(t[r]);return e},r.keysDifference=function(t,e){var r=[];for(var n in t)n in e||r.push(n);return r},r.extend=function(t){for(var e=arguments,r=[],n=arguments.length-1;n-- >0;)r[n]=e[n+1];for(var i=0,a=r;i=0)return!0;return!1};var o={};r.warnOnce=function(t){o[t]||("undefined"!=typeof console&&console.warn(t),o[t]=!0)},r.isCounterClockwise=function(t,e,r){return(r.y-t.y)*(e.x-t.x)>(e.y-t.y)*(r.x-t.x)},r.calculateSignedArea=function(t){for(var e=0,r=0,n=t.length,i=n-1,a=void 0,o=void 0;r0||Math.abs(e.y-n.y)>0)&&Math.abs(r.calculateSignedArea(t))>.01},r.sphericalToCartesian=function(t){var e=t[0],r=t[1],n=t[2];return r+=90,r*=Math.PI/180,n*=Math.PI/180,{x:e*Math.cos(r)*Math.sin(n),y:e*Math.sin(r)*Math.sin(n),z:e*Math.cos(n)}},r.parseCacheControl=function(t){var e={};if(t.replace(/(?:^|(?:\s*\,\s*))([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)(?:\=(?:([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)|(?:\"((?:[^"\\]|\\.)*)\")))?/g,function(t,r,n,i){var a=n||i;return e[r]=!a||a.toLowerCase(),""}),e["max-age"]){var r=parseInt(e["max-age"],10);isNaN(r)?delete e["max-age"]:e["max-age"]=r}return e}},{"../geo/coordinate":61,"../style-spec/util/deep_equal":155,"@mapbox/point-geometry":4,"@mapbox/unitbezier":7}],276:[function(t,e,r){var n=function(t,e,r,n){this.type="Feature",this._vectorTileFeature=t,t._z=e,t._x=r,t._y=n,this.properties=t.properties,null!=t.id&&(this.id=t.id)},i={geometry:{}};i.geometry.get=function(){return void 0===this._geometry&&(this._geometry=this._vectorTileFeature.toGeoJSON(this._vectorTileFeature._x,this._vectorTileFeature._y,this._vectorTileFeature._z).geometry),this._geometry},i.geometry.set=function(t){this._geometry=t},n.prototype.toJSON=function(){var t={geometry:this.geometry};for(var e in this)"_geometry"!==e&&"_vectorTileFeature"!==e&&(t[e]=this[e]);return t},Object.defineProperties(n.prototype,i),e.exports=n},{}],277:[function(t,e,r){var n=t("./script_detection");e.exports=function(t){for(var r="",i=0;i":"\ufe40","?":"\ufe16","@":"\uff20","[":"\ufe47","\\":"\uff3c","]":"\ufe48","^":"\uff3e",_:"\ufe33","`":"\uff40","{":"\ufe37","|":"\u2015","}":"\ufe38","~":"\uff5e","\xa2":"\uffe0","\xa3":"\uffe1","\xa5":"\uffe5","\xa6":"\uffe4","\xac":"\uffe2","\xaf":"\uffe3","\u2013":"\ufe32","\u2014":"\ufe31","\u2018":"\ufe43","\u2019":"\ufe44","\u201c":"\ufe41","\u201d":"\ufe42","\u2026":"\ufe19","\u2027":"\u30fb","\u20a9":"\uffe6","\u3001":"\ufe11","\u3002":"\ufe12","\u3008":"\ufe3f","\u3009":"\ufe40","\u300a":"\ufe3d","\u300b":"\ufe3e","\u300c":"\ufe41","\u300d":"\ufe42","\u300e":"\ufe43","\u300f":"\ufe44","\u3010":"\ufe3b","\u3011":"\ufe3c","\u3014":"\ufe39","\u3015":"\ufe3a","\u3016":"\ufe17","\u3017":"\ufe18","\uff01":"\ufe15","\uff08":"\ufe35","\uff09":"\ufe36","\uff0c":"\ufe10","\uff0d":"\ufe32","\uff0e":"\u30fb","\uff1a":"\ufe13","\uff1b":"\ufe14","\uff1c":"\ufe3f","\uff1e":"\ufe40","\uff1f":"\ufe16","\uff3b":"\ufe47","\uff3d":"\ufe48","\uff3f":"\ufe33","\uff5b":"\ufe37","\uff5c":"\u2015","\uff5d":"\ufe38","\uff5f":"\ufe35","\uff60":"\ufe36","\uff61":"\ufe12","\uff62":"\ufe41","\uff63":"\ufe42"}},{"./script_detection":269}],278:[function(t,e,r){function n(t,e,r){void 0===r&&(r={}),Object.defineProperty(e,"_classRegistryKey",{value:t,writeable:!1}),g[t]={klass:e,omit:r.omit||[],shallow:r.shallow||[]}}var i=t("grid-index"),a=t("../style-spec/util/color"),o=t("../style-spec/expression"),s=o.StylePropertyFunction,l=o.StyleExpression,c=o.StyleExpressionWithErrorHandling,u=o.ZoomDependentExpression,f=o.ZoomConstantExpression,h=t("../style-spec/expression/compound_expression").CompoundExpression,p=t("../style-spec/expression/definitions"),d=t("./window").ImageData,g={};for(var m in n("Object",Object),i.serialize=function(t,e){var r=t.toArrayBuffer();return e&&e.push(r),r},i.deserialize=function(t){return new i(t)},n("Grid",i),n("Color",a),n("StylePropertyFunction",s),n("StyleExpression",l,{omit:["_evaluator"]}),n("StyleExpressionWithErrorHandling",c,{omit:["_evaluator"]}),n("ZoomDependentExpression",u),n("ZoomConstantExpression",f),n("CompoundExpression",h,{omit:["_evaluate"]}),p)p[m]._classRegistryKey||n("Expression_"+m,p[m]);e.exports={register:n,serialize:function t(e,r){if(null==e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||e instanceof Boolean||e instanceof Number||e instanceof String||e instanceof Date||e instanceof RegExp)return e;if(e instanceof ArrayBuffer)return r&&r.push(e),e;if(ArrayBuffer.isView(e)){var n=e;return r&&r.push(n.buffer),n}if(e instanceof d)return r&&r.push(e.data.buffer),e;if(Array.isArray(e)){for(var i=[],a=0,o=e;a=0)){var h=e[f];u[f]=g[c].shallow.indexOf(f)>=0?h:t(h,r)}return{name:c,properties:u}}throw new Error("can't serialize object of type "+typeof e)},deserialize:function t(e){if(null==e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||e instanceof Boolean||e instanceof Number||e instanceof String||e instanceof Date||e instanceof RegExp||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||e instanceof d)return e;if(Array.isArray(e))return e.map(function(e){return t(e)});if("object"==typeof e){var r=e,n=r.name,i=r.properties;if(!n)throw new Error("can't deserialize object of anonymous class");var a=g[n].klass;if(!a)throw new Error("can't deserialize unregistered class "+n);if(a.deserialize)return a.deserialize(i._serialized);for(var o=Object.create(a.prototype),s=0,l=Object.keys(i);s=0?i[c]:t(i[c])}return o}throw new Error("can't deserialize object of type "+typeof e)}}},{"../style-spec/expression":139,"../style-spec/expression/compound_expression":123,"../style-spec/expression/definitions":131,"../style-spec/util/color":153,"./window":254,"grid-index":24}],279:[function(t,e,r){var n=t("./web_worker"),i=function(){this.active={}};i.prototype.acquire=function(e){if(!this.workers){var r=t("../").workerCount;for(this.workers=[];this.workers.lengthp[1][2]&&(v[0]=-v[0]),p[0][2]>p[2][0]&&(v[1]=-v[1]),p[1][0]>p[0][1]&&(v[2]=-v[2]),!0}},{"./normalize":372,"gl-mat4/clone":229,"gl-mat4/create":230,"gl-mat4/determinant":231,"gl-mat4/invert":235,"gl-mat4/transpose":245,"gl-vec3/cross":290,"gl-vec3/dot":293,"gl-vec3/length":298,"gl-vec3/normalize":304}],372:[function(t,e,r){e.exports=function(t,e){var r=e[15];if(0===r)return!1;for(var n=1/r,i=0;i<16;i++)t[i]=e[i]*n;return!0}},{}],373:[function(t,e,r){var n=t("gl-vec3/lerp"),i=t("mat4-recompose"),a=t("mat4-decompose"),o=t("gl-mat4/determinant"),s=t("quat-slerp"),l=f(),c=f(),u=f();function f(){return{translate:h(),scale:h(1),skew:h(),perspective:[0,0,0,1],quaternion:[0,0,0,1]}}function h(t){return[t||0,t||0,t||0]}e.exports=function(t,e,r,f){if(0===o(e)||0===o(r))return!1;var h=a(e,l.translate,l.scale,l.skew,l.perspective,l.quaternion),p=a(r,c.translate,c.scale,c.skew,c.perspective,c.quaternion);return!(!h||!p||(n(u.translate,l.translate,c.translate,f),n(u.skew,l.skew,c.skew,f),n(u.scale,l.scale,c.scale,f),n(u.perspective,l.perspective,c.perspective,f),s(u.quaternion,l.quaternion,c.quaternion,f),i(t,u.translate,u.scale,u.skew,u.perspective,u.quaternion),0))}},{"gl-mat4/determinant":231,"gl-vec3/lerp":299,"mat4-decompose":371,"mat4-recompose":374,"quat-slerp":425}],374:[function(t,e,r){var n={identity:t("gl-mat4/identity"),translate:t("gl-mat4/translate"),multiply:t("gl-mat4/multiply"),create:t("gl-mat4/create"),scale:t("gl-mat4/scale"),fromRotationTranslation:t("gl-mat4/fromRotationTranslation")},i=(n.create(),n.create());e.exports=function(t,e,r,a,o,s){return n.identity(t),n.fromRotationTranslation(t,s,e),t[3]=o[0],t[7]=o[1],t[11]=o[2],t[15]=o[3],n.identity(i),0!==a[2]&&(i[9]=a[2],n.multiply(t,t,i)),0!==a[1]&&(i[9]=0,i[8]=a[1],n.multiply(t,t,i)),0!==a[0]&&(i[8]=0,i[4]=a[0],n.multiply(t,t,i)),n.scale(t,t,r),t}},{"gl-mat4/create":230,"gl-mat4/fromRotationTranslation":233,"gl-mat4/identity":234,"gl-mat4/multiply":237,"gl-mat4/scale":243,"gl-mat4/translate":244}],375:[function(t,e,r){"use strict";e.exports=Math.log2||function(t){return Math.log(t)*Math.LOG2E}},{}],376:[function(t,e,r){"use strict";var n=t("binary-search-bounds"),i=t("mat4-interpolate"),a=t("gl-mat4/invert"),o=t("gl-mat4/rotateX"),s=t("gl-mat4/rotateY"),l=t("gl-mat4/rotateZ"),c=t("gl-mat4/lookAt"),u=t("gl-mat4/translate"),f=(t("gl-mat4/scale"),t("gl-vec3/normalize")),h=[0,0,0];function p(t){this._components=t.slice(),this._time=[0],this.prevMatrix=t.slice(),this.nextMatrix=t.slice(),this.computedMatrix=t.slice(),this.computedInverse=t.slice(),this.computedEye=[0,0,0],this.computedUp=[0,0,0],this.computedCenter=[0,0,0],this.computedRadius=[0],this._limits=[-1/0,1/0]}e.exports=function(t){return new p((t=t||{}).matrix||[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])};var d=p.prototype;d.recalcMatrix=function(t){var e=this._time,r=n.le(e,t),o=this.computedMatrix;if(!(r<0)){var s=this._components;if(r===e.length-1)for(var l=16*r,c=0;c<16;++c)o[c]=s[l++];else{var u=e[r+1]-e[r],h=(l=16*r,this.prevMatrix),p=!0;for(c=0;c<16;++c)h[c]=s[l++];var d=this.nextMatrix;for(c=0;c<16;++c)d[c]=s[l++],p=p&&h[c]===d[c];if(u<1e-6||p)for(c=0;c<16;++c)o[c]=h[c];else i(o,h,d,(t-e[r])/u)}var g=this.computedUp;g[0]=o[1],g[1]=o[5],g[2]=o[9],f(g,g);var m=this.computedInverse;a(m,o);var v=this.computedEye,y=m[15];v[0]=m[12]/y,v[1]=m[13]/y,v[2]=m[14]/y;var x=this.computedCenter,b=Math.exp(this.computedRadius[0]);for(c=0;c<3;++c)x[c]=v[c]-o[2+4*c]*b}},d.idle=function(t){if(!(t1&&n(t[o[u-2]],t[o[u-1]],c)<=0;)u-=1,o.pop();for(o.push(l),u=s.length;u>1&&n(t[s[u-2]],t[s[u-1]],c)>=0;)u-=1,s.pop();s.push(l)}for(var r=new Array(s.length+o.length-2),f=0,i=0,h=o.length;i0;--p)r[f++]=s[p];return r};var n=t("robust-orientation")[3]},{"robust-orientation":446}],378:[function(t,e,r){"use strict";e.exports=function(t,e){e||(e=t,t=window);var r=0,i=0,a=0,o={shift:!1,alt:!1,control:!1,meta:!1},s=!1;function l(t){var e=!1;return"altKey"in t&&(e=e||t.altKey!==o.alt,o.alt=!!t.altKey),"shiftKey"in t&&(e=e||t.shiftKey!==o.shift,o.shift=!!t.shiftKey),"ctrlKey"in t&&(e=e||t.ctrlKey!==o.control,o.control=!!t.ctrlKey),"metaKey"in t&&(e=e||t.metaKey!==o.meta,o.meta=!!t.metaKey),e}function c(t,s){var c=n.x(s),u=n.y(s);"buttons"in s&&(t=0|s.buttons),(t!==r||c!==i||u!==a||l(s))&&(r=0|t,i=c||0,a=u||0,e&&e(r,i,a,o))}function u(t){c(0,t)}function f(){(r||i||a||o.shift||o.alt||o.meta||o.control)&&(i=a=0,r=0,o.shift=o.alt=o.control=o.meta=!1,e&&e(0,0,0,o))}function h(t){l(t)&&e&&e(r,i,a,o)}function p(t){0===n.buttons(t)?c(0,t):c(r,t)}function d(t){c(r|n.buttons(t),t)}function g(t){c(r&~n.buttons(t),t)}function m(){s||(s=!0,t.addEventListener("mousemove",p),t.addEventListener("mousedown",d),t.addEventListener("mouseup",g),t.addEventListener("mouseleave",u),t.addEventListener("mouseenter",u),t.addEventListener("mouseout",u),t.addEventListener("mouseover",u),t.addEventListener("blur",f),t.addEventListener("keyup",h),t.addEventListener("keydown",h),t.addEventListener("keypress",h),t!==window&&(window.addEventListener("blur",f),window.addEventListener("keyup",h),window.addEventListener("keydown",h),window.addEventListener("keypress",h)))}m();var v={element:t};return Object.defineProperties(v,{enabled:{get:function(){return s},set:function(e){e?m():s&&(s=!1,t.removeEventListener("mousemove",p),t.removeEventListener("mousedown",d),t.removeEventListener("mouseup",g),t.removeEventListener("mouseleave",u),t.removeEventListener("mouseenter",u),t.removeEventListener("mouseout",u),t.removeEventListener("mouseover",u),t.removeEventListener("blur",f),t.removeEventListener("keyup",h),t.removeEventListener("keydown",h),t.removeEventListener("keypress",h),t!==window&&(window.removeEventListener("blur",f),window.removeEventListener("keyup",h),window.removeEventListener("keydown",h),window.removeEventListener("keypress",h)))},enumerable:!0},buttons:{get:function(){return r},enumerable:!0},x:{get:function(){return i},enumerable:!0},y:{get:function(){return a},enumerable:!0},mods:{get:function(){return o},enumerable:!0}}),v};var n=t("mouse-event")},{"mouse-event":380}],379:[function(t,e,r){var n={left:0,top:0};e.exports=function(t,e,r){e=e||t.currentTarget||t.srcElement,Array.isArray(r)||(r=[0,0]);var i=t.clientX||0,a=t.clientY||0,o=(s=e,s===window||s===document||s===document.body?n:s.getBoundingClientRect());var s;return r[0]=i-o.left,r[1]=a-o.top,r}},{}],380:[function(t,e,r){"use strict";function n(t){return t.target||t.srcElement||window}r.buttons=function(t){if("object"==typeof t){if("buttons"in t)return t.buttons;if("which"in t){if(2===(e=t.which))return 4;if(3===e)return 2;if(e>0)return 1<=0)return 1< 0");"function"!=typeof t.vertex&&e("Must specify vertex creation function");"function"!=typeof t.cell&&e("Must specify cell creation function");"function"!=typeof t.phase&&e("Must specify phase function");for(var C=t.getters||[],E=new Array(T),L=0;L=0?E[L]=!0:E[L]=!1;return function(t,e,r,T,S,C){var E=C.length,L=S.length;if(L<2)throw new Error("ndarray-extract-contour: Dimension must be at least 2");for(var z="extractContour"+S.join("_"),P=[],D=[],O=[],I=0;I0&&N.push(l(I,S[R-1])+"*"+s(S[R-1])),D.push(d(I,S[R])+"=("+N.join("-")+")|0")}for(var I=0;I=0;--I)j.push(s(S[I]));D.push(w+"=("+j.join("*")+")|0",b+"=mallocUint32("+w+")",x+"=mallocUint32("+w+")",k+"=0"),D.push(g(0)+"=0");for(var R=1;R<1<0;M=M-1&d)w.push(x+"["+k+"+"+v(M)+"]");w.push(y(0));for(var M=0;M=0;--e)G(e,0);for(var r=[],e=0;e0){",p(S[e]),"=1;");t(e-1,r|1<=0?s.push("0"):e.indexOf(-(l+1))>=0?s.push("s["+l+"]-1"):(s.push("-1"),a.push("1"),o.push("s["+l+"]-2"));var c=".lo("+a.join()+").hi("+o.join()+")";if(0===a.length&&(c=""),i>0){n.push("if(1");for(var l=0;l=0||e.indexOf(-(l+1))>=0||n.push("&&s[",l,"]>2");n.push("){grad",i,"(src.pick(",s.join(),")",c);for(var l=0;l=0||e.indexOf(-(l+1))>=0||n.push(",dst.pick(",s.join(),",",l,")",c);n.push(");")}for(var l=0;l1){dst.set(",s.join(),",",u,",0.5*(src.get(",h.join(),")-src.get(",p.join(),")))}else{dst.set(",s.join(),",",u,",0)};"):n.push("if(s[",u,"]>1){diff(",f,",src.pick(",h.join(),")",c,",src.pick(",p.join(),")",c,");}else{zero(",f,");};");break;case"mirror":0===i?n.push("dst.set(",s.join(),",",u,",0);"):n.push("zero(",f,");");break;case"wrap":var d=s.slice(),g=s.slice();e[l]<0?(d[u]="s["+u+"]-2",g[u]="0"):(d[u]="s["+u+"]-1",g[u]="1"),0===i?n.push("if(s[",u,"]>2){dst.set(",s.join(),",",u,",0.5*(src.get(",d.join(),")-src.get(",g.join(),")))}else{dst.set(",s.join(),",",u,",0)};"):n.push("if(s[",u,"]>2){diff(",f,",src.pick(",d.join(),")",c,",src.pick(",g.join(),")",c,");}else{zero(",f,");};");break;default:throw new Error("ndarray-gradient: Invalid boundary condition")}}i>0&&n.push("};")}for(var s=0;s<1<>",rrshift:">>>"};!function(){for(var t in s){var e=s[t];r[t]=o({args:["array","array","array"],body:{args:["a","b","c"],body:"a=b"+e+"c"},funcName:t}),r[t+"eq"]=o({args:["array","array"],body:{args:["a","b"],body:"a"+e+"=b"},rvalue:!0,funcName:t+"eq"}),r[t+"s"]=o({args:["array","array","scalar"],body:{args:["a","b","s"],body:"a=b"+e+"s"},funcName:t+"s"}),r[t+"seq"]=o({args:["array","scalar"],body:{args:["a","s"],body:"a"+e+"=s"},rvalue:!0,funcName:t+"seq"})}}();var l={not:"!",bnot:"~",neg:"-",recip:"1.0/"};!function(){for(var t in l){var e=l[t];r[t]=o({args:["array","array"],body:{args:["a","b"],body:"a="+e+"b"},funcName:t}),r[t+"eq"]=o({args:["array"],body:{args:["a"],body:"a="+e+"a"},rvalue:!0,count:2,funcName:t+"eq"})}}();var c={and:"&&",or:"||",eq:"===",neq:"!==",lt:"<",gt:">",leq:"<=",geq:">="};!function(){for(var t in c){var e=c[t];r[t]=o({args:["array","array","array"],body:{args:["a","b","c"],body:"a=b"+e+"c"},funcName:t}),r[t+"s"]=o({args:["array","array","scalar"],body:{args:["a","b","s"],body:"a=b"+e+"s"},funcName:t+"s"}),r[t+"eq"]=o({args:["array","array"],body:{args:["a","b"],body:"a=a"+e+"b"},rvalue:!0,count:2,funcName:t+"eq"}),r[t+"seq"]=o({args:["array","scalar"],body:{args:["a","s"],body:"a=a"+e+"s"},rvalue:!0,count:2,funcName:t+"seq"})}}();var u=["abs","acos","asin","atan","ceil","cos","exp","floor","log","round","sin","sqrt","tan"];!function(){for(var t=0;tthis_s){this_s=-a}else if(a>this_s){this_s=a}",localVars:[],thisVars:["this_s"]},post:{args:[],localVars:[],thisVars:["this_s"],body:"return this_s"},funcName:"norminf"}),r.norm1=n({args:["array"],pre:{args:[],localVars:[],thisVars:["this_s"],body:"this_s=0"},body:{args:[{name:"a",lvalue:!1,rvalue:!0,count:3}],body:"this_s+=a<0?-a:a",localVars:[],thisVars:["this_s"]},post:{args:[],localVars:[],thisVars:["this_s"],body:"return this_s"},funcName:"norm1"}),r.sup=n({args:["array"],pre:{body:"this_h=-Infinity",args:[],thisVars:["this_h"],localVars:[]},body:{body:"if(_inline_1_arg0_>this_h)this_h=_inline_1_arg0_",args:[{name:"_inline_1_arg0_",lvalue:!1,rvalue:!0,count:2}],thisVars:["this_h"],localVars:[]},post:{body:"return this_h",args:[],thisVars:["this_h"],localVars:[]}}),r.inf=n({args:["array"],pre:{body:"this_h=Infinity",args:[],thisVars:["this_h"],localVars:[]},body:{body:"if(_inline_1_arg0_this_v){this_v=_inline_1_arg1_;for(var _inline_1_k=0;_inline_1_k<_inline_1_arg0_.length;++_inline_1_k){this_i[_inline_1_k]=_inline_1_arg0_[_inline_1_k]}}}",args:[{name:"_inline_1_arg0_",lvalue:!1,rvalue:!0,count:2},{name:"_inline_1_arg1_",lvalue:!1,rvalue:!0,count:2}],thisVars:["this_i","this_v"],localVars:["_inline_1_k"]},post:{body:"{return this_i}",args:[],thisVars:["this_i"],localVars:[]}}),r.random=o({args:["array"],pre:{args:[],body:"this_f=Math.random",thisVars:["this_f"]},body:{args:["a"],body:"a=this_f()",thisVars:["this_f"]},funcName:"random"}),r.assign=o({args:["array","array"],body:{args:["a","b"],body:"a=b"},funcName:"assign"}),r.assigns=o({args:["array","scalar"],body:{args:["a","b"],body:"a=b"},funcName:"assigns"}),r.equals=n({args:["array","array"],pre:i,body:{args:[{name:"x",lvalue:!1,rvalue:!0,count:1},{name:"y",lvalue:!1,rvalue:!0,count:1}],body:"if(x!==y){return false}",localVars:[],thisVars:[]},post:{args:[],localVars:[],thisVars:[],body:"return true"},funcName:"equals"})},{"cwise-compiler":117}],388:[function(t,e,r){"use strict";var n=t("ndarray"),i=t("./doConvert.js");e.exports=function(t,e){for(var r=[],a=t,o=1;Array.isArray(a);)r.push(a.length),o*=a.length,a=a[0];return 0===r.length?n():(e||(e=n(new Float64Array(o),r)),i(e,t),e)}},{"./doConvert.js":389,ndarray:393}],389:[function(t,e,r){e.exports=t("cwise-compiler")({args:["array","scalar","index"],pre:{body:"{}",args:[],thisVars:[],localVars:[]},body:{body:"{\nvar _inline_1_v=_inline_1_arg1_,_inline_1_i\nfor(_inline_1_i=0;_inline_1_i<_inline_1_arg2_.length-1;++_inline_1_i) {\n_inline_1_v=_inline_1_v[_inline_1_arg2_[_inline_1_i]]\n}\n_inline_1_arg0_=_inline_1_v[_inline_1_arg2_[_inline_1_arg2_.length-1]]\n}",args:[{name:"_inline_1_arg0_",lvalue:!0,rvalue:!1,count:1},{name:"_inline_1_arg1_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_1_arg2_",lvalue:!1,rvalue:!0,count:4}],thisVars:[],localVars:["_inline_1_i","_inline_1_v"]},post:{body:"{}",args:[],thisVars:[],localVars:[]},funcName:"convert",blockSize:64})},{"cwise-compiler":117}],390:[function(t,e,r){"use strict";var n=t("typedarray-pool"),i=32;function a(t){switch(t){case"uint8":return[n.mallocUint8,n.freeUint8];case"uint16":return[n.mallocUint16,n.freeUint16];case"uint32":return[n.mallocUint32,n.freeUint32];case"int8":return[n.mallocInt8,n.freeInt8];case"int16":return[n.mallocInt16,n.freeInt16];case"int32":return[n.mallocInt32,n.freeInt32];case"float32":return[n.mallocFloat,n.freeFloat];case"float64":return[n.mallocDouble,n.freeDouble];default:return null}}function o(t){for(var e=[],r=0;r0?s.push(["d",d,"=s",d,"-d",f,"*n",f].join("")):s.push(["d",d,"=s",d].join("")),f=d),0!=(p=t.length-1-l)&&(h>0?s.push(["e",p,"=s",p,"-e",h,"*n",h,",f",p,"=",c[p],"-f",h,"*n",h].join("")):s.push(["e",p,"=s",p,",f",p,"=",c[p]].join("")),h=p)}r.push("var "+s.join(","));var g=["0","n0-1","data","offset"].concat(o(t.length));r.push(["if(n0<=",i,"){","insertionSort(",g.join(","),")}else{","quickSort(",g.join(","),")}"].join("")),r.push("}return "+n);var m=new Function("insertionSort","quickSort",r.join("\n")),v=function(t,e){var r=["'use strict'"],n=["ndarrayInsertionSort",t.join("d"),e].join(""),i=["left","right","data","offset"].concat(o(t.length)),s=a(e),l=["i,j,cptr,ptr=left*s0+offset"];if(t.length>1){for(var c=[],u=1;u1){for(r.push("dptr=0;sptr=ptr"),u=t.length-1;u>=0;--u)0!==(p=t[u])&&r.push(["for(i",p,"=0;i",p,"b){break __l}"].join("")),u=t.length-1;u>=1;--u)r.push("sptr+=e"+u,"dptr+=f"+u,"}");for(r.push("dptr=cptr;sptr=cptr-s0"),u=t.length-1;u>=0;--u)0!==(p=t[u])&&r.push(["for(i",p,"=0;i",p,"=0;--u)0!==(p=t[u])&&r.push(["for(i",p,"=0;i",p,"scratch)){",h("cptr",f("cptr-s0")),"cptr-=s0","}",h("cptr","scratch"));return r.push("}"),t.length>1&&s&&r.push("free(scratch)"),r.push("} return "+n),s?new Function("malloc","free",r.join("\n"))(s[0],s[1]):new Function(r.join("\n"))()}(t,e),y=function(t,e,r){var n=["'use strict'"],s=["ndarrayQuickSort",t.join("d"),e].join(""),l=["left","right","data","offset"].concat(o(t.length)),c=a(e),u=0;n.push(["function ",s,"(",l.join(","),"){"].join(""));var f=["sixth=((right-left+1)/6)|0","index1=left+sixth","index5=right-sixth","index3=(left+right)>>1","index2=index3-sixth","index4=index3+sixth","el1=index1","el2=index2","el3=index3","el4=index4","el5=index5","less=left+1","great=right-1","pivots_are_equal=true","tmp","tmp0","x","y","z","k","ptr0","ptr1","ptr2","comp_pivot1=0","comp_pivot2=0","comp=0"];if(t.length>1){for(var h=[],p=1;p=0;--a)0!==(o=t[a])&&n.push(["for(i",o,"=0;i",o,"1)for(a=0;a1?n.push("ptr_shift+=d"+o):n.push("ptr0+=d"+o),n.push("}"))}}function y(e,r,i,a){if(1===r.length)n.push("ptr0="+d(r[0]));else{for(var o=0;o1)for(o=0;o=1;--o)i&&n.push("pivot_ptr+=f"+o),r.length>1?n.push("ptr_shift+=e"+o):n.push("ptr0+=e"+o),n.push("}")}function x(){t.length>1&&c&&n.push("free(pivot1)","free(pivot2)")}function b(e,r){var i="el"+e,a="el"+r;if(t.length>1){var o="__l"+ ++u;y(o,[i,a],!1,["comp=",g("ptr0"),"-",g("ptr1"),"\n","if(comp>0){tmp0=",i,";",i,"=",a,";",a,"=tmp0;break ",o,"}\n","if(comp<0){break ",o,"}"].join(""))}else n.push(["if(",g(d(i)),">",g(d(a)),"){tmp0=",i,";",i,"=",a,";",a,"=tmp0}"].join(""))}function _(e,r){t.length>1?v([e,r],!1,m("ptr0",g("ptr1"))):n.push(m(d(e),g(d(r))))}function w(e,r,i){if(t.length>1){var a="__l"+ ++u;y(a,[r],!0,[e,"=",g("ptr0"),"-pivot",i,"[pivot_ptr]\n","if(",e,"!==0){break ",a,"}"].join(""))}else n.push([e,"=",g(d(r)),"-pivot",i].join(""))}function k(e,r){t.length>1?v([e,r],!1,["tmp=",g("ptr0"),"\n",m("ptr0",g("ptr1")),"\n",m("ptr1","tmp")].join("")):n.push(["ptr0=",d(e),"\n","ptr1=",d(r),"\n","tmp=",g("ptr0"),"\n",m("ptr0",g("ptr1")),"\n",m("ptr1","tmp")].join(""))}function M(e,r,i){t.length>1?(v([e,r,i],!1,["tmp=",g("ptr0"),"\n",m("ptr0",g("ptr1")),"\n",m("ptr1",g("ptr2")),"\n",m("ptr2","tmp")].join("")),n.push("++"+r,"--"+i)):n.push(["ptr0=",d(e),"\n","ptr1=",d(r),"\n","ptr2=",d(i),"\n","++",r,"\n","--",i,"\n","tmp=",g("ptr0"),"\n",m("ptr0",g("ptr1")),"\n",m("ptr1",g("ptr2")),"\n",m("ptr2","tmp")].join(""))}function A(t,e){k(t,e),n.push("--"+e)}function T(e,r,i){t.length>1?v([e,r],!0,[m("ptr0",g("ptr1")),"\n",m("ptr1",["pivot",i,"[pivot_ptr]"].join(""))].join("")):n.push(m(d(e),g(d(r))),m(d(r),"pivot"+i))}function S(e,r){n.push(["if((",r,"-",e,")<=",i,"){\n","insertionSort(",e,",",r,",data,offset,",o(t.length).join(","),")\n","}else{\n",s,"(",e,",",r,",data,offset,",o(t.length).join(","),")\n","}"].join(""))}function C(e,r,i){t.length>1?(n.push(["__l",++u,":while(true){"].join("")),v([e],!0,["if(",g("ptr0"),"!==pivot",r,"[pivot_ptr]){break __l",u,"}"].join("")),n.push(i,"}")):n.push(["while(",g(d(e)),"===pivot",r,"){",i,"}"].join(""))}return n.push("var "+f.join(",")),b(1,2),b(4,5),b(1,3),b(2,3),b(1,4),b(3,4),b(2,5),b(2,3),b(4,5),t.length>1?v(["el1","el2","el3","el4","el5","index1","index3","index5"],!0,["pivot1[pivot_ptr]=",g("ptr1"),"\n","pivot2[pivot_ptr]=",g("ptr3"),"\n","pivots_are_equal=pivots_are_equal&&(pivot1[pivot_ptr]===pivot2[pivot_ptr])\n","x=",g("ptr0"),"\n","y=",g("ptr2"),"\n","z=",g("ptr4"),"\n",m("ptr5","x"),"\n",m("ptr6","y"),"\n",m("ptr7","z")].join("")):n.push(["pivot1=",g(d("el2")),"\n","pivot2=",g(d("el4")),"\n","pivots_are_equal=pivot1===pivot2\n","x=",g(d("el1")),"\n","y=",g(d("el3")),"\n","z=",g(d("el5")),"\n",m(d("index1"),"x"),"\n",m(d("index3"),"y"),"\n",m(d("index5"),"z")].join("")),_("index2","left"),_("index4","right"),n.push("if(pivots_are_equal){"),n.push("for(k=less;k<=great;++k){"),w("comp","k",1),n.push("if(comp===0){continue}"),n.push("if(comp<0){"),n.push("if(k!==less){"),k("k","less"),n.push("}"),n.push("++less"),n.push("}else{"),n.push("while(true){"),w("comp","great",1),n.push("if(comp>0){"),n.push("great--"),n.push("}else if(comp<0){"),M("k","less","great"),n.push("break"),n.push("}else{"),A("k","great"),n.push("break"),n.push("}"),n.push("}"),n.push("}"),n.push("}"),n.push("}else{"),n.push("for(k=less;k<=great;++k){"),w("comp_pivot1","k",1),n.push("if(comp_pivot1<0){"),n.push("if(k!==less){"),k("k","less"),n.push("}"),n.push("++less"),n.push("}else{"),w("comp_pivot2","k",2),n.push("if(comp_pivot2>0){"),n.push("while(true){"),w("comp","great",2),n.push("if(comp>0){"),n.push("if(--greatindex5){"),C("less",1,"++less"),C("great",2,"--great"),n.push("for(k=less;k<=great;++k){"),w("comp_pivot1","k",1),n.push("if(comp_pivot1===0){"),n.push("if(k!==less){"),k("k","less"),n.push("}"),n.push("++less"),n.push("}else{"),w("comp_pivot2","k",2),n.push("if(comp_pivot2===0){"),n.push("while(true){"),w("comp","great",2),n.push("if(comp===0){"),n.push("if(--great1&&c?new Function("insertionSort","malloc","free",n.join("\n"))(r,c[0],c[1]):new Function("insertionSort",n.join("\n"))(r)}(t,e,v);return m(v,y)}},{"typedarray-pool":481}],391:[function(t,e,r){"use strict";var n=t("./lib/compile_sort.js"),i={};e.exports=function(t){var e=t.order,r=t.dtype,a=[e,r].join(":"),o=i[a];return o||(i[a]=o=n(e,r)),o(t),t}},{"./lib/compile_sort.js":390}],392:[function(t,e,r){"use strict";var n=t("ndarray-linear-interpolate"),i=t("cwise/lib/wrapper")({args:["index","array","scalar","scalar","scalar"],pre:{body:"{this_warped=new Array(_inline_3_arg4_)}",args:[{name:"_inline_3_arg0_",lvalue:!1,rvalue:!1,count:0},{name:"_inline_3_arg1_",lvalue:!1,rvalue:!1,count:0},{name:"_inline_3_arg2_",lvalue:!1,rvalue:!1,count:0},{name:"_inline_3_arg3_",lvalue:!1,rvalue:!1,count:0},{name:"_inline_3_arg4_",lvalue:!1,rvalue:!0,count:1}],thisVars:["this_warped"],localVars:[]},body:{body:"{_inline_4_arg2_(this_warped,_inline_4_arg0_),_inline_4_arg1_=_inline_4_arg3_.apply(void 0,this_warped)}",args:[{name:"_inline_4_arg0_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_4_arg1_",lvalue:!0,rvalue:!1,count:1},{name:"_inline_4_arg2_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_4_arg3_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_4_arg4_",lvalue:!1,rvalue:!1,count:0}],thisVars:["this_warped"],localVars:[]},post:{body:"{}",args:[],thisVars:[],localVars:[]},debug:!1,funcName:"warpND",blockSize:64}),a=t("cwise/lib/wrapper")({args:["index","array","scalar","scalar","scalar"],pre:{body:"{this_warped=[0]}",args:[],thisVars:["this_warped"],localVars:[]},body:{body:"{_inline_7_arg2_(this_warped,_inline_7_arg0_),_inline_7_arg1_=_inline_7_arg3_(_inline_7_arg4_,this_warped[0])}",args:[{name:"_inline_7_arg0_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_7_arg1_",lvalue:!0,rvalue:!1,count:1},{name:"_inline_7_arg2_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_7_arg3_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_7_arg4_",lvalue:!1,rvalue:!0,count:1}],thisVars:["this_warped"],localVars:[]},post:{body:"{}",args:[],thisVars:[],localVars:[]},debug:!1,funcName:"warp1D",blockSize:64}),o=t("cwise/lib/wrapper")({args:["index","array","scalar","scalar","scalar"],pre:{body:"{this_warped=[0,0]}",args:[],thisVars:["this_warped"],localVars:[]},body:{body:"{_inline_10_arg2_(this_warped,_inline_10_arg0_),_inline_10_arg1_=_inline_10_arg3_(_inline_10_arg4_,this_warped[0],this_warped[1])}",args:[{name:"_inline_10_arg0_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_10_arg1_",lvalue:!0,rvalue:!1,count:1},{name:"_inline_10_arg2_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_10_arg3_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_10_arg4_",lvalue:!1,rvalue:!0,count:1}],thisVars:["this_warped"],localVars:[]},post:{body:"{}",args:[],thisVars:[],localVars:[]},debug:!1,funcName:"warp2D",blockSize:64}),s=t("cwise/lib/wrapper")({args:["index","array","scalar","scalar","scalar"],pre:{body:"{this_warped=[0,0,0]}",args:[],thisVars:["this_warped"],localVars:[]},body:{body:"{_inline_13_arg2_(this_warped,_inline_13_arg0_),_inline_13_arg1_=_inline_13_arg3_(_inline_13_arg4_,this_warped[0],this_warped[1],this_warped[2])}",args:[{name:"_inline_13_arg0_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_13_arg1_",lvalue:!0,rvalue:!1,count:1},{name:"_inline_13_arg2_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_13_arg3_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_13_arg4_",lvalue:!1,rvalue:!0,count:1}],thisVars:["this_warped"],localVars:[]},post:{body:"{}",args:[],thisVars:[],localVars:[]},debug:!1,funcName:"warp3D",blockSize:64});e.exports=function(t,e,r){switch(e.shape.length){case 1:a(t,r,n.d1,e);break;case 2:o(t,r,n.d2,e);break;case 3:s(t,r,n.d3,e);break;default:i(t,r,n.bind(void 0,e),e.shape.length)}return t}},{"cwise/lib/wrapper":120,"ndarray-linear-interpolate":386}],393:[function(t,e,r){var n=t("iota-array"),i=t("is-buffer"),a="undefined"!=typeof Float64Array;function o(t,e){return t[0]-e[0]}function s(){var t,e=this.stride,r=new Array(e.length);for(t=0;tMath.abs(this.stride[1]))?[1,0]:[0,1]}})"):3===e&&a.push("var s0=Math.abs(this.stride[0]),s1=Math.abs(this.stride[1]),s2=Math.abs(this.stride[2]);if(s0>s1){if(s1>s2){return [2,1,0];}else if(s0>s2){return [1,2,0];}else{return [1,0,2];}}else if(s0>s2){return [2,0,1];}else if(s2>s1){return [0,1,2];}else{return [0,2,1];}}})")):a.push("ORDER})")),a.push("proto.set=function "+r+"_set("+l.join(",")+",v){"),i?a.push("return this.data.set("+u+",v)}"):a.push("return this.data["+u+"]=v}"),a.push("proto.get=function "+r+"_get("+l.join(",")+"){"),i?a.push("return this.data.get("+u+")}"):a.push("return this.data["+u+"]}"),a.push("proto.index=function "+r+"_index(",l.join(),"){return "+u+"}"),a.push("proto.hi=function "+r+"_hi("+l.join(",")+"){return new "+r+"(this.data,"+o.map(function(t){return["(typeof i",t,"!=='number'||i",t,"<0)?this.shape[",t,"]:i",t,"|0"].join("")}).join(",")+","+o.map(function(t){return"this.stride["+t+"]"}).join(",")+",this.offset)}");var p=o.map(function(t){return"a"+t+"=this.shape["+t+"]"}),d=o.map(function(t){return"c"+t+"=this.stride["+t+"]"});a.push("proto.lo=function "+r+"_lo("+l.join(",")+"){var b=this.offset,d=0,"+p.join(",")+","+d.join(","));for(var g=0;g=0){d=i"+g+"|0;b+=c"+g+"*d;a"+g+"-=d}");a.push("return new "+r+"(this.data,"+o.map(function(t){return"a"+t}).join(",")+","+o.map(function(t){return"c"+t}).join(",")+",b)}"),a.push("proto.step=function "+r+"_step("+l.join(",")+"){var "+o.map(function(t){return"a"+t+"=this.shape["+t+"]"}).join(",")+","+o.map(function(t){return"b"+t+"=this.stride["+t+"]"}).join(",")+",c=this.offset,d=0,ceil=Math.ceil");for(g=0;g=0){c=(c+this.stride["+g+"]*i"+g+")|0}else{a.push(this.shape["+g+"]);b.push(this.stride["+g+"])}");return a.push("var ctor=CTOR_LIST[a.length+1];return ctor(this.data,a,b,c)}"),a.push("return function construct_"+r+"(data,shape,stride,offset){return new "+r+"(data,"+o.map(function(t){return"shape["+t+"]"}).join(",")+","+o.map(function(t){return"stride["+t+"]"}).join(",")+",offset)}"),new Function("CTOR_LIST","ORDER",a.join("\n"))(c[t],s)}var c={float32:[],float64:[],int8:[],int16:[],int32:[],uint8:[],uint16:[],uint32:[],array:[],uint8_clamped:[],buffer:[],generic:[]};e.exports=function(t,e,r,n){if(void 0===t)return(0,c.array[0])([]);"number"==typeof t&&(t=[t]),void 0===e&&(e=[t.length]);var o=e.length;if(void 0===r){r=new Array(o);for(var s=o-1,u=1;s>=0;--s)r[s]=u,u*=e[s]}if(void 0===n)for(n=0,s=0;s>>0;e.exports=function(t,e){if(isNaN(t)||isNaN(e))return NaN;if(t===e)return t;if(0===t)return e<0?-i:i;var r=n.hi(t),o=n.lo(t);e>t==t>0?o===a?(r+=1,o=0):o+=1:0===o?(o=a,r-=1):o-=1;return n.pack(o,r)}},{"double-bits":134}],395:[function(t,e,r){var n=Math.PI,i=c(120);function a(t,e,r,n){return["C",t,e,r,n,r,n]}function o(t,e,r,n,i,a){return["C",t/3+2/3*r,e/3+2/3*n,i/3+2/3*r,a/3+2/3*n,i,a]}function s(t,e,r,a,o,c,u,f,h,p){if(p)k=p[0],M=p[1],_=p[2],w=p[3];else{var d=l(t,e,-o);t=d.x,e=d.y;var g=(t-(f=(d=l(f,h,-o)).x))/2,m=(e-(h=d.y))/2,v=g*g/(r*r)+m*m/(a*a);v>1&&(r*=v=Math.sqrt(v),a*=v);var y=r*r,x=a*a,b=(c==u?-1:1)*Math.sqrt(Math.abs((y*x-y*m*m-x*g*g)/(y*m*m+x*g*g)));b==1/0&&(b=1);var _=b*r*m/a+(t+f)/2,w=b*-a*g/r+(e+h)/2,k=Math.asin(((e-w)/a).toFixed(9)),M=Math.asin(((h-w)/a).toFixed(9));(k=t<_?n-k:k)<0&&(k=2*n+k),(M=f<_?n-M:M)<0&&(M=2*n+M),u&&k>M&&(k-=2*n),!u&&M>k&&(M-=2*n)}if(Math.abs(M-k)>i){var A=M,T=f,S=h;M=k+i*(u&&M>k?1:-1);var C=s(f=_+r*Math.cos(M),h=w+a*Math.sin(M),r,a,o,0,u,T,S,[M,A,_,w])}var E=Math.tan((M-k)/4),L=4/3*r*E,z=4/3*a*E,P=[2*t-(t+L*Math.sin(k)),2*e-(e-z*Math.cos(k)),f+L*Math.sin(M),h-z*Math.cos(M),f,h];if(p)return P;C&&(P=P.concat(C));for(var D=0;D7&&(r.push(v.splice(0,7)),v.unshift("C"));break;case"S":var x=p,b=d;"C"!=e&&"S"!=e||(x+=x-n,b+=b-i),v=["C",x,b,v[1],v[2],v[3],v[4]];break;case"T":"Q"==e||"T"==e?(f=2*p-f,h=2*d-h):(f=p,h=d),v=o(p,d,f,h,v[1],v[2]);break;case"Q":f=v[1],h=v[2],v=o(p,d,v[1],v[2],v[3],v[4]);break;case"L":v=a(p,d,v[1],v[2]);break;case"H":v=a(p,d,v[1],d);break;case"V":v=a(p,d,p,v[1]);break;case"Z":v=a(p,d,l,u)}e=y,p=v[v.length-2],d=v[v.length-1],v.length>4?(n=v[v.length-4],i=v[v.length-3]):(n=p,i=d),r.push(v)}return r}},{}],396:[function(t,e,r){r.vertexNormals=function(t,e,r){for(var n=e.length,i=new Array(n),a=void 0===r?1e-6:r,o=0;oa){var b=i[c],_=1/Math.sqrt(m*y);for(x=0;x<3;++x){var w=(x+1)%3,k=(x+2)%3;b[x]+=_*(v[w]*g[k]-v[k]*g[w])}}}for(o=0;oa)for(_=1/Math.sqrt(M),x=0;x<3;++x)b[x]*=_;else for(x=0;x<3;++x)b[x]=0}return i},r.faceNormals=function(t,e,r){for(var n=t.length,i=new Array(n),a=void 0===r?1e-6:r,o=0;oa?1/Math.sqrt(p):0;for(c=0;c<3;++c)h[c]*=p;i[o]=h}return i}},{}],397:[function(t,e,r){"use strict";var n=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var t=new String("abc");if(t[5]="de","5"===Object.getOwnPropertyNames(t)[0])return!1;for(var e={},r=0;r<10;r++)e["_"+String.fromCharCode(r)]=r;if("0123456789"!==Object.getOwnPropertyNames(e).map(function(t){return e[t]}).join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach(function(t){n[t]=t}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},n)).join("")}catch(t){return!1}}()?Object.assign:function(t,e){for(var r,o,s=function(t){if(null==t)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(t)}(t),l=1;l0){var f=Math.sqrt(u+1);t[0]=.5*(o-l)/f,t[1]=.5*(s-n)/f,t[2]=.5*(r-a)/f,t[3]=.5*f}else{var h=Math.max(e,a,c),f=Math.sqrt(2*h-u+1);e>=h?(t[0]=.5*f,t[1]=.5*(i+r)/f,t[2]=.5*(s+n)/f,t[3]=.5*(o-l)/f):a>=h?(t[0]=.5*(r+i)/f,t[1]=.5*f,t[2]=.5*(l+o)/f,t[3]=.5*(s-n)/f):(t[0]=.5*(n+s)/f,t[1]=.5*(o+l)/f,t[2]=.5*f,t[3]=.5*(r-i)/f)}return t}},{}],399:[function(t,e,r){"use strict";e.exports=function(t){var e=(t=t||{}).center||[0,0,0],r=t.rotation||[0,0,0,1],n=t.radius||1;e=[].slice.call(e,0,3),u(r=[].slice.call(r,0,4),r);var i=new f(r,e,Math.log(n));i.setDistanceLimits(t.zoomMin,t.zoomMax),("eye"in t||"up"in t)&&i.lookAt(0,t.eye,t.center,t.up);return i};var n=t("filtered-vector"),i=t("gl-mat4/lookAt"),a=t("gl-mat4/fromQuat"),o=t("gl-mat4/invert"),s=t("./lib/quatFromFrame");function l(t,e,r){return Math.sqrt(Math.pow(t,2)+Math.pow(e,2)+Math.pow(r,2))}function c(t,e,r,n){return Math.sqrt(Math.pow(t,2)+Math.pow(e,2)+Math.pow(r,2)+Math.pow(n,2))}function u(t,e){var r=e[0],n=e[1],i=e[2],a=e[3],o=c(r,n,i,a);o>1e-6?(t[0]=r/o,t[1]=n/o,t[2]=i/o,t[3]=a/o):(t[0]=t[1]=t[2]=0,t[3]=1)}function f(t,e,r){this.radius=n([r]),this.center=n(e),this.rotation=n(t),this.computedRadius=this.radius.curve(0),this.computedCenter=this.center.curve(0),this.computedRotation=this.rotation.curve(0),this.computedUp=[.1,0,0],this.computedEye=[.1,0,0],this.computedMatrix=[.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],this.recalcMatrix(0)}var h=f.prototype;h.lastT=function(){return Math.max(this.radius.lastT(),this.center.lastT(),this.rotation.lastT())},h.recalcMatrix=function(t){this.radius.curve(t),this.center.curve(t),this.rotation.curve(t);var e=this.computedRotation;u(e,e);var r=this.computedMatrix;a(r,e);var n=this.computedCenter,i=this.computedEye,o=this.computedUp,s=Math.exp(this.computedRadius[0]);i[0]=n[0]+s*r[2],i[1]=n[1]+s*r[6],i[2]=n[2]+s*r[10],o[0]=r[1],o[1]=r[5],o[2]=r[9];for(var l=0;l<3;++l){for(var c=0,f=0;f<3;++f)c+=r[l+4*f]*i[f];r[12+l]=-c}},h.getMatrix=function(t,e){this.recalcMatrix(t);var r=this.computedMatrix;if(e){for(var n=0;n<16;++n)e[n]=r[n];return e}return r},h.idle=function(t){this.center.idle(t),this.radius.idle(t),this.rotation.idle(t)},h.flush=function(t){this.center.flush(t),this.radius.flush(t),this.rotation.flush(t)},h.pan=function(t,e,r,n){e=e||0,r=r||0,n=n||0,this.recalcMatrix(t);var i=this.computedMatrix,a=i[1],o=i[5],s=i[9],c=l(a,o,s);a/=c,o/=c,s/=c;var u=i[0],f=i[4],h=i[8],p=u*a+f*o+h*s,d=l(u-=a*p,f-=o*p,h-=s*p);u/=d,f/=d,h/=d;var g=i[2],m=i[6],v=i[10],y=g*a+m*o+v*s,x=g*u+m*f+v*h,b=l(g-=y*a+x*u,m-=y*o+x*f,v-=y*s+x*h);g/=b,m/=b,v/=b;var _=u*e+a*r,w=f*e+o*r,k=h*e+s*r;this.center.move(t,_,w,k);var M=Math.exp(this.computedRadius[0]);M=Math.max(1e-4,M+n),this.radius.set(t,Math.log(M))},h.rotate=function(t,e,r,n){this.recalcMatrix(t),e=e||0,r=r||0;var i=this.computedMatrix,a=i[0],o=i[4],s=i[8],u=i[1],f=i[5],h=i[9],p=i[2],d=i[6],g=i[10],m=e*a+r*u,v=e*o+r*f,y=e*s+r*h,x=-(d*y-g*v),b=-(g*m-p*y),_=-(p*v-d*m),w=Math.sqrt(Math.max(0,1-Math.pow(x,2)-Math.pow(b,2)-Math.pow(_,2))),k=c(x,b,_,w);k>1e-6?(x/=k,b/=k,_/=k,w/=k):(x=b=_=0,w=1);var M=this.computedRotation,A=M[0],T=M[1],S=M[2],C=M[3],E=A*w+C*x+T*_-S*b,L=T*w+C*b+S*x-A*_,z=S*w+C*_+A*b-T*x,P=C*w-A*x-T*b-S*_;if(n){x=p,b=d,_=g;var D=Math.sin(n)/l(x,b,_);x*=D,b*=D,_*=D,P=P*(w=Math.cos(e))-(E=E*w+P*x+L*_-z*b)*x-(L=L*w+P*b+z*x-E*_)*b-(z=z*w+P*_+E*b-L*x)*_}var O=c(E,L,z,P);O>1e-6?(E/=O,L/=O,z/=O,P/=O):(E=L=z=0,P=1),this.rotation.set(t,E,L,z,P)},h.lookAt=function(t,e,r,n){this.recalcMatrix(t),r=r||this.computedCenter,e=e||this.computedEye,n=n||this.computedUp;var a=this.computedMatrix;i(a,e,r,n);var o=this.computedRotation;s(o,a[0],a[1],a[2],a[4],a[5],a[6],a[8],a[9],a[10]),u(o,o),this.rotation.set(t,o[0],o[1],o[2],o[3]);for(var l=0,c=0;c<3;++c)l+=Math.pow(r[c]-e[c],2);this.radius.set(t,.5*Math.log(Math.max(l,1e-6))),this.center.set(t,r[0],r[1],r[2])},h.translate=function(t,e,r,n){this.center.move(t,e||0,r||0,n||0)},h.setMatrix=function(t,e){var r=this.computedRotation;s(r,e[0],e[1],e[2],e[4],e[5],e[6],e[8],e[9],e[10]),u(r,r),this.rotation.set(t,r[0],r[1],r[2],r[3]);var n=this.computedMatrix;o(n,e);var i=n[15];if(Math.abs(i)>1e-6){var a=n[12]/i,l=n[13]/i,c=n[14]/i;this.recalcMatrix(t);var f=Math.exp(this.computedRadius[0]);this.center.set(t,a-n[2]*f,l-n[6]*f,c-n[10]*f),this.radius.idle(t)}else this.center.idle(t),this.radius.idle(t)},h.setDistance=function(t,e){e>0&&this.radius.set(t,Math.log(e))},h.setDistanceLimits=function(t,e){t=t>0?Math.log(t):-1/0,e=e>0?Math.log(e):1/0,e=Math.max(e,t),this.radius.bounds[0][0]=t,this.radius.bounds[1][0]=e},h.getDistanceLimits=function(t){var e=this.radius.bounds;return t?(t[0]=Math.exp(e[0][0]),t[1]=Math.exp(e[1][0]),t):[Math.exp(e[0][0]),Math.exp(e[1][0])]},h.toJSON=function(){return this.recalcMatrix(this.lastT()),{center:this.computedCenter.slice(),rotation:this.computedRotation.slice(),distance:Math.log(this.computedRadius[0]),zoomMin:this.radius.bounds[0][0],zoomMax:this.radius.bounds[1][0]}},h.fromJSON=function(t){var e=this.lastT(),r=t.center;r&&this.center.set(e,r[0],r[1],r[2]);var n=t.rotation;n&&this.rotation.set(e,n[0],n[1],n[2],n[3]);var i=t.distance;i&&i>0&&this.radius.set(e,Math.log(i)),this.setDistanceLimits(t.zoomMin,t.zoomMax)}},{"./lib/quatFromFrame":398,"filtered-vector":198,"gl-mat4/fromQuat":232,"gl-mat4/invert":235,"gl-mat4/lookAt":236}],400:[function(t,e,r){"use strict";var n=t("repeat-string");e.exports=function(t,e,r){return n(r=void 0!==r?r+"":" ",e)+t}},{"repeat-string":439}],401:[function(t,e,r){"use strict";var n=t("pick-by-alias");e.exports=function(t){var e;arguments.length>1&&(t=arguments);"string"==typeof t?t=t.split(/\s/).map(parseFloat):"number"==typeof t&&(t=[t]);t.length&&"number"==typeof t[0]?e=1===t.length?{width:t[0],height:t[0],x:0,y:0}:2===t.length?{width:t[0],height:t[1],x:0,y:0}:{x:t[0],y:t[1],width:t[2]-t[0]||0,height:t[3]-t[1]||0}:t&&(t=n(t,{left:"x l left Left",top:"y t top Top",width:"w width W Width",height:"h height W Width",bottom:"b bottom Bottom",right:"r right Right"}),e={x:t.left||0,y:t.top||0},null==t.width?t.right?e.width=t.right-e.x:e.width=0:e.width=t.width,null==t.height?t.bottom?e.height=t.bottom-e.y:e.height=0:e.height=t.height);return e}},{"pick-by-alias":407}],402:[function(t,e,r){e.exports=function(t){var e=[];return t.replace(i,function(t,r,i){var o=r.toLowerCase();for(i=function(t){var e=t.match(a);return e?e.map(Number):[]}(i),"m"==o&&i.length>2&&(e.push([r].concat(i.splice(0,2))),o="l",r="m"==r?"l":"L");;){if(i.length==n[o])return i.unshift(r),e.push(i);if(i.length0;--o)a=l[o],r=s[o],s[o]=s[a],s[a]=r,l[o]=l[r],l[r]=a,c=(c+r)*o;return n.freeUint32(l),n.freeUint32(s),c},r.unrank=function(t,e,r){switch(t){case 0:return r||[];case 1:return r?(r[0]=0,r):[0];case 2:return r?(e?(r[0]=0,r[1]=1):(r[0]=1,r[1]=0),r):e?[0,1]:[1,0]}var n,i,a,o=1;for((r=r||new Array(t))[0]=0,a=1;a0;--a)e=e-(n=e/o|0)*o|0,o=o/a|0,i=0|r[a],r[a]=0|r[n],r[n]=0|i;return r}},{"invert-permutation":359,"typedarray-pool":481}],407:[function(t,e,r){"use strict";e.exports=function(t,e,r){var n,a,o={};if("string"==typeof e&&(e=i(e)),Array.isArray(e)){var s={};for(a=0;a0){o=a[u][r][0],l=u;break}s=o[1^l];for(var f=0;f<2;++f)for(var h=a[f][r],p=0;p0&&(o=d,s=g,l=f)}return i?s:(o&&c(o,l),s)}function f(t,r){var i=a[r][t][0],o=[t];c(i,r);for(var s=i[1^r];;){for(;s!==t;)o.push(s),s=u(o[o.length-2],s,!1);if(a[0][t].length+a[1][t].length===0)break;var l=o[o.length-1],f=t,h=o[1],p=u(l,f,!0);if(n(e[l],e[f],e[h],e[p])<0)break;o.push(t),s=u(l,f)}return o}function h(t,e){return e[1]===e[e.length-1]}for(var o=0;o0;){a[0][o].length;var g=f(o,p);h(d,g)?d.push.apply(d,g):(d.length>0&&l.push(d),d=g)}d.length>0&&l.push(d)}return l};var n=t("compare-angle")},{"compare-angle":108}],409:[function(t,e,r){"use strict";e.exports=function(t,e){for(var r=n(t,e.length),i=new Array(e.length),a=new Array(e.length),o=[],s=0;s0;){var c=o.pop();i[c]=!1;for(var u=r[c],s=0;s0})).length,m=new Array(g),v=new Array(g),p=0;p0;){var N=B.pop(),j=E[N];l(j,function(t,e){return t-e});var V,U=j.length,q=F[N];if(0===q){var k=d[N];V=[k]}for(var p=0;p=0)&&(F[H]=1^q,B.push(H),0===q)){var k=d[H];R(k)||(k.reverse(),V.push(k))}}0===q&&r.push(V)}return r};var n=t("edges-to-adjacency-list"),i=t("planar-dual"),a=t("point-in-big-polygon"),o=t("two-product"),s=t("robust-sum"),l=t("uniq"),c=t("./lib/trim-leaves");function u(t,e){for(var r=new Array(t),n=0;n>>1;e.dtype||(e.dtype="array"),"string"==typeof e.dtype?d=new(f(e.dtype))(m):e.dtype&&(d=e.dtype,Array.isArray(d)&&(d.length=m));for(var v=0;vr){for(var h=0;hl||A>c||T=E||o===s)){var u=y[a];void 0===s&&(s=u.length);for(var f=o;f=g&&p<=v&&d>=m&&d<=w&&z.push(h)}var b=x[a],_=b[4*o+0],k=b[4*o+1],C=b[4*o+2],L=b[4*o+3],P=function(t,e){for(var r=null,n=0;null===r;)if(r=t[4*e+n],++n>t.length)return null;return r}(b,o+1),D=.5*i,O=a+1;e(r,n,D,O,_,k||C||L||P),e(r,n+D,D,O,k,C||L||P),e(r+D,n,D,O,C,L||P),e(r+D,n+D,D,O,L,P)}}}(0,0,1,0,0,1),z},d;function C(t,e,r){for(var n=1,i=.5,a=.5,o=.5,s=0;s0&&e[i]===r[0]))return 1;a=t[i-1]}for(var s=1;a;){var l=a.key,c=n(r,l[0],l[1]);if(l[0][0]0))return 0;s=-1,a=a.right}else if(c>0)a=a.left;else{if(!(c<0))return 0;s=1,a=a.right}}return s}}(v.slabs,v.coordinates);return 0===a.length?y:function(t,e){return function(r){return t(r[0],r[1])?0:e(r)}}(l(a),y)};var n=t("robust-orientation")[3],i=t("slab-decomposition"),a=t("interval-tree-1d"),o=t("binary-search-bounds");function s(){return!0}function l(t){for(var e={},r=0;r=-t},pointBetween:function(e,r,n){var i=e[1]-r[1],a=n[0]-r[0],o=e[0]-r[0],s=n[1]-r[1],l=o*a+i*s;return!(l-t)},pointsSameX:function(e,r){return Math.abs(e[0]-r[0])t!=o-i>t&&(a-c)*(i-u)/(o-u)+c-n>t&&(s=!s),a=c,o=u}return s}};return e}},{}],418:[function(t,e,r){var n={toPolygon:function(t,e){function r(e){if(e.length<=0)return t.segments({inverted:!1,regions:[]});function r(e){var r=e.slice(0,e.length-1);return t.segments({inverted:!1,regions:[r]})}for(var n=r(e[0]),i=1;i0})}function u(t,n){var i=t.seg,a=n.seg,o=i.start,s=i.end,c=a.start,u=a.end;r&&r.checkIntersection(i,a);var f=e.linesIntersect(o,s,c,u);if(!1===f){if(!e.pointsCollinear(o,s,c))return!1;if(e.pointsSame(o,u)||e.pointsSame(s,c))return!1;var h=e.pointsSame(o,c),p=e.pointsSame(s,u);if(h&&p)return n;var d=!h&&e.pointBetween(o,c,u),g=!p&&e.pointBetween(s,c,u);if(h)return g?l(n,s):l(t,u),n;d&&(p||(g?l(n,s):l(t,u)),l(n,o))}else 0===f.alongA&&(-1===f.alongB?l(t,c):0===f.alongB?l(t,f.pt):1===f.alongB&&l(t,u)),0===f.alongB&&(-1===f.alongA?l(n,o):0===f.alongA?l(n,f.pt):1===f.alongA&&l(n,s));return!1}for(var f=[];!a.isEmpty();){var h=a.getHead();if(r&&r.vert(h.pt[0]),h.isStart){r&&r.segmentNew(h.seg,h.primary);var p=c(h),d=p.before?p.before.ev:null,g=p.after?p.after.ev:null;function m(){if(d){var t=u(h,d);if(t)return t}return!!g&&u(h,g)}r&&r.tempStatus(h.seg,!!d&&d.seg,!!g&&g.seg);var v,y,x=m();if(x)t?(y=null===h.seg.myFill.below||h.seg.myFill.above!==h.seg.myFill.below)&&(x.seg.myFill.above=!x.seg.myFill.above):x.seg.otherFill=h.seg.myFill,r&&r.segmentUpdate(x.seg),h.other.remove(),h.remove();if(a.getHead()!==h){r&&r.rewind(h.seg);continue}t?(y=null===h.seg.myFill.below||h.seg.myFill.above!==h.seg.myFill.below,h.seg.myFill.below=g?g.seg.myFill.above:i,h.seg.myFill.above=y?!h.seg.myFill.below:h.seg.myFill.below):null===h.seg.otherFill&&(v=g?h.primary===g.primary?g.seg.otherFill.above:g.seg.myFill.above:h.primary?o:i,h.seg.otherFill={above:v,below:v}),r&&r.status(h.seg,!!d&&d.seg,!!g&&g.seg),h.other.status=p.insert(n.node({ev:h}))}else{var b=h.status;if(null===b)throw new Error("PolyBool: Zero-length segment detected; your epsilon is probably too small or too large");if(s.exists(b.prev)&&s.exists(b.next)&&u(b.prev.ev,b.next.ev),r&&r.statusRemove(b.ev.seg),b.remove(),!h.primary){var _=h.seg.myFill;h.seg.myFill=h.seg.otherFill,h.seg.otherFill=_}f.push(h.seg)}a.getHead().remove()}return r&&r.done(),f}return t?{addRegion:function(t){for(var n,i,a,o=t[t.length-1],l=0;l=c?(M=1,y=c+2*h+d):y=h*(M=-h/c)+d):(M=0,p>=0?(A=0,y=d):-p>=f?(A=1,y=f+2*p+d):y=p*(A=-p/f)+d);else if(A<0)A=0,h>=0?(M=0,y=d):-h>=c?(M=1,y=c+2*h+d):y=h*(M=-h/c)+d;else{var T=1/k;y=(M*=T)*(c*M+u*(A*=T)+2*h)+A*(u*M+f*A+2*p)+d}else M<0?(b=f+p)>(x=u+h)?(_=b-x)>=(w=c-2*u+f)?(M=1,A=0,y=c+2*h+d):y=(M=_/w)*(c*M+u*(A=1-M)+2*h)+A*(u*M+f*A+2*p)+d:(M=0,b<=0?(A=1,y=f+2*p+d):p>=0?(A=0,y=d):y=p*(A=-p/f)+d):A<0?(b=c+h)>(x=u+p)?(_=b-x)>=(w=c-2*u+f)?(A=1,M=0,y=f+2*p+d):y=(M=1-(A=_/w))*(c*M+u*A+2*h)+A*(u*M+f*A+2*p)+d:(A=0,b<=0?(M=1,y=c+2*h+d):h>=0?(M=0,y=d):y=h*(M=-h/c)+d):(_=f+p-u-h)<=0?(M=0,A=1,y=f+2*p+d):_>=(w=c-2*u+f)?(M=1,A=0,y=c+2*h+d):y=(M=_/w)*(c*M+u*(A=1-M)+2*h)+A*(u*M+f*A+2*p)+d;var S=1-M-A;for(l=0;l1)for(var r=1;r0){var c=t[r-1];if(0===n(s,c)&&a(c)!==l){r-=1;continue}}t[r++]=s}}return t.length=r,t}},{"cell-orientation":93,"compare-cell":109,"compare-oriented-cell":110}],432:[function(t,e,r){"use strict";var n=t("array-bounds"),i=t("color-normalize"),a=t("update-diff"),o=t("pick-by-alias"),s=t("object-assign"),l=t("flatten-vertex-data"),c=t("to-float32"),u=c.float32,f=c.fract32;e.exports=function(t,e){"function"==typeof t?(e||(e={}),e.regl=t):e=t;e.length&&(e.positions=e);if(!(t=e.regl).hasExtension("ANGLE_instanced_arrays"))throw Error("regl-error2d: `ANGLE_instanced_arrays` extension should be enabled");var r,c,p,d,g,m,v=t._gl,y={color:"black",capSize:5,lineWidth:1,opacity:1,viewport:null,range:null,offset:0,count:0,bounds:null,positions:[],errors:[]},x=[];return d=t.buffer({usage:"dynamic",type:"uint8",data:new Uint8Array(0)}),c=t.buffer({usage:"dynamic",type:"float",data:new Uint8Array(0)}),p=t.buffer({usage:"dynamic",type:"float",data:new Uint8Array(0)}),g=t.buffer({usage:"dynamic",type:"float",data:new Uint8Array(0)}),m=t.buffer({usage:"static",type:"float",data:h}),k(e),r=t({vert:"\n\t\tprecision highp float;\n\n\t\tattribute vec2 position, positionFract;\n\t\tattribute vec4 error;\n\t\tattribute vec4 color;\n\n\t\tattribute vec2 direction, lineOffset, capOffset;\n\n\t\tuniform vec4 viewport;\n\t\tuniform float lineWidth, capSize;\n\t\tuniform vec2 scale, scaleFract, translate, translateFract;\n\n\t\tvarying vec4 fragColor;\n\n\t\tvoid main() {\n\t\t\tfragColor = color / 255.;\n\n\t\t\tvec2 pixelOffset = lineWidth * lineOffset + (capSize + lineWidth) * capOffset;\n\n\t\t\tvec2 dxy = -step(.5, direction.xy) * error.xz + step(direction.xy, vec2(-.5)) * error.yw;\n\n\t\t\tvec2 position = position + dxy;\n\n\t\t\tvec2 pos = (position + translate) * scale\n\t\t\t\t+ (positionFract + translateFract) * scale\n\t\t\t\t+ (position + translate) * scaleFract\n\t\t\t\t+ (positionFract + translateFract) * scaleFract;\n\n\t\t\tpos += pixelOffset / viewport.zw;\n\n\t\t\tgl_Position = vec4(pos * 2. - 1., 0, 1);\n\t\t}\n\t\t",frag:"\n\t\tprecision mediump float;\n\n\t\tvarying vec4 fragColor;\n\n\t\tuniform float opacity;\n\n\t\tvoid main() {\n\t\t\tgl_FragColor = fragColor;\n\t\t\tgl_FragColor.a *= opacity;\n\t\t}\n\t\t",uniforms:{range:t.prop("range"),lineWidth:t.prop("lineWidth"),capSize:t.prop("capSize"),opacity:t.prop("opacity"),scale:t.prop("scale"),translate:t.prop("translate"),scaleFract:t.prop("scaleFract"),translateFract:t.prop("translateFract"),viewport:function(t,e){return[e.viewport.x,e.viewport.y,t.viewportWidth,t.viewportHeight]}},attributes:{color:{buffer:d,offset:function(t,e){return 4*e.offset},divisor:1},position:{buffer:c,offset:function(t,e){return 8*e.offset},divisor:1},positionFract:{buffer:p,offset:function(t,e){return 8*e.offset},divisor:1},error:{buffer:g,offset:function(t,e){return 16*e.offset},divisor:1},direction:{buffer:m,stride:24,offset:0},lineOffset:{buffer:m,stride:24,offset:8},capOffset:{buffer:m,stride:24,offset:16}},primitive:"triangles",blend:{enable:!0,color:[0,0,0,0],equation:{rgb:"add",alpha:"add"},func:{srcRGB:"src alpha",dstRGB:"one minus src alpha",srcAlpha:"one minus dst alpha",dstAlpha:"one"}},depth:{enable:!1},scissor:{enable:!0,box:t.prop("viewport")},viewport:t.prop("viewport"),stencil:!1,instances:t.prop("count"),count:h.length}),s(b,{update:k,draw:_,destroy:M,regl:t,gl:v,canvas:v.canvas,groups:x}),b;function b(t){t?k(t):null===t&&M(),_()}function _(e){if("number"==typeof e)return w(e);e&&!Array.isArray(e)&&(e=[e]),t._refresh(),x.forEach(function(t,r){t&&(e&&(e[r]?t.draw=!0:t.draw=!1),t.draw?w(r):t.draw=!0)})}function w(t){"number"==typeof t&&(t=x[t]),null!=t&&t&&t.count&&t.color&&t.opacity&&t.positions&&t.positions.length>1&&(t.scaleRatio=[t.scale[0]*t.viewport.width,t.scale[1]*t.viewport.height],r(t),t.after&&t.after(t))}function k(t){if(t){null!=t.length?"number"==typeof t[0]&&(t=[{positions:t}]):Array.isArray(t)||(t=[t]);var e=0,r=0;if(b.groups=x=t.map(function(t,c){var u=x[c];return t?("function"==typeof t?t={after:t}:"number"==typeof t[0]&&(t={positions:t}),t=o(t,{color:"color colors fill",capSize:"capSize cap capsize cap-size",lineWidth:"lineWidth line-width width line thickness",opacity:"opacity alpha",range:"range dataBox",viewport:"viewport viewBox",errors:"errors error",positions:"positions position data points"}),u||(x[c]=u={id:c,scale:null,translate:null,scaleFract:null,translateFract:null,draw:!0},t=s({},y,t)),a(u,t,[{lineWidth:function(t){return.5*+t},capSize:function(t){return.5*+t},opacity:parseFloat,errors:function(t){return t=l(t),r+=t.length,t},positions:function(t,r){return t=l(t,"float64"),r.count=Math.floor(t.length/2),r.bounds=n(t,2),r.offset=e,e+=r.count,t}},{color:function(t,e){var r=e.count;if(t||(t="transparent"),!Array.isArray(t)||"number"==typeof t[0]){var n=t;t=Array(r);for(var a=0;a 0. && baClipping < length(normalWidth * endBotJoin)) {\n\t\t//handle miter clipping\n\t\tbTopCoord -= normalWidth * endTopJoin;\n\t\tbTopCoord += normalize(endTopJoin * normalWidth) * baClipping;\n\t}\n\n\tif (nextReverse) {\n\t\t//make join rectangular\n\t\tvec2 miterShift = normalWidth * endJoinDirection * miterLimit * .5;\n\t\tfloat normalAdjust = 1. - min(miterLimit / endMiterRatio, 1.);\n\t\tbBotCoord = bCoord + miterShift - normalAdjust * normalWidth * currNormal * .5;\n\t\tbTopCoord = bCoord + miterShift + normalAdjust * normalWidth * currNormal * .5;\n\t}\n\telse if (!prevReverse && abClipping > 0. && abClipping < length(normalWidth * startBotJoin)) {\n\t\t//handle miter clipping\n\t\taBotCoord -= normalWidth * startBotJoin;\n\t\taBotCoord += normalize(startBotJoin * normalWidth) * abClipping;\n\t}\n\n\tvec2 aTopPosition = (aTopCoord) * scale + translate;\n\tvec2 aBotPosition = (aBotCoord) * scale + translate;\n\n\tvec2 bTopPosition = (bTopCoord) * scale + translate;\n\tvec2 bBotPosition = (bBotCoord) * scale + translate;\n\n\t//position is normalized 0..1 coord on the screen\n\tvec2 position = (aTopPosition * lineTop + aBotPosition * lineBot) * lineStart + (bTopPosition * lineTop + bBotPosition * lineBot) * lineEnd;\n\n\tstartCoord = aCoord * scaleRatio + translate * viewport.zw + viewport.xy;\n\tendCoord = bCoord * scaleRatio + translate * viewport.zw + viewport.xy;\n\n\tgl_Position = vec4(position * 2.0 - 1.0, depth, 1);\n\n\tenableStartMiter = step(dot(currTangent, prevTangent), .5);\n\tenableEndMiter = step(dot(currTangent, nextTangent), .5);\n\n\t//bevel miter cutoffs\n\tif (miterMode == 1.) {\n\t\tif (enableStartMiter == 1.) {\n\t\t\tvec2 startMiterWidth = vec2(startJoinDirection) * thickness * miterLimit * .5;\n\t\t\tstartCutoff = vec4(aCoord, aCoord);\n\t\t\tstartCutoff.zw += vec2(-startJoinDirection.y, startJoinDirection.x) / scaleRatio;\n\t\t\tstartCutoff = startCutoff * scaleRatio.xyxy + translate.xyxy * viewport.zwzw;\n\t\t\tstartCutoff += viewport.xyxy;\n\t\t\tstartCutoff += startMiterWidth.xyxy;\n\t\t}\n\n\t\tif (enableEndMiter == 1.) {\n\t\t\tvec2 endMiterWidth = vec2(endJoinDirection) * thickness * miterLimit * .5;\n\t\t\tendCutoff = vec4(bCoord, bCoord);\n\t\t\tendCutoff.zw += vec2(-endJoinDirection.y, endJoinDirection.x) / scaleRatio;\n\t\t\tendCutoff = endCutoff * scaleRatio.xyxy + translate.xyxy * viewport.zwzw;\n\t\t\tendCutoff += viewport.xyxy;\n\t\t\tendCutoff += endMiterWidth.xyxy;\n\t\t}\n\t}\n\n\t//round miter cutoffs\n\telse if (miterMode == 2.) {\n\t\tif (enableStartMiter == 1.) {\n\t\t\tvec2 startMiterWidth = vec2(startJoinDirection) * thickness * abs(dot(startJoinDirection, currNormal)) * .5;\n\t\t\tstartCutoff = vec4(aCoord, aCoord);\n\t\t\tstartCutoff.zw += vec2(-startJoinDirection.y, startJoinDirection.x) / scaleRatio;\n\t\t\tstartCutoff = startCutoff * scaleRatio.xyxy + translate.xyxy * viewport.zwzw;\n\t\t\tstartCutoff += viewport.xyxy;\n\t\t\tstartCutoff += startMiterWidth.xyxy;\n\t\t}\n\n\t\tif (enableEndMiter == 1.) {\n\t\t\tvec2 endMiterWidth = vec2(endJoinDirection) * thickness * abs(dot(endJoinDirection, currNormal)) * .5;\n\t\t\tendCutoff = vec4(bCoord, bCoord);\n\t\t\tendCutoff.zw += vec2(-endJoinDirection.y, endJoinDirection.x) / scaleRatio;\n\t\t\tendCutoff = endCutoff * scaleRatio.xyxy + translate.xyxy * viewport.zwzw;\n\t\t\tendCutoff += viewport.xyxy;\n\t\t\tendCutoff += endMiterWidth.xyxy;\n\t\t}\n\t}\n}\n"]),frag:o(["precision highp float;\n#define GLSLIFY 1\n\nuniform sampler2D dashPattern;\nuniform float dashSize, pixelRatio, thickness, opacity, id, miterMode;\n\nvarying vec4 fragColor;\nvarying vec2 tangent;\nvarying vec4 startCutoff, endCutoff;\nvarying vec2 startCoord, endCoord;\nvarying float enableStartMiter, enableEndMiter;\n\nfloat distToLine(vec2 p, vec2 a, vec2 b) {\n\tvec2 diff = b - a;\n\tvec2 perp = normalize(vec2(-diff.y, diff.x));\n\treturn dot(p - a, perp);\n}\n\nvoid main() {\n\tfloat alpha = 1., distToStart, distToEnd;\n\tfloat cutoff = thickness * .5;\n\n\t//bevel miter\n\tif (miterMode == 1.) {\n\t\tif (enableStartMiter == 1.) {\n\t\t\tdistToStart = distToLine(gl_FragCoord.xy, startCutoff.xy, startCutoff.zw);\n\t\t\tif (distToStart < -1.) {\n\t\t\t\tdiscard;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\talpha *= min(max(distToStart + 1., 0.), 1.);\n\t\t}\n\n\t\tif (enableEndMiter == 1.) {\n\t\t\tdistToEnd = distToLine(gl_FragCoord.xy, endCutoff.xy, endCutoff.zw);\n\t\t\tif (distToEnd < -1.) {\n\t\t\t\tdiscard;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\talpha *= min(max(distToEnd + 1., 0.), 1.);\n\t\t}\n\t}\n\n\t// round miter\n\telse if (miterMode == 2.) {\n\t\tif (enableStartMiter == 1.) {\n\t\t\tdistToStart = distToLine(gl_FragCoord.xy, startCutoff.xy, startCutoff.zw);\n\t\t\tif (distToStart < 0.) {\n\t\t\t\tfloat radius = length(gl_FragCoord.xy - startCoord);\n\n\t\t\t\tif(radius > cutoff + .5) {\n\t\t\t\t\tdiscard;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\talpha -= smoothstep(cutoff - .5, cutoff + .5, radius);\n\t\t\t}\n\t\t}\n\n\t\tif (enableEndMiter == 1.) {\n\t\t\tdistToEnd = distToLine(gl_FragCoord.xy, endCutoff.xy, endCutoff.zw);\n\t\t\tif (distToEnd < 0.) {\n\t\t\t\tfloat radius = length(gl_FragCoord.xy - endCoord);\n\n\t\t\t\tif(radius > cutoff + .5) {\n\t\t\t\t\tdiscard;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\talpha -= smoothstep(cutoff - .5, cutoff + .5, radius);\n\t\t\t}\n\t\t}\n\t}\n\n\tfloat t = fract(dot(tangent, gl_FragCoord.xy) / dashSize) * .5 + .25;\n\tfloat dash = texture2D(dashPattern, vec2(t, .5)).r;\n\n\tgl_FragColor = fragColor;\n\tgl_FragColor.a *= alpha * opacity * dash;\n}\n"]),attributes:{lineEnd:{buffer:r,divisor:0,stride:8,offset:0},lineTop:{buffer:r,divisor:0,stride:8,offset:4},aColor:{buffer:t.prop("colorBuffer"),stride:4,offset:0,divisor:1},bColor:{buffer:t.prop("colorBuffer"),stride:4,offset:4,divisor:1},prevCoord:{buffer:t.prop("positionBuffer"),stride:8,offset:0,divisor:1},aCoord:{buffer:t.prop("positionBuffer"),stride:8,offset:8,divisor:1},bCoord:{buffer:t.prop("positionBuffer"),stride:8,offset:16,divisor:1},nextCoord:{buffer:t.prop("positionBuffer"),stride:8,offset:24,divisor:1}}},n))}catch(t){e=i}return{fill:t({primitive:"triangle",elements:function(t,e){return e.triangles},offset:0,vert:o(["precision highp float;\n#define GLSLIFY 1\n\nattribute vec2 position, positionFract;\n\nuniform vec4 color;\nuniform vec2 scale, scaleFract, translate, translateFract;\nuniform float pixelRatio, id;\nuniform vec4 viewport;\nuniform float opacity;\n\nvarying vec4 fragColor;\n\nconst float MAX_LINES = 256.;\n\nvoid main() {\n\tfloat depth = (MAX_LINES - 4. - id) / (MAX_LINES);\n\n\tvec2 position = position * scale + translate\n + positionFract * scale + translateFract\n + position * scaleFract\n + positionFract * scaleFract;\n\n\tgl_Position = vec4(position * 2.0 - 1.0, depth, 1);\n\n\tfragColor = color / 255.;\n\tfragColor.a *= opacity;\n}\n"]),frag:o(["precision highp float;\n#define GLSLIFY 1\n\nvarying vec4 fragColor;\n\nvoid main() {\n\tgl_FragColor = fragColor;\n}\n"]),uniforms:{scale:t.prop("scale"),color:t.prop("fill"),scaleFract:t.prop("scaleFract"),translateFract:t.prop("translateFract"),translate:t.prop("translate"),opacity:t.prop("opacity"),pixelRatio:t.context("pixelRatio"),id:t.prop("id"),viewport:function(t,e){return[e.viewport.x,e.viewport.y,t.viewportWidth,t.viewportHeight]}},attributes:{position:{buffer:t.prop("positionBuffer"),stride:8,offset:8},positionFract:{buffer:t.prop("positionFractBuffer"),stride:8,offset:8}},blend:n.blend,depth:{enable:!1},scissor:n.scissor,stencil:n.stencil,viewport:n.viewport}),rect:i,miter:e}},m.defaults={dashes:null,join:"miter",miterLimit:1,thickness:10,cap:"square",color:"black",opacity:1,overlay:!1,viewport:null,range:null,close:!1,fill:null},m.prototype.render=function(){for(var t,e=[],r=arguments.length;r--;)e[r]=arguments[r];e.length&&(t=this).update.apply(t,e),this.draw()},m.prototype.draw=function(){for(var t=this,e=[],r=arguments.length;r--;)e[r]=arguments[r];return(e.length?e:this.passes).forEach(function(e,r){if(e&&Array.isArray(e))return(n=t).draw.apply(n,e);var n;("number"==typeof e&&(e=t.passes[e]),e&&e.count>1&&e.opacity)&&(t.regl._refresh(),e.fill&&e.triangles&&e.triangles.length>2&&t.shaders.fill(e),e.thickness&&(e.scale[0]*e.viewport.width>m.precisionThreshold||e.scale[1]*e.viewport.height>m.precisionThreshold?t.shaders.rect(e):"rect"===e.join||!e.join&&(e.thickness<=2||e.count>=m.maxPoints)?t.shaders.rect(e):t.shaders.miter(e)))}),this},m.prototype.update=function(t){var e=this;if(t){null!=t.length?"number"==typeof t[0]&&(t=[{positions:t}]):Array.isArray(t)||(t=[t]);var r=this.regl,o=this.gl;if(t.forEach(function(t,f){var d=e.passes[f];if(void 0!==t)if(null!==t){if("number"==typeof t[0]&&(t={positions:t}),t=s(t,{positions:"positions points data coords",thickness:"thickness lineWidth lineWidths line-width linewidth width stroke-width strokewidth strokeWidth",join:"lineJoin linejoin join type mode",miterLimit:"miterlimit miterLimit",dashes:"dash dashes dasharray dash-array dashArray",color:"color colour stroke colors colours stroke-color strokeColor",fill:"fill fill-color fillColor",opacity:"alpha opacity",overlay:"overlay crease overlap intersect",close:"closed close closed-path closePath",range:"range dataBox",viewport:"viewport viewBox",hole:"holes hole hollow"}),d||(e.passes[f]=d={id:f,scale:null,scaleFract:null,translate:null,translateFract:null,count:0,hole:[],depth:0,dashLength:1,dashTexture:r.texture({channels:1,data:new Uint8Array([255]),width:1,height:1,mag:"linear",min:"linear"}),colorBuffer:r.buffer({usage:"dynamic",type:"uint8",data:new Uint8Array}),positionBuffer:r.buffer({usage:"dynamic",type:"float",data:new Uint8Array}),positionFractBuffer:r.buffer({usage:"dynamic",type:"float",data:new Uint8Array})},t=a({},m.defaults,t)),null!=t.thickness&&(d.thickness=parseFloat(t.thickness)),null!=t.opacity&&(d.opacity=parseFloat(t.opacity)),null!=t.miterLimit&&(d.miterLimit=parseFloat(t.miterLimit)),null!=t.overlay&&(d.overlay=!!t.overlay,f 1.0 + delta) {\n\t\tdiscard;\n\t}\n\n\talpha -= smoothstep(1.0 - delta, 1.0 + delta, radius);\n\n\tfloat borderRadius = fragBorderRadius;\n\tfloat ratio = smoothstep(borderRadius - delta, borderRadius + delta, radius);\n\tvec4 color = mix(fragColor, fragBorderColor, ratio);\n\tcolor.a *= alpha * opacity;\n\tgl_FragColor = color;\n}\n"]),u.vert=l(["precision highp float;\n#define GLSLIFY 1\n\nattribute float x, y, xFract, yFract;\nattribute float size, borderSize;\nattribute vec4 colorId, borderColorId;\nattribute float isActive;\n\nuniform vec2 scale, scaleFract, translate, translateFract;\nuniform float pixelRatio;\nuniform sampler2D palette;\nuniform vec2 paletteSize;\n\nconst float maxSize = 100.;\n\nvarying vec4 fragColor, fragBorderColor;\nvarying float fragBorderRadius, fragWidth;\n\nvec2 paletteCoord(float id) {\n return vec2(\n (mod(id, paletteSize.x) + .5) / paletteSize.x,\n (floor(id / paletteSize.x) + .5) / paletteSize.y\n );\n}\nvec2 paletteCoord(vec2 id) {\n return vec2(\n (id.x + .5) / paletteSize.x,\n (id.y + .5) / paletteSize.y\n );\n}\n\nvec4 getColor(vec4 id) {\n // zero-palette means we deal with direct buffer\n if (paletteSize.x == 0.) return id / 255.;\n return texture2D(palette, paletteCoord(id.xy));\n}\n\nvoid main() {\n // ignore inactive points\n if (isActive == 0.) return;\n\n vec2 position = vec2(x, y);\n vec2 positionFract = vec2(xFract, yFract);\n\n vec4 color = getColor(colorId);\n vec4 borderColor = getColor(borderColorId);\n\n float size = size * maxSize / 255.;\n float borderSize = borderSize * maxSize / 255.;\n\n gl_PointSize = (size + borderSize) * pixelRatio;\n\n vec2 pos = (position + translate) * scale\n + (positionFract + translateFract) * scale\n + (position + translate) * scaleFract\n + (positionFract + translateFract) * scaleFract;\n\n gl_Position = vec4(pos * 2. - 1., 0, 1);\n\n fragBorderRadius = 1. - 2. * borderSize / (size + borderSize);\n fragColor = color;\n fragBorderColor = borderColor.a == 0. || borderSize == 0. ? vec4(color.rgb, 0.) : borderColor;\n fragWidth = 1. / gl_PointSize;\n}\n"]),h&&(u.frag=u.frag.replace("smoothstep","smoothStep")),this.drawCircle=t(u)}e.exports=v,v.defaults={color:"black",borderColor:"transparent",borderSize:0,size:12,opacity:1,marker:void 0,viewport:null,range:null,pixelSize:null,count:0,offset:0,bounds:null,positions:[],snap:1e4},v.prototype.render=function(){for(var t,e=[],r=arguments.length;r--;)e[r]=arguments[r];return e.length&&(t=this).update.apply(t,e),this.draw(),this},v.prototype.draw=function(){for(var t=this,e=[],r=arguments.length;r--;)e[r]=arguments[r];var n=this.groups;if(1===e.length&&Array.isArray(e[0])&&(null===e[0][0]||Array.isArray(e[0][0]))&&(e=e[0]),this.regl._refresh(),e.length)for(var i=0;in)?e.tree=o(t,{bounds:h}):n&&n.length&&(e.tree=n),e.tree){var p={primitive:"points",usage:"static",data:e.tree,type:"uint32"};e.elements?e.elements(p):e.elements=l.elements(p)}return a({data:d(t),usage:"dynamic"}),s({data:g(t),usage:"dynamic"}),c({data:new Uint8Array(u),type:"uint8",usage:"stream"}),t}},{marker:function(e,r,n){var i=r.activation;if(i.forEach(function(t){return t&&t.destroy&&t.destroy()}),i.length=0,e&&"number"!=typeof e[0]){for(var a=[],o=0,s=Math.min(e.length,r.count);o=0)return a;if(t instanceof Uint8Array||t instanceof Uint8ClampedArray)e=t;else{e=new Uint8Array(t.length);for(var o=0,s=t.length;oi*i*4&&(this.tooManyColors=!0),this.updatePalette(r),1===o.length?o[0]:o},v.prototype.updatePalette=function(t){if(!this.tooManyColors){var e=this.maxColors,r=this.paletteTexture,n=Math.ceil(.25*t.length/e);if(n>1)for(var i=.25*(t=t.slice()).length%e;i2?(s[0],s[2],n=s[1],i=s[3]):s.length?(n=s[0],i=s[1]):(s.x,n=s.y,s.x+s.width,i=s.y+s.height),l.length>2?(a=l[0],o=l[2],l[1],l[3]):l.length?(a=l[0],o=l[1]):(a=l.x,l.y,o=l.x+l.width,l.y+l.height),[a,n,o,i]}function p(t){if("number"==typeof t)return[t,t,t,t];if(2===t.length)return[t[0],t[1],t[0],t[1]];var e=l(t);return[e.x,e.y,e.x+e.width,e.y+e.height]}e.exports=u,u.prototype.render=function(){for(var t,e=this,r=[],n=arguments.length;n--;)r[n]=arguments[n];return r.length&&(t=this).update.apply(t,r),this.regl.attributes.preserveDrawingBuffer?this.draw():(this.dirty?null==this.planned&&(this.planned=o(function(){e.draw(),e.dirty=!0,e.planned=null})):(this.draw(),this.dirty=!0,o(function(){e.dirty=!1})),this)},u.prototype.update=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t.length){for(var r=0;rM))&&(s.lower||!(k>>=e))<<3,(e|=r=(15<(t>>>=r))<<2)|(r=(3<(t>>>=r))<<1)|t>>>r>>1}function l(t){t:{for(var e=16;268435456>=e;e*=16)if(t<=e){t=e;break t}t=0}return 0<(e=Y[s(t)>>2]).length?e.pop():new ArrayBuffer(t)}function c(t){Y[s(t.byteLength)>>2].push(t)}function u(t,e,r,n,i,a){for(var o=0;o(i=l)&&(i=n.buffer.byteLength,5123===f?i>>=1:5125===f&&(i>>=2)),n.vertCount=i,i=s,0>s&&(i=4,1===(s=n.buffer.dimension)&&(i=0),2===s&&(i=1),3===s&&(i=4)),n.primType=i}function s(t){n.elementsCount--,delete l[t.id],t.buffer.destroy(),t.buffer=null}var l={},c=0,u={uint8:5121,uint16:5123};e.oes_element_index_uint&&(u.uint32=5125),i.prototype.bind=function(){this.buffer.bind()};var f=[];return{create:function(t,e){function l(t){if(t)if("number"==typeof t)c(t),f.primType=4,f.vertCount=0|t,f.type=5121;else{var e=null,r=35044,n=-1,i=-1,s=0,h=0;Array.isArray(t)||G(t)||a(t)?e=t:("data"in t&&(e=t.data),"usage"in t&&(r=Q[t.usage]),"primitive"in t&&(n=rt[t.primitive]),"count"in t&&(i=0|t.count),"type"in t&&(h=u[t.type]),"length"in t?s=0|t.length:(s=i,5123===h||5122===h?s*=2:5125!==h&&5124!==h||(s*=4))),o(f,e,r,n,i,s,h)}else c(),f.primType=4,f.vertCount=0,f.type=5121;return l}var c=r.create(null,34963,!0),f=new i(c._buffer);return n.elementsCount++,l(t),l._reglType="elements",l._elements=f,l.subdata=function(t,e){return c.subdata(t,e),l},l.destroy=function(){s(f)},l},createStream:function(t){var e=f.pop();return e||(e=new i(r.create(null,34963,!0,!1)._buffer)),o(e,t,35040,-1,-1,0,0),e},destroyStream:function(t){f.push(t)},getElements:function(t){return"function"==typeof t&&t._elements instanceof i?t._elements:null},clear:function(){W(l).forEach(s)}}}function m(t){for(var e=X.allocType(5123,t.length),r=0;r>>31<<15,i=(a<<1>>>24)-127,a=a>>13&1023;e[r]=-24>i?n:-14>i?n+(a+1024>>-14-i):15>=i,r.height>>=i,p(r,n[i]),t.mipmask|=1<e;++e)t.images[e]=null;return t}function L(t){for(var e=t.images,r=0;re){for(var r=0;r=--this.refCount&&B(this)}}),s.profile&&(o.getTotalTextureSize=function(){var t=0;return Object.keys(ht).forEach(function(e){t+=ht[e].stats.size}),t}),{create2D:function(e,r){function n(t,e){var r=i.texInfo;z.call(r);var a=E();return"number"==typeof t?T(a,0|t,"number"==typeof e?0|e:0|t):t?(P(r,t),S(a,t)):T(a,1,1),r.genMipmaps&&(a.mipmask=(a.width<<1)-1),i.mipmask=a.mipmask,c(i,a),i.internalformat=a.internalformat,n.width=a.width,n.height=a.height,I(i),C(a,3553),D(r,3553),R(),L(a),s.profile&&(i.stats.size=k(i.internalformat,i.type,a.width,a.height,r.genMipmaps,!1)),n.format=tt[i.internalformat],n.type=et[i.type],n.mag=rt[r.magFilter],n.min=nt[r.minFilter],n.wrapS=it[r.wrapS],n.wrapT=it[r.wrapT],n}var i=new O(3553);return ht[i.id]=i,o.textureCount++,n(e,r),n.subimage=function(t,e,r,a){e|=0,r|=0,a|=0;var o=g();return c(o,i),o.width=0,o.height=0,p(o,t),o.width=o.width||(i.width>>a)-e,o.height=o.height||(i.height>>a)-r,I(i),d(o,3553,e,r,a),R(),M(o),n},n.resize=function(e,r){var a=0|e,o=0|r||a;if(a===i.width&&o===i.height)return n;n.width=i.width=a,n.height=i.height=o,I(i);for(var l=0;i.mipmask>>l;++l)t.texImage2D(3553,l,i.format,a>>l,o>>l,0,i.format,i.type,null);return R(),s.profile&&(i.stats.size=k(i.internalformat,i.type,a,o,!1,!1)),n},n._reglType="texture2d",n._texture=i,s.profile&&(n.stats=i.stats),n.destroy=function(){i.decRef()},n},createCube:function(e,r,n,i,a,l){function f(t,e,r,n,i,a){var o,l=h.texInfo;for(z.call(l),o=0;6>o;++o)m[o]=E();if("number"!=typeof t&&t){if("object"==typeof t)if(e)S(m[0],t),S(m[1],e),S(m[2],r),S(m[3],n),S(m[4],i),S(m[5],a);else if(P(l,t),u(h,t),"faces"in t)for(t=t.faces,o=0;6>o;++o)c(m[o],h),S(m[o],t[o]);else for(o=0;6>o;++o)S(m[o],t)}else for(t=0|t||1,o=0;6>o;++o)T(m[o],t,t);for(c(h,m[0]),h.mipmask=l.genMipmaps?(m[0].width<<1)-1:m[0].mipmask,h.internalformat=m[0].internalformat,f.width=m[0].width,f.height=m[0].height,I(h),o=0;6>o;++o)C(m[o],34069+o);for(D(l,34067),R(),s.profile&&(h.stats.size=k(h.internalformat,h.type,f.width,f.height,l.genMipmaps,!0)),f.format=tt[h.internalformat],f.type=et[h.type],f.mag=rt[l.magFilter],f.min=nt[l.minFilter],f.wrapS=it[l.wrapS],f.wrapT=it[l.wrapT],o=0;6>o;++o)L(m[o]);return f}var h=new O(34067);ht[h.id]=h,o.cubeCount++;var m=Array(6);return f(e,r,n,i,a,l),f.subimage=function(t,e,r,n,i){r|=0,n|=0,i|=0;var a=g();return c(a,h),a.width=0,a.height=0,p(a,e),a.width=a.width||(h.width>>i)-r,a.height=a.height||(h.height>>i)-n,I(h),d(a,34069+t,r,n,i),R(),M(a),f},f.resize=function(e){if((e|=0)!==h.width){f.width=h.width=e,f.height=h.height=e,I(h);for(var r=0;6>r;++r)for(var n=0;h.mipmask>>n;++n)t.texImage2D(34069+r,n,h.format,e>>n,e>>n,0,h.format,h.type,null);return R(),s.profile&&(h.stats.size=k(h.internalformat,h.type,f.width,f.height,!1,!0)),f}},f._reglType="textureCube",f._texture=h,s.profile&&(f.stats=h.stats),f.destroy=function(){h.decRef()},f},clear:function(){for(var e=0;er;++r)if(0!=(e.mipmask&1<>r,e.height>>r,0,e.internalformat,e.type,null);else for(var n=0;6>n;++n)t.texImage2D(34069+n,r,e.internalformat,e.width>>r,e.height>>r,0,e.internalformat,e.type,null);D(e.texInfo,e.target)})}}}function A(t,e,r,n,i,a){function o(t,e,r){this.target=t,this.texture=e,this.renderbuffer=r;var n=t=0;e?(t=e.width,n=e.height):r&&(t=r.width,n=r.height),this.width=t,this.height=n}function s(t){t&&(t.texture&&t.texture._texture.decRef(),t.renderbuffer&&t.renderbuffer._renderbuffer.decRef())}function l(t,e,r){t&&(t.texture?t.texture._texture.refCount+=1:t.renderbuffer._renderbuffer.refCount+=1)}function c(e,r){r&&(r.texture?t.framebufferTexture2D(36160,e,r.target,r.texture._texture.texture,0):t.framebufferRenderbuffer(36160,e,36161,r.renderbuffer._renderbuffer.renderbuffer))}function u(t){var e=3553,r=null,n=null,i=t;return"object"==typeof t&&(i=t.data,"target"in t&&(e=0|t.target)),"texture2d"===(t=i._reglType)?r=i:"textureCube"===t?r=i:"renderbuffer"===t&&(n=i,e=36161),new o(e,r,n)}function f(t,e,r,a,s){return r?((t=n.create2D({width:t,height:e,format:a,type:s}))._texture.refCount=0,new o(3553,t,null)):((t=i.create({width:t,height:e,format:a}))._renderbuffer.refCount=0,new o(36161,null,t))}function h(t){return t&&(t.texture||t.renderbuffer)}function p(t,e,r){t&&(t.texture?t.texture.resize(e,r):t.renderbuffer&&t.renderbuffer.resize(e,r))}function d(){this.id=k++,M[this.id]=this,this.framebuffer=t.createFramebuffer(),this.height=this.width=0,this.colorAttachments=[],this.depthStencilAttachment=this.stencilAttachment=this.depthAttachment=null}function g(t){t.colorAttachments.forEach(s),s(t.depthAttachment),s(t.stencilAttachment),s(t.depthStencilAttachment)}function m(e){t.deleteFramebuffer(e.framebuffer),e.framebuffer=null,a.framebufferCount--,delete M[e.id]}function v(e){var n;t.bindFramebuffer(36160,e.framebuffer);var i=e.colorAttachments;for(n=0;ni;++i){for(c=0;ct;++t)r[t].resize(n);return e.width=e.height=n,e},_reglType:"framebufferCube",destroy:function(){r.forEach(function(t){t.destroy()})}})},clear:function(){W(M).forEach(m)},restore:function(){W(M).forEach(function(e){e.framebuffer=t.createFramebuffer(),v(e)})}})}function T(){this.w=this.z=this.y=this.x=this.state=0,this.buffer=null,this.size=0,this.normalized=!1,this.type=5126,this.divisor=this.stride=this.offset=0}function S(t,e,r,n){function i(t,e,r,n){this.name=t,this.id=e,this.location=r,this.info=n}function a(t,e){for(var r=0;rt&&(t=e.stats.uniformsCount)}),t},r.getMaxAttributesCount=function(){var t=0;return h.forEach(function(e){e.stats.attributesCount>t&&(t=e.stats.attributesCount)}),t}),{clear:function(){var e=t.deleteShader.bind(t);W(c).forEach(e),c={},W(u).forEach(e),u={},h.forEach(function(e){t.deleteProgram(e.program)}),h.length=0,f={},r.shaderCount=0},program:function(t,e,n){var i=f[e];i||(i=f[e]={});var a=i[t];return a||(a=new s(e,t),r.shaderCount++,l(a),i[t]=a,h.push(a)),a},restore:function(){c={},u={};for(var t=0;t="+e+"?"+i+".constant["+e+"]:0;"}).join(""),"}}else{","if(",o,"(",i,".buffer)){",u,"=",s,".createStream(",34962,",",i,".buffer);","}else{",u,"=",s,".getBuffer(",i,".buffer);","}",f,'="type" in ',i,"?",a.glTypes,"[",i,".type]:",u,".dtype;",l.normalized,"=!!",i,".normalized;"),n("size"),n("offset"),n("stride"),n("divisor"),r("}}"),r.exit("if(",l.isStream,"){",s,".destroyStream(",u,");","}"),l})}),o}function A(t,e,r,n,i){var a=_(t),s=function(t,e,r){function n(t){if(t in i){var r=i[t];t=!0;var n,o,s=0|r.x,l=0|r.y;return"width"in r?n=0|r.width:t=!1,"height"in r?o=0|r.height:t=!1,new O(!t&&e&&e.thisDep,!t&&e&&e.contextDep,!t&&e&&e.propDep,function(t,e){var i=t.shared.context,a=n;"width"in r||(a=e.def(i,".","framebufferWidth","-",s));var c=o;return"height"in r||(c=e.def(i,".","framebufferHeight","-",l)),[s,l,a,c]})}if(t in a){var c=a[t];return t=B(c,function(t,e){var r=t.invoke(e,c),n=t.shared.context,i=e.def(r,".x|0"),a=e.def(r,".y|0");return[i,a,e.def('"width" in ',r,"?",r,".width|0:","(",n,".","framebufferWidth","-",i,")"),r=e.def('"height" in ',r,"?",r,".height|0:","(",n,".","framebufferHeight","-",a,")")]}),e&&(t.thisDep=t.thisDep||e.thisDep,t.contextDep=t.contextDep||e.contextDep,t.propDep=t.propDep||e.propDep),t}return e?new O(e.thisDep,e.contextDep,e.propDep,function(t,e){var r=t.shared.context;return[0,0,e.def(r,".","framebufferWidth"),e.def(r,".","framebufferHeight")]}):null}var i=t.static,a=t.dynamic;if(t=n("viewport")){var o=t;t=new O(t.thisDep,t.contextDep,t.propDep,function(t,e){var r=o.append(t,e),n=t.shared.context;return e.set(n,".viewportWidth",r[2]),e.set(n,".viewportHeight",r[3]),r})}return{viewport:t,scissor_box:n("scissor.box")}}(t,a),l=k(t),c=function(t,e){var r=t.static,n=t.dynamic,i={};return nt.forEach(function(t){function e(e,o){if(t in r){var s=e(r[t]);i[a]=R(function(){return s})}else if(t in n){var l=n[t];i[a]=B(l,function(t,e){return o(t,e,t.invoke(e,l))})}}var a=m(t);switch(t){case"cull.enable":case"blend.enable":case"dither":case"stencil.enable":case"depth.enable":case"scissor.enable":case"polygonOffset.enable":case"sample.alpha":case"sample.enable":case"depth.mask":return e(function(t){return t},function(t,e,r){return r});case"depth.func":return e(function(t){return yt[t]},function(t,e,r){return e.def(t.constants.compareFuncs,"[",r,"]")});case"depth.range":return e(function(t){return t},function(t,e,r){return[e.def("+",r,"[0]"),e=e.def("+",r,"[1]")]});case"blend.func":return e(function(t){return[vt["srcRGB"in t?t.srcRGB:t.src],vt["dstRGB"in t?t.dstRGB:t.dst],vt["srcAlpha"in t?t.srcAlpha:t.src],vt["dstAlpha"in t?t.dstAlpha:t.dst]]},function(t,e,r){function n(t,n){return e.def('"',t,n,'" in ',r,"?",r,".",t,n,":",r,".",t)}t=t.constants.blendFuncs;var i=n("src","RGB"),a=n("dst","RGB"),o=(i=e.def(t,"[",i,"]"),e.def(t,"[",n("src","Alpha"),"]"));return[i,a=e.def(t,"[",a,"]"),o,t=e.def(t,"[",n("dst","Alpha"),"]")]});case"blend.equation":return e(function(t){return"string"==typeof t?[J[t],J[t]]:"object"==typeof t?[J[t.rgb],J[t.alpha]]:void 0},function(t,e,r){var n=t.constants.blendEquations,i=e.def(),a=e.def();return(t=t.cond("typeof ",r,'==="string"')).then(i,"=",a,"=",n,"[",r,"];"),t.else(i,"=",n,"[",r,".rgb];",a,"=",n,"[",r,".alpha];"),e(t),[i,a]});case"blend.color":return e(function(t){return o(4,function(e){return+t[e]})},function(t,e,r){return o(4,function(t){return e.def("+",r,"[",t,"]")})});case"stencil.mask":return e(function(t){return 0|t},function(t,e,r){return e.def(r,"|0")});case"stencil.func":return e(function(t){return[yt[t.cmp||"keep"],t.ref||0,"mask"in t?t.mask:-1]},function(t,e,r){return[t=e.def('"cmp" in ',r,"?",t.constants.compareFuncs,"[",r,".cmp]",":",7680),e.def(r,".ref|0"),e=e.def('"mask" in ',r,"?",r,".mask|0:-1")]});case"stencil.opFront":case"stencil.opBack":return e(function(e){return["stencil.opBack"===t?1029:1028,xt[e.fail||"keep"],xt[e.zfail||"keep"],xt[e.zpass||"keep"]]},function(e,r,n){function i(t){return r.def('"',t,'" in ',n,"?",a,"[",n,".",t,"]:",7680)}var a=e.constants.stencilOps;return["stencil.opBack"===t?1029:1028,i("fail"),i("zfail"),i("zpass")]});case"polygonOffset.offset":return e(function(t){return[0|t.factor,0|t.units]},function(t,e,r){return[e.def(r,".factor|0"),e=e.def(r,".units|0")]});case"cull.face":return e(function(t){var e=0;return"front"===t?e=1028:"back"===t&&(e=1029),e},function(t,e,r){return e.def(r,'==="front"?',1028,":",1029)});case"lineWidth":return e(function(t){return t},function(t,e,r){return r});case"frontFace":return e(function(t){return bt[t]},function(t,e,r){return e.def(r+'==="cw"?2304:2305')});case"colorMask":return e(function(t){return t.map(function(t){return!!t})},function(t,e,r){return o(4,function(t){return"!!"+r+"["+t+"]"})});case"sample.coverage":return e(function(t){return["value"in t?t.value:1,!!t.invert]},function(t,e,r){return[e.def('"value" in ',r,"?+",r,".value:1"),e=e.def("!!",r,".invert")]})}}),i}(t),u=w(t),f=s.viewport;return f&&(c.viewport=f),(s=s[f=m("scissor.box")])&&(c[f]=s),(a={framebuffer:a,draw:l,shader:u,state:c,dirty:s=0>1)",s],");")}function e(){r(l,".drawArraysInstancedANGLE(",[d,g,m,s],");")}p?y?t():(r("if(",p,"){"),t(),r("}else{"),e(),r("}")):e()}function o(){function t(){r(u+".drawElements("+[d,m,v,g+"<<(("+v+"-5121)>>1)"]+");")}function e(){r(u+".drawArrays("+[d,g,m]+");")}p?y?t():(r("if(",p,"){"),t(),r("}else{"),e(),r("}")):e()}var s,l,c=t.shared,u=c.gl,f=c.draw,h=n.draw,p=function(){var i=h.elements,a=e;return i?((i.contextDep&&n.contextDynamic||i.propDep)&&(a=r),i=i.append(t,a)):i=a.def(f,".","elements"),i&&a("if("+i+")"+u+".bindBuffer(34963,"+i+".buffer.buffer);"),i}(),d=i("primitive"),g=i("offset"),m=function(){var i=h.count,a=e;return i?((i.contextDep&&n.contextDynamic||i.propDep)&&(a=r),i=i.append(t,a)):i=a.def(f,".","count"),i}();if("number"==typeof m){if(0===m)return}else r("if(",m,"){"),r.exit("}");Q&&(s=i("instances"),l=t.instancing);var v=p+".type",y=h.elements&&I(h.elements);Q&&("number"!=typeof s||0<=s)?"string"==typeof s?(r("if(",s,">0){"),a(),r("}else if(",s,"<0){"),o(),r("}")):a():o()}function q(t,e,r,n,i){return i=(e=b()).proc("body",i),Q&&(e.instancing=i.def(e.shared.extensions,".angle_instanced_arrays")),t(e,i,r,n),e.compile().body}function H(t,e,r,n){L(t,e),N(t,e,r,n.attributes,function(){return!0}),j(t,e,r,n.uniforms,function(){return!0}),V(t,e,e,r)}function G(t,e,r,n){function i(){return!0}t.batchId="a1",L(t,e),N(t,e,r,n.attributes,i),j(t,e,r,n.uniforms,i),V(t,e,e,r)}function W(t,e,r,n){function i(t){return t.contextDep&&o||t.propDep}function a(t){return!i(t)}L(t,e);var o=r.contextDep,s=e.def(),l=e.def();t.shared.props=l,t.batchId=s;var c=t.scope(),u=t.scope();e(c.entry,"for(",s,"=0;",s,"<","a1",";++",s,"){",l,"=","a0","[",s,"];",u,"}",c.exit),r.needsContext&&T(t,u,r.context),r.needsFramebuffer&&S(t,u,r.framebuffer),E(t,u,r.state,i),r.profile&&i(r.profile)&&F(t,u,r,!1,!0),n?(N(t,c,r,n.attributes,a),N(t,u,r,n.attributes,i),j(t,c,r,n.uniforms,a),j(t,u,r,n.uniforms,i),V(t,c,u,r)):(e=t.global.def("{}"),n=r.shader.progVar.append(t,u),l=u.def(n,".id"),c=u.def(e,"[",l,"]"),u(t.shared.gl,".useProgram(",n,".program);","if(!",c,"){",c,"=",e,"[",l,"]=",t.link(function(e){return q(G,t,r,e,2)}),"(",n,");}",c,".call(this,a0[",s,"],",s,");"))}function Y(t,r){function n(e){var n=r.shader[e];n&&i.set(a.shader,"."+e,n.append(t,i))}var i=t.proc("scope",3);t.batchId="a2";var a=t.shared,o=a.current;T(t,i,r.context),r.framebuffer&&r.framebuffer.append(t,i),D(Object.keys(r.state)).forEach(function(e){var n=r.state[e].append(t,i);v(n)?n.forEach(function(r,n){i.set(t.next[e],"["+n+"]",r)}):i.set(a.next,"."+e,n)}),F(t,i,r,!0,!0),["elements","offset","count","instances","primitive"].forEach(function(e){var n=r.draw[e];n&&i.set(a.draw,"."+e,""+n.append(t,i))}),Object.keys(r.uniforms).forEach(function(n){i.set(a.uniforms,"["+e.id(n)+"]",r.uniforms[n].append(t,i))}),Object.keys(r.attributes).forEach(function(e){var n=r.attributes[e].append(t,i),a=t.scopeAttrib(e);Object.keys(new Z).forEach(function(t){i.set(a,"."+t,n[t])})}),n("vert"),n("frag"),0=--this.refCount&&o(this)},i.profile&&(n.getTotalRenderbufferSize=function(){var t=0;return Object.keys(u).forEach(function(e){t+=u[e].stats.size}),t}),{create:function(e,r){function o(e,r){var n=0,a=0,u=32854;if("object"==typeof e&&e?("shape"in e?(n=0|(a=e.shape)[0],a=0|a[1]):("radius"in e&&(n=a=0|e.radius),"width"in e&&(n=0|e.width),"height"in e&&(a=0|e.height)),"format"in e&&(u=s[e.format])):"number"==typeof e?(n=0|e,a="number"==typeof r?0|r:n):e||(n=a=1),n!==c.width||a!==c.height||u!==c.format)return o.width=c.width=n,o.height=c.height=a,c.format=u,t.bindRenderbuffer(36161,c.renderbuffer),t.renderbufferStorage(36161,u,n,a),i.profile&&(c.stats.size=ft[c.format]*c.width*c.height),o.format=l[c.format],o}var c=new a(t.createRenderbuffer());return u[c.id]=c,n.renderbufferCount++,o(e,r),o.resize=function(e,r){var n=0|e,a=0|r||n;return n===c.width&&a===c.height?o:(o.width=c.width=n,o.height=c.height=a,t.bindRenderbuffer(36161,c.renderbuffer),t.renderbufferStorage(36161,c.format,n,a),i.profile&&(c.stats.size=ft[c.format]*c.width*c.height),o)},o._reglType="renderbuffer",o._renderbuffer=c,i.profile&&(o.stats=c.stats),o.destroy=function(){c.decRef()},o},clear:function(){W(u).forEach(o)},restore:function(){W(u).forEach(function(e){e.renderbuffer=t.createRenderbuffer(),t.bindRenderbuffer(36161,e.renderbuffer),t.renderbufferStorage(36161,e.format,e.width,e.height)}),t.bindRenderbuffer(36161,null)}}},pt=[];pt[6408]=4;var dt=[];dt[5121]=1,dt[5126]=4,dt[36193]=2;var gt=["x","y","z","w"],mt="blend.func blend.equation stencil.func stencil.opFront stencil.opBack sample.coverage viewport scissor.box polygonOffset.offset".split(" "),vt={0:0,1:1,zero:0,one:1,"src color":768,"one minus src color":769,"src alpha":770,"one minus src alpha":771,"dst color":774,"one minus dst color":775,"dst alpha":772,"one minus dst alpha":773,"constant color":32769,"one minus constant color":32770,"constant alpha":32771,"one minus constant alpha":32772,"src alpha saturate":776},yt={never:512,less:513,"<":513,equal:514,"=":514,"==":514,"===":514,lequal:515,"<=":515,greater:516,">":516,notequal:517,"!=":517,"!==":517,gequal:518,">=":518,always:519},xt={0:0,zero:0,keep:7680,replace:7681,increment:7682,decrement:7683,"increment wrap":34055,"decrement wrap":34056,invert:5386},bt={cw:2304,ccw:2305},_t=new O(!1,!1,!1,function(){});return function(t){function e(){if(0===X.length)w&&w.update(),Q=null;else{Q=q.next(e),f();for(var t=X.length-1;0<=t;--t){var r=X[t];r&&r(z,null,0)}m.flush(),w&&w.update()}}function r(){!Q&&0=X.length&&n()}}}}function u(){var t=W.viewport,e=W.scissor_box;t[0]=t[1]=e[0]=e[1]=0,z.viewportWidth=z.framebufferWidth=z.drawingBufferWidth=t[2]=e[2]=m.drawingBufferWidth,z.viewportHeight=z.framebufferHeight=z.drawingBufferHeight=t[3]=e[3]=m.drawingBufferHeight}function f(){z.tick+=1,z.time=p(),u(),G.procs.poll()}function h(){u(),G.procs.refresh(),w&&w.update()}function p(){return(H()-k)/1e3}if(!(t=i(t)))return null;var m=t.gl,v=m.getContextAttributes();m.isContextLost();var y=function(t,e){function r(e){var r;e=e.toLowerCase();try{r=n[e]=t.getExtension(e)}catch(t){}return!!r}for(var n={},i=0;ie;++e)$(j({framebuffer:t.framebuffer.faces[e]},t),l);else $(t,l);else l(0,t)},prop:U.define.bind(null,1),context:U.define.bind(null,2),this:U.define.bind(null,3),draw:s({}),buffer:function(t){return D.create(t,34962,!1,!1)},elements:function(t){return O.create(t,!1)},texture:R.create2D,cube:R.createCube,renderbuffer:B.create,framebuffer:V.create,framebufferCube:V.createCube,attributes:v,frame:c,on:function(t,e){var r;switch(t){case"frame":return c(e);case"lost":r=Z;break;case"restore":r=J;break;case"destroy":r=K}return r.push(e),{cancel:function(){for(var t=0;t=r)return i.substr(0,r);for(;r>i.length&&e>1;)1&e&&(i+=t),e>>=1,t+=t;return i=(i+=t).substr(0,r)}},{}],440:[function(t,e,r){(function(t){e.exports=t.performance&&t.performance.now?function(){return performance.now()}:Date.now||function(){return+new Date}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],441:[function(t,e,r){"use strict";e.exports=function(t){for(var e=t.length,r=t[t.length-1],n=e,i=e-2;i>=0;--i){var a=r,o=t[i],s=(r=a+o)-a,l=o-s;l&&(t[--n]=r,r=l)}for(var c=0,i=n;i>1;return["sum(",t(e.slice(0,r)),",",t(e.slice(r)),")"].join("")}(e);var n}function u(t){return new Function("sum","scale","prod","compress",["function robustDeterminant",t,"(m){return compress(",c(function(t){for(var e=new Array(t),r=0;r>1;return["sum(",c(t.slice(0,e)),",",c(t.slice(e)),")"].join("")}function u(t,e){if("m"===t.charAt(0)){if("w"===e.charAt(0)){var r=t.split("[");return["w",e.substr(1),"m",r[0].substr(1)].join("")}return["prod(",t,",",e,")"].join("")}return u(e,t)}function f(t){if(2===t.length)return[["diff(",u(t[0][0],t[1][1]),",",u(t[1][0],t[0][1]),")"].join("")];for(var e=[],r=0;r0&&r.push(","),r.push("[");for(var o=0;o0&&r.push(","),o===i?r.push("+b[",a,"]"):r.push("+A[",a,"][",o,"]");r.push("]")}r.push("]),")}r.push("det(A)]}return ",e);var s=new Function("det",r.join(""));return s(t<6?n[t]:n)}var o=[function(){return[0]},function(t,e){return[[e[0]],[t[0][0]]]}];!function(){for(;o.length>1;return["sum(",c(t.slice(0,e)),",",c(t.slice(e)),")"].join("")}function u(t){if(2===t.length)return[["sum(prod(",t[0][0],",",t[1][1],"),prod(-",t[0][1],",",t[1][0],"))"].join("")];for(var e=[],r=0;r0){if(a<=0)return o;n=i+a}else{if(!(i<0))return o;if(a>=0)return o;n=-(i+a)}var s=3.3306690738754716e-16*n;return o>=s||o<=-s?o:h(t,e,r)},function(t,e,r,n){var i=t[0]-n[0],a=e[0]-n[0],o=r[0]-n[0],s=t[1]-n[1],l=e[1]-n[1],c=r[1]-n[1],u=t[2]-n[2],f=e[2]-n[2],h=r[2]-n[2],d=a*c,g=o*l,m=o*s,v=i*c,y=i*l,x=a*s,b=u*(d-g)+f*(m-v)+h*(y-x),_=7.771561172376103e-16*((Math.abs(d)+Math.abs(g))*Math.abs(u)+(Math.abs(m)+Math.abs(v))*Math.abs(f)+(Math.abs(y)+Math.abs(x))*Math.abs(h));return b>_||-b>_?b:p(t,e,r,n)}];!function(){for(;d.length<=s;)d.push(f(d.length));for(var t=[],r=["slow"],n=0;n<=s;++n)t.push("a"+n),r.push("o"+n);var i=["function getOrientation(",t.join(),"){switch(arguments.length){case 0:case 1:return 0;"];for(n=2;n<=s;++n)i.push("case ",n,":return o",n,"(",t.slice(0,n).join(),");");i.push("}var s=new Array(arguments.length);for(var i=0;i0&&o>0||a<0&&o<0)return!1;var s=n(r,t,e),l=n(i,t,e);if(s>0&&l>0||s<0&&l<0)return!1;if(0===a&&0===o&&0===s&&0===l)return function(t,e,r,n){for(var i=0;i<2;++i){var a=t[i],o=e[i],s=Math.min(a,o),l=Math.max(a,o),c=r[i],u=n[i],f=Math.min(c,u),h=Math.max(c,u);if(h=n?(i=f,(l+=1)=n?(i=f,(l+=1)0?1:0}},{}],453:[function(t,e,r){"use strict";e.exports=function(t){return i(n(t))};var n=t("boundary-cells"),i=t("reduce-simplicial-complex")},{"boundary-cells":77,"reduce-simplicial-complex":431}],454:[function(t,e,r){"use strict";e.exports=function(t,e,r,s){r=r||0,void 0===s&&(s=function(t){for(var e=t.length,r=0,n=0;n>1,v=E[2*m+1];","if(v===b){return m}","if(b0&&l.push(","),l.push("[");for(var n=0;n0&&l.push(","),l.push("B(C,E,c[",i[0],"],c[",i[1],"])")}l.push("]")}l.push(");")}}for(var a=t+1;a>1;--a){a>1,s=a(t[o],e);s<=0?(0===s&&(i=o),r=o+1):s>0&&(n=o-1)}return i}function u(t,e){for(var r=new Array(t.length),i=0,o=r.length;i=t.length||0!==a(t[m],s)););}return r}function f(t,e){if(e<0)return[];for(var r=[],i=(1<>>u&1&&c.push(i[u]);e.push(c)}return s(e)},r.skeleton=f,r.boundary=function(t){for(var e=[],r=0,n=t.length;r>1:(t>>1)-1}function x(t){for(var e=v(t);;){var r=e,n=2*t+1,i=2*(t+1),a=t;if(n0;){var r=y(t);if(r>=0){var n=v(r);if(e0){var t=M[0];return m(0,S-1),S-=1,x(0),t}return-1}function w(t,e){var r=M[t];return c[r]===e?t:(c[r]=-1/0,b(t),_(),c[r]=e,b((S+=1)-1))}function k(t){if(!u[t]){u[t]=!0;var e=s[t],r=l[t];s[r]>=0&&(s[r]=e),l[e]>=0&&(l[e]=r),A[e]>=0&&w(A[e],g(e)),A[r]>=0&&w(A[r],g(r))}}for(var M=[],A=new Array(a),f=0;f>1;f>=0;--f)x(f);for(;;){var C=_();if(C<0||c[C]>r)break;k(C)}for(var E=[],f=0;f=0&&r>=0&&e!==r){var n=A[e],i=A[r];n!==i&&z.push([n,i])}}),i.unique(i.normalize(z)),{positions:E,edges:z}};var n=t("robust-orientation"),i=t("simplicial-complex")},{"robust-orientation":446,"simplicial-complex":458}],461:[function(t,e,r){"use strict";e.exports=function(t,e){var r,a,o,s;if(e[0][0]e[1][0]))return i(e,t);r=e[1],a=e[0]}if(t[0][0]t[1][0]))return-i(t,e);o=t[1],s=t[0]}var l=n(r,a,s),c=n(r,a,o);if(l<0){if(c<=0)return l}else if(l>0){if(c>=0)return l}else if(c)return c;if(l=n(s,o,a),c=n(s,o,r),l<0){if(c<=0)return l}else if(l>0){if(c>=0)return l}else if(c)return c;return a[0]-s[0]};var n=t("robust-orientation");function i(t,e){var r,i,a,o;if(e[0][0]e[1][0])){var s=Math.min(t[0][1],t[1][1]),l=Math.max(t[0][1],t[1][1]),c=Math.min(e[0][1],e[1][1]),u=Math.max(e[0][1],e[1][1]);return lu?s-u:l-u}r=e[1],i=e[0]}t[0][1]0)if(e[0]!==o[1][0])r=t,t=t.right;else{if(l=c(t.right,e))return l;t=t.left}else{if(e[0]!==o[1][0])return t;var l;if(l=c(t.right,e))return l;t=t.left}}return r}function u(t,e,r,n){this.y=t,this.index=e,this.start=r,this.closed=n}function f(t,e,r,n){this.x=t,this.segment=e,this.create=r,this.index=n}s.prototype.castUp=function(t){var e=n.le(this.coordinates,t[0]);if(e<0)return-1;this.slabs[e];var r=c(this.slabs[e],t),i=-1;if(r&&(i=r.value),this.coordinates[e]===t[0]){var s=null;if(r&&(s=r.key),e>0){var u=c(this.slabs[e-1],t);u&&(s?o(u.key,s)>0&&(s=u.key,i=u.value):(i=u.value,s=u.key))}var f=this.horizontal[e];if(f.length>0){var h=n.ge(f,t[1],l);if(h=f.length)return i;p=f[h]}}if(p.start)if(s){var d=a(s[0],s[1],[t[0],p.y]);s[0][0]>s[1][0]&&(d=-d),d>0&&(i=p.index)}else i=p.index;else p.y!==t[1]&&(i=p.index)}}}return i}},{"./lib/order-segments":461,"binary-search-bounds":73,"functional-red-black-tree":200,"robust-orientation":446}],463:[function(t,e,r){"use strict";var n=t("robust-dot-product"),i=t("robust-sum");function a(t,e){var r=i(n(t,e),[e[e.length-1]]);return r[r.length-1]}function o(t,e,r,n){var i=-e/(n-e);i<0?i=0:i>1&&(i=1);for(var a=1-i,o=t.length,s=new Array(o),l=0;l0||i>0&&u<0){var f=o(s,u,l,i);r.push(f),n.push(f.slice())}u<0?n.push(l.slice()):u>0?r.push(l.slice()):(r.push(l.slice()),n.push(l.slice())),i=u}return{positive:r,negative:n}},e.exports.positive=function(t,e){for(var r=[],n=a(t[t.length-1],e),i=t[t.length-1],s=t[0],l=0;l0||n>0&&c<0)&&r.push(o(i,c,s,n)),c>=0&&r.push(s.slice()),n=c}return r},e.exports.negative=function(t,e){for(var r=[],n=a(t[t.length-1],e),i=t[t.length-1],s=t[0],l=0;l0||n>0&&c<0)&&r.push(o(i,c,s,n)),c<=0&&r.push(s.slice()),n=c}return r}},{"robust-dot-product":443,"robust-sum":451}],464:[function(t,e,r){!function(){"use strict";var t={not_string:/[^s]/,not_bool:/[^t]/,not_type:/[^T]/,not_primitive:/[^v]/,number:/[diefg]/,numeric_arg:/[bcdiefguxX]/,json:/[j]/,not_json:/[^j]/,text:/^[^\x25]+/,modulo:/^\x25{2}/,placeholder:/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,key:/^([a-z_][a-z_\d]*)/i,key_access:/^\.([a-z_][a-z_\d]*)/i,index_access:/^\[(\d+)\]/,sign:/^[\+\-]/};function e(r){return function(r,n){var i,a,o,s,l,c,u,f,h,p=1,d=r.length,g="";for(a=0;a=0),s[8]){case"b":i=parseInt(i,10).toString(2);break;case"c":i=String.fromCharCode(parseInt(i,10));break;case"d":case"i":i=parseInt(i,10);break;case"j":i=JSON.stringify(i,null,s[6]?parseInt(s[6]):0);break;case"e":i=s[7]?parseFloat(i).toExponential(s[7]):parseFloat(i).toExponential();break;case"f":i=s[7]?parseFloat(i).toFixed(s[7]):parseFloat(i);break;case"g":i=s[7]?String(Number(i.toPrecision(s[7]))):parseFloat(i);break;case"o":i=(parseInt(i,10)>>>0).toString(8);break;case"s":i=String(i),i=s[7]?i.substring(0,s[7]):i;break;case"t":i=String(!!i),i=s[7]?i.substring(0,s[7]):i;break;case"T":i=Object.prototype.toString.call(i).slice(8,-1).toLowerCase(),i=s[7]?i.substring(0,s[7]):i;break;case"u":i=parseInt(i,10)>>>0;break;case"v":i=i.valueOf(),i=s[7]?i.substring(0,s[7]):i;break;case"x":i=(parseInt(i,10)>>>0).toString(16);break;case"X":i=(parseInt(i,10)>>>0).toString(16).toUpperCase()}t.json.test(s[8])?g+=i:(!t.number.test(s[8])||f&&!s[3]?h="":(h=f?"+":"-",i=i.toString().replace(t.sign,"")),c=s[4]?"0"===s[4]?"0":s[4].charAt(1):" ",u=s[6]-(h+i).length,l=s[6]&&u>0?c.repeat(u):"",g+=s[5]?h+i+l:"0"===c?h+l+i:l+h+i)}return g}(function(e){if(i[e])return i[e];var r,n=e,a=[],o=0;for(;n;){if(null!==(r=t.text.exec(n)))a.push(r[0]);else if(null!==(r=t.modulo.exec(n)))a.push("%");else{if(null===(r=t.placeholder.exec(n)))throw new SyntaxError("[sprintf] unexpected placeholder");if(r[2]){o|=1;var s=[],l=r[2],c=[];if(null===(c=t.key.exec(l)))throw new SyntaxError("[sprintf] failed to parse named argument key");for(s.push(c[1]);""!==(l=l.substring(c[0].length));)if(null!==(c=t.key_access.exec(l)))s.push(c[1]);else{if(null===(c=t.index_access.exec(l)))throw new SyntaxError("[sprintf] failed to parse named argument key");s.push(c[1])}r[2]=s}else o|=2;if(3===o)throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported");a.push(r)}n=n.substring(r[0].length)}return i[e]=a}(r),arguments)}function n(t,r){return e.apply(null,[t].concat(r||[]))}var i=Object.create(null);void 0!==r&&(r.sprintf=e,r.vsprintf=n),"undefined"!=typeof window&&(window.sprintf=e,window.vsprintf=n)}()},{}],465:[function(t,e,r){"use strict";e.exports=function(t){for(var e=t.length,r=new Array(e),n=new Array(e),i=new Array(e),a=new Array(e),o=new Array(e),s=new Array(e),l=0;l0;){e=c[c.length-1];var p=t[e];if(a[e]=0&&s[e].push(o[g])}a[e]=d}else{if(n[e]===r[e]){for(var m=[],v=[],y=0,d=l.length-1;d>=0;--d){var x=l[d];if(i[x]=!1,m.push(x),v.push(s[x]),y+=s[x].length,o[x]=f.length,x===e){l.length=d;break}}f.push(m);for(var b=new Array(y),d=0;d c)|0 },"),"generic"===e&&a.push("getters:[0],");for(var s=[],l=[],c=0;c>>7){");for(var c=0;c<1<<(1<128&&c%128==0){f.length>0&&h.push("}}");var p="vExtra"+f.length;a.push("case ",c>>>7,":",p,"(m&0x7f,",l.join(),");break;"),h=["function ",p,"(m,",l.join(),"){switch(m){"],f.push(h)}h.push("case ",127&c,":");for(var d=new Array(r),g=new Array(r),m=new Array(r),v=new Array(r),y=0,x=0;xx)&&!(c&1<<_)!=!(c&1<0&&(A="+"+m[b]+"*c");var T=d[b].length/y*.5,S=.5+v[b]/y*.5;M.push("d"+b+"-"+S+"-"+T+"*("+d[b].join("+")+A+")/("+g[b].join("+")+")")}h.push("a.push([",M.join(),"]);","break;")}a.push("}},"),f.length>0&&h.push("}}");for(var C=[],c=0;c<1<1&&(a=1),a<-1&&(a=-1),i*Math.acos(a)};r.default=function(t){var e=t.px,r=t.py,l=t.cx,c=t.cy,u=t.rx,f=t.ry,h=t.xAxisRotation,p=void 0===h?0:h,d=t.largeArcFlag,g=void 0===d?0:d,m=t.sweepFlag,v=void 0===m?0:m,y=[];if(0===u||0===f)return[];var x=Math.sin(p*i/360),b=Math.cos(p*i/360),_=b*(e-l)/2+x*(r-c)/2,w=-x*(e-l)/2+b*(r-c)/2;if(0===_&&0===w)return[];u=Math.abs(u),f=Math.abs(f);var k=Math.pow(_,2)/Math.pow(u,2)+Math.pow(w,2)/Math.pow(f,2);k>1&&(u*=Math.sqrt(k),f*=Math.sqrt(k));var M=function(t,e,r,n,a,o,l,c,u,f,h,p){var d=Math.pow(a,2),g=Math.pow(o,2),m=Math.pow(h,2),v=Math.pow(p,2),y=d*g-d*v-g*m;y<0&&(y=0),y/=d*v+g*m;var x=(y=Math.sqrt(y)*(l===c?-1:1))*a/o*p,b=y*-o/a*h,_=f*x-u*b+(t+r)/2,w=u*x+f*b+(e+n)/2,k=(h-x)/a,M=(p-b)/o,A=(-h-x)/a,T=(-p-b)/o,S=s(1,0,k,M),C=s(k,M,A,T);return 0===c&&C>0&&(C-=i),1===c&&C<0&&(C+=i),[_,w,S,C]}(e,r,l,c,u,f,g,v,x,b,_,w),A=n(M,4),T=A[0],S=A[1],C=A[2],E=A[3],L=Math.max(Math.ceil(Math.abs(E)/(i/4)),1);E/=L;for(var z=0;ze[2]&&(e[2]=c[u+0]),c[u+1]>e[3]&&(e[3]=c[u+1]);return e}},{"abs-svg-path":45,assert:53,"is-svg-path":367,"normalize-svg-path":470,"parse-svg-path":402}],470:[function(t,e,r){"use strict";e.exports=function(t){for(var e,r=[],o=0,s=0,l=0,c=0,u=null,f=null,h=0,p=0,d=0,g=t.length;d4?(o=m[m.length-4],s=m[m.length-3]):(o=h,s=p),r.push(m)}return r};var n=t("svg-arc-to-cubic-bezier");function i(t,e,r,n){return["C",t,e,r,n,r,n]}function a(t,e,r,n,i,a){return["C",t/3+2/3*r,e/3+2/3*n,i/3+2/3*r,a/3+2/3*n,i,a]}},{"svg-arc-to-cubic-bezier":468}],471:[function(t,e,r){(function(r){"use strict";var n=t("svg-path-bounds"),i=t("parse-svg-path"),a=t("draw-svg-path"),o=t("is-svg-path"),s=t("bitmap-sdf"),l=document.createElement("canvas"),c=l.getContext("2d");e.exports=function(t,e){if(!o(t))throw Error("Argument should be valid svg path string");e||(e={});var u,f;e.shape?(u=e.shape[0],f=e.shape[1]):(u=l.width=e.w||e.width||200,f=l.height=e.h||e.height||200);var h=Math.min(u,f),p=e.stroke||0,d=e.viewbox||e.viewBox||n(t),g=[u/(d[2]-d[0]),f/(d[3]-d[1])],m=Math.min(g[0]||0,g[1]||0)/2;c.fillStyle="black",c.fillRect(0,0,u,f),c.fillStyle="white",p&&("number"!=typeof p&&(p=1),c.strokeStyle=p>0?"white":"black",c.lineWidth=Math.abs(p));if(c.translate(.5*u,.5*f),c.scale(m,m),r.Path2D){var v=new Path2D(t);c.fill(v),p&&c.stroke(v)}else{var y=i(t);a(c,y),c.fill(),p&&c.stroke()}return c.setTransform(1,0,0,1,0,0),s(c,{cutoff:null!=e.cutoff?e.cutoff:.5,radius:null!=e.radius?e.radius:.5*h})}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"bitmap-sdf":75,"draw-svg-path":135,"is-svg-path":367,"parse-svg-path":402,"svg-path-bounds":469}],472:[function(t,e,r){(function(r){"use strict";e.exports=function t(e,r,i){var i=i||{};var o=a[e];o||(o=a[e]={" ":{data:new Float32Array(0),shape:.2}});var s=o[r];if(!s)if(r.length<=1||!/\d/.test(r))s=o[r]=function(t){for(var e=t.cells,r=t.positions,n=new Float32Array(6*e.length),i=0,a=0,o=0;o0&&(f+=.02);for(var p=new Float32Array(u),d=0,g=-.5*f,h=0;h1&&(r-=1),r<1/6?t+6*(e-t)*r:r<.5?e:r<2/3?t+(e-t)*(2/3-r)*6:t}if(t=L(t,360),e=L(e,100),r=L(r,100),0===e)n=i=a=r;else{var s=r<.5?r*(1+e):r+e-r*e,l=2*r-s;n=o(l,s,t+1/3),i=o(l,s,t),a=o(l,s,t-1/3)}return{r:255*n,g:255*i,b:255*a}}(e.h,l,u),f=!0,h="hsl"),e.hasOwnProperty("a")&&(a=e.a));var p,d,g;return a=E(a),{ok:f,format:e.format||h,r:o(255,s(i.r,0)),g:o(255,s(i.g,0)),b:o(255,s(i.b,0)),a:a}}(e);this._originalInput=e,this._r=u.r,this._g=u.g,this._b=u.b,this._a=u.a,this._roundA=a(100*this._a)/100,this._format=l.format||u.format,this._gradientType=l.gradientType,this._r<1&&(this._r=a(this._r)),this._g<1&&(this._g=a(this._g)),this._b<1&&(this._b=a(this._b)),this._ok=u.ok,this._tc_id=i++}function u(t,e,r){t=L(t,255),e=L(e,255),r=L(r,255);var n,i,a=s(t,e,r),l=o(t,e,r),c=(a+l)/2;if(a==l)n=i=0;else{var u=a-l;switch(i=c>.5?u/(2-a-l):u/(a+l),a){case t:n=(e-r)/u+(e>1)+720)%360;--e;)n.h=(n.h+i)%360,a.push(c(n));return a}function T(t,e){e=e||6;for(var r=c(t).toHsv(),n=r.h,i=r.s,a=r.v,o=[],s=1/e;e--;)o.push(c({h:n,s:i,v:a})),a=(a+s)%1;return o}c.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var t=this.toRgb();return(299*t.r+587*t.g+114*t.b)/1e3},getLuminance:function(){var e,r,n,i=this.toRgb();return e=i.r/255,r=i.g/255,n=i.b/255,.2126*(e<=.03928?e/12.92:t.pow((e+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:t.pow((r+.055)/1.055,2.4))+.0722*(n<=.03928?n/12.92:t.pow((n+.055)/1.055,2.4))},setAlpha:function(t){return this._a=E(t),this._roundA=a(100*this._a)/100,this},toHsv:function(){var t=f(this._r,this._g,this._b);return{h:360*t.h,s:t.s,v:t.v,a:this._a}},toHsvString:function(){var t=f(this._r,this._g,this._b),e=a(360*t.h),r=a(100*t.s),n=a(100*t.v);return 1==this._a?"hsv("+e+", "+r+"%, "+n+"%)":"hsva("+e+", "+r+"%, "+n+"%, "+this._roundA+")"},toHsl:function(){var t=u(this._r,this._g,this._b);return{h:360*t.h,s:t.s,l:t.l,a:this._a}},toHslString:function(){var t=u(this._r,this._g,this._b),e=a(360*t.h),r=a(100*t.s),n=a(100*t.l);return 1==this._a?"hsl("+e+", "+r+"%, "+n+"%)":"hsla("+e+", "+r+"%, "+n+"%, "+this._roundA+")"},toHex:function(t){return h(this._r,this._g,this._b,t)},toHexString:function(t){return"#"+this.toHex(t)},toHex8:function(t){return function(t,e,r,n,i){var o=[D(a(t).toString(16)),D(a(e).toString(16)),D(a(r).toString(16)),D(I(n))];if(i&&o[0].charAt(0)==o[0].charAt(1)&&o[1].charAt(0)==o[1].charAt(1)&&o[2].charAt(0)==o[2].charAt(1)&&o[3].charAt(0)==o[3].charAt(1))return o[0].charAt(0)+o[1].charAt(0)+o[2].charAt(0)+o[3].charAt(0);return o.join("")}(this._r,this._g,this._b,this._a,t)},toHex8String:function(t){return"#"+this.toHex8(t)},toRgb:function(){return{r:a(this._r),g:a(this._g),b:a(this._b),a:this._a}},toRgbString:function(){return 1==this._a?"rgb("+a(this._r)+", "+a(this._g)+", "+a(this._b)+")":"rgba("+a(this._r)+", "+a(this._g)+", "+a(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:a(100*L(this._r,255))+"%",g:a(100*L(this._g,255))+"%",b:a(100*L(this._b,255))+"%",a:this._a}},toPercentageRgbString:function(){return 1==this._a?"rgb("+a(100*L(this._r,255))+"%, "+a(100*L(this._g,255))+"%, "+a(100*L(this._b,255))+"%)":"rgba("+a(100*L(this._r,255))+"%, "+a(100*L(this._g,255))+"%, "+a(100*L(this._b,255))+"%, "+this._roundA+")"},toName:function(){return 0===this._a?"transparent":!(this._a<1)&&(C[h(this._r,this._g,this._b,!0)]||!1)},toFilter:function(t){var e="#"+p(this._r,this._g,this._b,this._a),r=e,n=this._gradientType?"GradientType = 1, ":"";if(t){var i=c(t);r="#"+p(i._r,i._g,i._b,i._a)}return"progid:DXImageTransform.Microsoft.gradient("+n+"startColorstr="+e+",endColorstr="+r+")"},toString:function(t){var e=!!t;t=t||this._format;var r=!1,n=this._a<1&&this._a>=0;return e||!n||"hex"!==t&&"hex6"!==t&&"hex3"!==t&&"hex4"!==t&&"hex8"!==t&&"name"!==t?("rgb"===t&&(r=this.toRgbString()),"prgb"===t&&(r=this.toPercentageRgbString()),"hex"!==t&&"hex6"!==t||(r=this.toHexString()),"hex3"===t&&(r=this.toHexString(!0)),"hex4"===t&&(r=this.toHex8String(!0)),"hex8"===t&&(r=this.toHex8String()),"name"===t&&(r=this.toName()),"hsl"===t&&(r=this.toHslString()),"hsv"===t&&(r=this.toHsvString()),r||this.toHexString()):"name"===t&&0===this._a?this.toName():this.toRgbString()},clone:function(){return c(this.toString())},_applyModification:function(t,e){var r=t.apply(null,[this].concat([].slice.call(e)));return this._r=r._r,this._g=r._g,this._b=r._b,this.setAlpha(r._a),this},lighten:function(){return this._applyModification(v,arguments)},brighten:function(){return this._applyModification(y,arguments)},darken:function(){return this._applyModification(x,arguments)},desaturate:function(){return this._applyModification(d,arguments)},saturate:function(){return this._applyModification(g,arguments)},greyscale:function(){return this._applyModification(m,arguments)},spin:function(){return this._applyModification(b,arguments)},_applyCombination:function(t,e){return t.apply(null,[this].concat([].slice.call(e)))},analogous:function(){return this._applyCombination(A,arguments)},complement:function(){return this._applyCombination(_,arguments)},monochromatic:function(){return this._applyCombination(T,arguments)},splitcomplement:function(){return this._applyCombination(M,arguments)},triad:function(){return this._applyCombination(w,arguments)},tetrad:function(){return this._applyCombination(k,arguments)}},c.fromRatio=function(t,e){if("object"==typeof t){var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]="a"===n?t[n]:O(t[n]));t=r}return c(t,e)},c.equals=function(t,e){return!(!t||!e)&&c(t).toRgbString()==c(e).toRgbString()},c.random=function(){return c.fromRatio({r:l(),g:l(),b:l()})},c.mix=function(t,e,r){r=0===r?0:r||50;var n=c(t).toRgb(),i=c(e).toRgb(),a=r/100;return c({r:(i.r-n.r)*a+n.r,g:(i.g-n.g)*a+n.g,b:(i.b-n.b)*a+n.b,a:(i.a-n.a)*a+n.a})},c.readability=function(e,r){var n=c(e),i=c(r);return(t.max(n.getLuminance(),i.getLuminance())+.05)/(t.min(n.getLuminance(),i.getLuminance())+.05)},c.isReadable=function(t,e,r){var n,i,a=c.readability(t,e);switch(i=!1,(n=function(t){var e,r;e=((t=t||{level:"AA",size:"small"}).level||"AA").toUpperCase(),r=(t.size||"small").toLowerCase(),"AA"!==e&&"AAA"!==e&&(e="AA");"small"!==r&&"large"!==r&&(r="small");return{level:e,size:r}}(r)).level+n.size){case"AAsmall":case"AAAlarge":i=a>=4.5;break;case"AAlarge":i=a>=3;break;case"AAAsmall":i=a>=7}return i},c.mostReadable=function(t,e,r){var n,i,a,o,s=null,l=0;i=(r=r||{}).includeFallbackColors,a=r.level,o=r.size;for(var u=0;ul&&(l=n,s=c(e[u]));return c.isReadable(t,s,{level:a,size:o})||!i?s:(r.includeFallbackColors=!1,c.mostReadable(t,["#fff","#000"],r))};var S=c.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"},C=c.hexNames=function(t){var e={};for(var r in t)t.hasOwnProperty(r)&&(e[t[r]]=r);return e}(S);function E(t){return t=parseFloat(t),(isNaN(t)||t<0||t>1)&&(t=1),t}function L(e,r){(function(t){return"string"==typeof t&&-1!=t.indexOf(".")&&1===parseFloat(t)})(e)&&(e="100%");var n=function(t){return"string"==typeof t&&-1!=t.indexOf("%")}(e);return e=o(r,s(0,parseFloat(e))),n&&(e=parseInt(e*r,10)/100),t.abs(e-r)<1e-6?1:e%r/parseFloat(r)}function z(t){return o(1,s(0,t))}function P(t){return parseInt(t,16)}function D(t){return 1==t.length?"0"+t:""+t}function O(t){return t<=1&&(t=100*t+"%"),t}function I(e){return t.round(255*parseFloat(e)).toString(16)}function R(t){return P(t)/255}var B,F,N,j=(F="[\\s|\\(]+("+(B="(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)")+")[,|\\s]+("+B+")[,|\\s]+("+B+")\\s*\\)?",N="[\\s|\\(]+("+B+")[,|\\s]+("+B+")[,|\\s]+("+B+")[,|\\s]+("+B+")\\s*\\)?",{CSS_UNIT:new RegExp(B),rgb:new RegExp("rgb"+F),rgba:new RegExp("rgba"+N),hsl:new RegExp("hsl"+F),hsla:new RegExp("hsla"+N),hsv:new RegExp("hsv"+F),hsva:new RegExp("hsva"+N),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/});function V(t){return!!j.CSS_UNIT.exec(t)}void 0!==e&&e.exports?e.exports=c:window.tinycolor=c}(Math)},{}],474:[function(t,e,r){"use strict";function n(t){if(t instanceof Float32Array)return t;if("number"==typeof t)return new Float32Array([t])[0];var e=new Float32Array(t);return e.set(t),e}e.exports=n,e.exports.float32=e.exports.float=n,e.exports.fract32=e.exports.fract=function(t){if("number"==typeof t)return n(t-n(t));for(var e=n(t),r=0,i=e.length;rf&&(f=l[0]),l[1]h&&(h=l[1])}function i(t){switch(t.type){case"GeometryCollection":t.geometries.forEach(i);break;case"Point":n(t.coordinates);break;case"MultiPoint":t.coordinates.forEach(n)}}if(!e){var a,o,s=r(t),l=new Array(2),c=1/0,u=c,f=-c,h=-c;for(o in t.arcs.forEach(function(t){for(var e=-1,r=t.length;++ef&&(f=l[0]),l[1]h&&(h=l[1])}),t.objects)i(t.objects[o]);e=t.bbox=[c,u,f,h]}return e},i=function(t,e){for(var r,n=t.length,i=n-e;i<--n;)r=t[i],t[i++]=t[n],t[n]=r};function a(t,e){var r=e.id,n=e.bbox,i=null==e.properties?{}:e.properties,a=o(t,e);return null==r&&null==n?{type:"Feature",properties:i,geometry:a}:null==n?{type:"Feature",id:r,properties:i,geometry:a}:{type:"Feature",id:r,bbox:n,properties:i,geometry:a}}function o(t,e){var n=r(t),a=t.arcs;function o(t,e){e.length&&e.pop();for(var r=a[t<0?~t:t],o=0,s=r.length;o1)n=function(t,e,r){var n,i=[],a=[];function o(t){var e=t<0?~t:t;(a[e]||(a[e]=[])).push({i:t,g:n})}function s(t){t.forEach(o)}function l(t){t.forEach(s)}return function t(e){switch(n=e,e.type){case"GeometryCollection":e.geometries.forEach(t);break;case"LineString":s(e.arcs);break;case"MultiLineString":case"Polygon":l(e.arcs);break;case"MultiPolygon":e.arcs.forEach(l)}}(e),a.forEach(null==r?function(t){i.push(t[0].i)}:function(t){r(t[0].g,t[t.length-1].g)&&i.push(t[0].i)}),i}(0,e,r);else for(i=0,n=new Array(a=t.arcs.length);i1)for(var a,o,c=1,u=l(i[0]);cu&&(o=i[0],i[0]=i[c],i[c]=o,u=a);return i})}}var u=function(t,e){for(var r=0,n=t.length;r>>1;t[i]=2))throw new Error("n must be \u22652");if(t.transform)throw new Error("already quantized");var r,i=n(t),a=i[0],o=(i[2]-a)/(e-1)||1,s=i[1],l=(i[3]-s)/(e-1)||1;function c(t){t[0]=Math.round((t[0]-a)/o),t[1]=Math.round((t[1]-s)/l)}function u(t){switch(t.type){case"GeometryCollection":t.geometries.forEach(u);break;case"Point":c(t.coordinates);break;case"MultiPoint":t.coordinates.forEach(c)}}for(r in t.arcs.forEach(function(t){for(var e,r,n,i=1,c=1,u=t.length,f=t[0],h=f[0]=Math.round((f[0]-a)/o),p=f[1]=Math.round((f[1]-s)/l);iMath.max(r,n)?i[2]=1:r>Math.max(e,n)?i[0]=1:i[1]=1;for(var a=0,o=0,l=0;l<3;++l)a+=t[l]*t[l],o+=i[l]*t[l];for(l=0;l<3;++l)i[l]-=o/a*t[l];return s(i,i),i}function h(t,e,r,i,a,o,s,l){this.center=n(r),this.up=n(i),this.right=n(a),this.radius=n([o]),this.angle=n([s,l]),this.angle.bounds=[[-1/0,-Math.PI/2],[1/0,Math.PI/2]],this.setDistanceLimits(t,e),this.computedCenter=this.center.curve(0),this.computedUp=this.up.curve(0),this.computedRight=this.right.curve(0),this.computedRadius=this.radius.curve(0),this.computedAngle=this.angle.curve(0),this.computedToward=[0,0,0],this.computedEye=[0,0,0],this.computedMatrix=new Array(16);for(var c=0;c<16;++c)this.computedMatrix[c]=.5;this.recalcMatrix(0)}var p=h.prototype;p.setDistanceLimits=function(t,e){t=t>0?Math.log(t):-1/0,e=e>0?Math.log(e):1/0,e=Math.max(e,t),this.radius.bounds[0][0]=t,this.radius.bounds[1][0]=e},p.getDistanceLimits=function(t){var e=this.radius.bounds[0];return t?(t[0]=Math.exp(e[0][0]),t[1]=Math.exp(e[1][0]),t):[Math.exp(e[0][0]),Math.exp(e[1][0])]},p.recalcMatrix=function(t){this.center.curve(t),this.up.curve(t),this.right.curve(t),this.radius.curve(t),this.angle.curve(t);for(var e=this.computedUp,r=this.computedRight,n=0,i=0,a=0;a<3;++a)i+=e[a]*r[a],n+=e[a]*e[a];var l=Math.sqrt(n),u=0;for(a=0;a<3;++a)r[a]-=e[a]*i/n,u+=r[a]*r[a],e[a]/=l;var f=Math.sqrt(u);for(a=0;a<3;++a)r[a]/=f;var h=this.computedToward;o(h,e,r),s(h,h);var p=Math.exp(this.computedRadius[0]),d=this.computedAngle[0],g=this.computedAngle[1],m=Math.cos(d),v=Math.sin(d),y=Math.cos(g),x=Math.sin(g),b=this.computedCenter,_=m*y,w=v*y,k=x,M=-m*x,A=-v*x,T=y,S=this.computedEye,C=this.computedMatrix;for(a=0;a<3;++a){var E=_*r[a]+w*h[a]+k*e[a];C[4*a+1]=M*r[a]+A*h[a]+T*e[a],C[4*a+2]=E,C[4*a+3]=0}var L=C[1],z=C[5],P=C[9],D=C[2],O=C[6],I=C[10],R=z*I-P*O,B=P*D-L*I,F=L*O-z*D,N=c(R,B,F);R/=N,B/=N,F/=N,C[0]=R,C[4]=B,C[8]=F;for(a=0;a<3;++a)S[a]=b[a]+C[2+4*a]*p;for(a=0;a<3;++a){u=0;for(var j=0;j<3;++j)u+=C[a+4*j]*S[j];C[12+a]=-u}C[15]=1},p.getMatrix=function(t,e){this.recalcMatrix(t);var r=this.computedMatrix;if(e){for(var n=0;n<16;++n)e[n]=r[n];return e}return r};var d=[0,0,0];p.rotate=function(t,e,r,n){if(this.angle.move(t,e,r),n){this.recalcMatrix(t);var i=this.computedMatrix;d[0]=i[2],d[1]=i[6],d[2]=i[10];for(var o=this.computedUp,s=this.computedRight,l=this.computedToward,c=0;c<3;++c)i[4*c]=o[c],i[4*c+1]=s[c],i[4*c+2]=l[c];a(i,i,n,d);for(c=0;c<3;++c)o[c]=i[4*c],s[c]=i[4*c+1];this.up.set(t,o[0],o[1],o[2]),this.right.set(t,s[0],s[1],s[2])}},p.pan=function(t,e,r,n){e=e||0,r=r||0,n=n||0,this.recalcMatrix(t);var i=this.computedMatrix,a=(Math.exp(this.computedRadius[0]),i[1]),o=i[5],s=i[9],l=c(a,o,s);a/=l,o/=l,s/=l;var u=i[0],f=i[4],h=i[8],p=u*a+f*o+h*s,d=c(u-=a*p,f-=o*p,h-=s*p),g=(u/=d)*e+a*r,m=(f/=d)*e+o*r,v=(h/=d)*e+s*r;this.center.move(t,g,m,v);var y=Math.exp(this.computedRadius[0]);y=Math.max(1e-4,y+n),this.radius.set(t,Math.log(y))},p.translate=function(t,e,r,n){this.center.move(t,e||0,r||0,n||0)},p.setMatrix=function(t,e,r,n){var a=1;"number"==typeof r&&(a=0|r),(a<0||a>3)&&(a=1);var o=(a+2)%3;e||(this.recalcMatrix(t),e=this.computedMatrix);var s=e[a],l=e[a+4],f=e[a+8];if(n){var h=Math.abs(s),p=Math.abs(l),d=Math.abs(f),g=Math.max(h,p,d);h===g?(s=s<0?-1:1,l=f=0):d===g?(f=f<0?-1:1,s=l=0):(l=l<0?-1:1,s=f=0)}else{var m=c(s,l,f);s/=m,l/=m,f/=m}var v,y,x=e[o],b=e[o+4],_=e[o+8],w=x*s+b*l+_*f,k=c(x-=s*w,b-=l*w,_-=f*w),M=l*(_/=k)-f*(b/=k),A=f*(x/=k)-s*_,T=s*b-l*x,S=c(M,A,T);if(M/=S,A/=S,T/=S,this.center.jump(t,H,G,W),this.radius.idle(t),this.up.jump(t,s,l,f),this.right.jump(t,x,b,_),2===a){var C=e[1],E=e[5],L=e[9],z=C*x+E*b+L*_,P=C*M+E*A+L*T;v=R<0?-Math.PI/2:Math.PI/2,y=Math.atan2(P,z)}else{var D=e[2],O=e[6],I=e[10],R=D*s+O*l+I*f,B=D*x+O*b+I*_,F=D*M+O*A+I*T;v=Math.asin(u(R)),y=Math.atan2(F,B)}this.angle.jump(t,y,v),this.recalcMatrix(t);var N=e[2],j=e[6],V=e[10],U=this.computedMatrix;i(U,e);var q=U[15],H=U[12]/q,G=U[13]/q,W=U[14]/q,Y=Math.exp(this.computedRadius[0]);this.center.jump(t,H-N*Y,G-j*Y,W-V*Y)},p.lastT=function(){return Math.max(this.center.lastT(),this.up.lastT(),this.right.lastT(),this.radius.lastT(),this.angle.lastT())},p.idle=function(t){this.center.idle(t),this.up.idle(t),this.right.idle(t),this.radius.idle(t),this.angle.idle(t)},p.flush=function(t){this.center.flush(t),this.up.flush(t),this.right.flush(t),this.radius.flush(t),this.angle.flush(t)},p.setDistance=function(t,e){e>0&&this.radius.set(t,Math.log(e))},p.lookAt=function(t,e,r,n){this.recalcMatrix(t),e=e||this.computedEye,r=r||this.computedCenter;var i=(n=n||this.computedUp)[0],a=n[1],o=n[2],s=c(i,a,o);if(!(s<1e-6)){i/=s,a/=s,o/=s;var l=e[0]-r[0],f=e[1]-r[1],h=e[2]-r[2],p=c(l,f,h);if(!(p<1e-6)){l/=p,f/=p,h/=p;var d=this.computedRight,g=d[0],m=d[1],v=d[2],y=i*g+a*m+o*v,x=c(g-=y*i,m-=y*a,v-=y*o);if(!(x<.01&&(x=c(g=a*h-o*f,m=o*l-i*h,v=i*f-a*l))<1e-6)){g/=x,m/=x,v/=x,this.up.set(t,i,a,o),this.right.set(t,g,m,v),this.center.set(t,r[0],r[1],r[2]),this.radius.set(t,Math.log(p));var b=a*v-o*m,_=o*g-i*v,w=i*m-a*g,k=c(b,_,w),M=i*l+a*f+o*h,A=g*l+m*f+v*h,T=(b/=k)*l+(_/=k)*f+(w/=k)*h,S=Math.asin(u(M)),C=Math.atan2(T,A),E=this.angle._state,L=E[E.length-1],z=E[E.length-2];L%=2*Math.PI;var P=Math.abs(L+2*Math.PI-C),D=Math.abs(L-C),O=Math.abs(L-2*Math.PI-C);P0?r.pop():new ArrayBuffer(t)}function h(t){return new Uint8Array(f(t),0,t)}function p(t){return new Uint16Array(f(2*t),0,t)}function d(t){return new Uint32Array(f(4*t),0,t)}function g(t){return new Int8Array(f(t),0,t)}function m(t){return new Int16Array(f(2*t),0,t)}function v(t){return new Int32Array(f(4*t),0,t)}function y(t){return new Float32Array(f(4*t),0,t)}function x(t){return new Float64Array(f(8*t),0,t)}function b(t){return o?new Uint8ClampedArray(f(t),0,t):h(t)}function _(t){return new DataView(f(t),0,t)}function w(t){t=i.nextPow2(t);var e=i.log2(t),r=c[e];return r.length>0?r.pop():new n(t)}r.free=function(t){if(n.isBuffer(t))c[i.log2(t.length)].push(t);else{if("[object ArrayBuffer]"!==Object.prototype.toString.call(t)&&(t=t.buffer),!t)return;var e=t.length||t.byteLength,r=0|i.log2(e);l[r].push(t)}},r.freeUint8=r.freeUint16=r.freeUint32=r.freeInt8=r.freeInt16=r.freeInt32=r.freeFloat32=r.freeFloat=r.freeFloat64=r.freeDouble=r.freeUint8Clamped=r.freeDataView=function(t){u(t.buffer)},r.freeArrayBuffer=u,r.freeBuffer=function(t){c[i.log2(t.length)].push(t)},r.malloc=function(t,e){if(void 0===e||"arraybuffer"===e)return f(t);switch(e){case"uint8":return h(t);case"uint16":return p(t);case"uint32":return d(t);case"int8":return g(t);case"int16":return m(t);case"int32":return v(t);case"float":case"float32":return y(t);case"double":case"float64":return x(t);case"uint8_clamped":return b(t);case"buffer":return w(t);case"data":case"dataview":return _(t);default:return null}return null},r.mallocArrayBuffer=f,r.mallocUint8=h,r.mallocUint16=p,r.mallocUint32=d,r.mallocInt8=g,r.mallocInt16=m,r.mallocInt32=v,r.mallocFloat32=r.mallocFloat=y,r.mallocFloat64=r.mallocDouble=x,r.mallocUint8Clamped=b,r.mallocDataView=_,r.mallocBuffer=w,r.clearCache=function(){for(var t=0;t<32;++t)s.UINT8[t].length=0,s.UINT16[t].length=0,s.UINT32[t].length=0,s.INT8[t].length=0,s.INT16[t].length=0,s.INT32[t].length=0,s.FLOAT[t].length=0,s.DOUBLE[t].length=0,s.UINT8C[t].length=0,l[t].length=0,c[t].length=0}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},t("buffer").Buffer)},{"bit-twiddle":74,buffer:86,dup:137}],482:[function(t,e,r){"use strict";"use restrict";function n(t){this.roots=new Array(t),this.ranks=new Array(t);for(var e=0;e=a)return t;switch(t){case"%s":return String(n[r++]);case"%d":return Number(n[r++]);case"%j":try{return JSON.stringify(n[r++])}catch(t){return"[Circular]"}default:return t}}),l=n[r];r=3&&(n.depth=arguments[2]),arguments.length>=4&&(n.colors=arguments[3]),d(e)?n.showHidden=e:e&&r._extend(n,e),y(n.showHidden)&&(n.showHidden=!1),y(n.depth)&&(n.depth=2),y(n.colors)&&(n.colors=!1),y(n.customInspect)&&(n.customInspect=!0),n.colors&&(n.stylize=l),u(n,t,n.depth)}function l(t,e){var r=s.styles[e];return r?"\x1b["+s.colors[r][0]+"m"+t+"\x1b["+s.colors[r][1]+"m":t}function c(t,e){return t}function u(t,e,n){if(t.customInspect&&e&&k(e.inspect)&&e.inspect!==r.inspect&&(!e.constructor||e.constructor.prototype!==e)){var i=e.inspect(n,t);return v(i)||(i=u(t,i,n)),i}var a=function(t,e){if(y(e))return t.stylize("undefined","undefined");if(v(e)){var r="'"+JSON.stringify(e).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return t.stylize(r,"string")}if(m(e))return t.stylize(""+e,"number");if(d(e))return t.stylize(""+e,"boolean");if(g(e))return t.stylize("null","null")}(t,e);if(a)return a;var o=Object.keys(e),s=function(t){var e={};return t.forEach(function(t,r){e[t]=!0}),e}(o);if(t.showHidden&&(o=Object.getOwnPropertyNames(e)),w(e)&&(o.indexOf("message")>=0||o.indexOf("description")>=0))return f(e);if(0===o.length){if(k(e)){var l=e.name?": "+e.name:"";return t.stylize("[Function"+l+"]","special")}if(x(e))return t.stylize(RegExp.prototype.toString.call(e),"regexp");if(_(e))return t.stylize(Date.prototype.toString.call(e),"date");if(w(e))return f(e)}var c,b="",M=!1,A=["{","}"];(p(e)&&(M=!0,A=["[","]"]),k(e))&&(b=" [Function"+(e.name?": "+e.name:"")+"]");return x(e)&&(b=" "+RegExp.prototype.toString.call(e)),_(e)&&(b=" "+Date.prototype.toUTCString.call(e)),w(e)&&(b=" "+f(e)),0!==o.length||M&&0!=e.length?n<0?x(e)?t.stylize(RegExp.prototype.toString.call(e),"regexp"):t.stylize("[Object]","special"):(t.seen.push(e),c=M?function(t,e,r,n,i){for(var a=[],o=0,s=e.length;o=0&&0,t+e.replace(/\u001b\[\d\d?m/g,"").length+1},0)>60)return r[0]+(""===e?"":e+"\n ")+" "+t.join(",\n ")+" "+r[1];return r[0]+e+" "+t.join(", ")+" "+r[1]}(c,b,A)):A[0]+b+A[1]}function f(t){return"["+Error.prototype.toString.call(t)+"]"}function h(t,e,r,n,i,a){var o,s,l;if((l=Object.getOwnPropertyDescriptor(e,i)||{value:e[i]}).get?s=l.set?t.stylize("[Getter/Setter]","special"):t.stylize("[Getter]","special"):l.set&&(s=t.stylize("[Setter]","special")),S(n,i)||(o="["+i+"]"),s||(t.seen.indexOf(l.value)<0?(s=g(r)?u(t,l.value,null):u(t,l.value,r-1)).indexOf("\n")>-1&&(s=a?s.split("\n").map(function(t){return" "+t}).join("\n").substr(2):"\n"+s.split("\n").map(function(t){return" "+t}).join("\n")):s=t.stylize("[Circular]","special")),y(o)){if(a&&i.match(/^\d+$/))return s;(o=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(o=o.substr(1,o.length-2),o=t.stylize(o,"name")):(o=o.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),o=t.stylize(o,"string"))}return o+": "+s}function p(t){return Array.isArray(t)}function d(t){return"boolean"==typeof t}function g(t){return null===t}function m(t){return"number"==typeof t}function v(t){return"string"==typeof t}function y(t){return void 0===t}function x(t){return b(t)&&"[object RegExp]"===M(t)}function b(t){return"object"==typeof t&&null!==t}function _(t){return b(t)&&"[object Date]"===M(t)}function w(t){return b(t)&&("[object Error]"===M(t)||t instanceof Error)}function k(t){return"function"==typeof t}function M(t){return Object.prototype.toString.call(t)}function A(t){return t<10?"0"+t.toString(10):t.toString(10)}r.debuglog=function(t){if(y(a)&&(a=e.env.NODE_DEBUG||""),t=t.toUpperCase(),!o[t])if(new RegExp("\\b"+t+"\\b","i").test(a)){var n=e.pid;o[t]=function(){var e=r.format.apply(r,arguments);console.error("%s %d: %s",t,n,e)}}else o[t]=function(){};return o[t]},r.inspect=s,s.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},s.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},r.isArray=p,r.isBoolean=d,r.isNull=g,r.isNullOrUndefined=function(t){return null==t},r.isNumber=m,r.isString=v,r.isSymbol=function(t){return"symbol"==typeof t},r.isUndefined=y,r.isRegExp=x,r.isObject=b,r.isDate=_,r.isError=w,r.isFunction=k,r.isPrimitive=function(t){return null===t||"boolean"==typeof t||"number"==typeof t||"string"==typeof t||"symbol"==typeof t||void 0===t},r.isBuffer=t("./support/isBuffer");var T=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function S(t,e){return Object.prototype.hasOwnProperty.call(t,e)}r.log=function(){var t,e;console.log("%s - %s",(t=new Date,e=[A(t.getHours()),A(t.getMinutes()),A(t.getSeconds())].join(":"),[t.getDate(),T[t.getMonth()],e].join(" ")),r.format.apply(r,arguments))},r.inherits=t("inherits"),r._extend=function(t,e){if(!e||!b(e))return t;for(var r=Object.keys(e),n=r.length;n--;)t[r[n]]=e[r[n]];return t}}).call(this,t("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./support/isBuffer":486,_process:424,inherits:485}],488:[function(t,e,r){"use strict";e.exports=function(t,e){"object"==typeof e&&null!==e||(e={});return n(t,e.canvas||i,e.context||a,e)};var n=t("./lib/vtext"),i=null,a=null;"undefined"!=typeof document&&((i=document.createElement("canvas")).width=8192,i.height=1024,a=i.getContext("2d"))},{"./lib/vtext":489}],489:[function(t,e,r){"use strict";e.exports=function(t,e,r,n){var a=n.size||64,o=n.font||"normal";return r.font=a+"px "+o,r.textAlign="start",r.textBaseline="alphabetic",r.direction="ltr",f(function(t,e,r,n){var a=0|Math.ceil(e.measureText(r).width+2*n);if(a>8192)throw new Error("vectorize-text: String too long (sorry, this will get fixed later)");var o=3*n;t.height=0?e[a]:i})},has___:{value:x(function(e){var n=y(e);return n?r in n:t.indexOf(e)>=0})},set___:{value:x(function(n,i){var a,o=y(n);return o?o[r]=i:(a=t.indexOf(n))>=0?e[a]=i:(a=t.length,e[a]=i,t[a]=n),this})},delete___:{value:x(function(n){var i,a,o=y(n);return o?r in o&&delete o[r]:!((i=t.indexOf(n))<0||(a=t.length-1,t[i]=void 0,e[i]=e[a],t[i]=t[a],t.length=a,e.length=a,0))})}})};g.prototype=Object.create(Object.prototype,{get:{value:function(t,e){return this.get___(t,e)},writable:!0,configurable:!0},has:{value:function(t){return this.has___(t)},writable:!0,configurable:!0},set:{value:function(t,e){return this.set___(t,e)},writable:!0,configurable:!0},delete:{value:function(t){return this.delete___(t)},writable:!0,configurable:!0}}),"function"==typeof r?function(){function n(){this instanceof g||b();var e,n=new r,i=void 0,a=!1;return e=t?function(t,e){return n.set(t,e),n.has(t)||(i||(i=new g),i.set(t,e)),this}:function(t,e){if(a)try{n.set(t,e)}catch(r){i||(i=new g),i.set___(t,e)}else n.set(t,e);return this},Object.create(g.prototype,{get___:{value:x(function(t,e){return i?n.has(t)?n.get(t):i.get___(t,e):n.get(t,e)})},has___:{value:x(function(t){return n.has(t)||!!i&&i.has___(t)})},set___:{value:x(e)},delete___:{value:x(function(t){var e=!!n.delete(t);return i&&i.delete___(t)||e})},permitHostObjects___:{value:x(function(t){if(t!==m)throw new Error("bogus call to permitHostObjects___");a=!0})}})}t&&"undefined"!=typeof Proxy&&(Proxy=void 0),n.prototype=g.prototype,e.exports=n,Object.defineProperty(WeakMap.prototype,"constructor",{value:WeakMap,enumerable:!1,configurable:!0,writable:!0})}():("undefined"!=typeof Proxy&&(Proxy=void 0),e.exports=g)}function m(t){t.permitHostObjects___&&t.permitHostObjects___(m)}function v(t){return!(t.substr(0,l.length)==l&&"___"===t.substr(t.length-3))}function y(t){if(t!==Object(t))throw new TypeError("Not an object: "+t);var e=t[c];if(e&&e.key===t)return e;if(s(t)){e={key:t};try{return o(t,c,{value:e,writable:!1,enumerable:!1,configurable:!1}),e}catch(t){return}}}function x(t){return t.prototype=null,Object.freeze(t)}function b(){p||"undefined"==typeof console||(p=!0,console.warn("WeakMap should be invoked as new WeakMap(), not WeakMap(). This will be an error in the future."))}}()},{}],491:[function(t,e,r){var n=t("./hidden-store.js");e.exports=function(){var t={};return function(e){if(("object"!=typeof e||null===e)&&"function"!=typeof e)throw new Error("Weakmap-shim: Key must be object");var r=e.valueOf(t);return r&&r.identity===t?r:n(e,t)}}},{"./hidden-store.js":492}],492:[function(t,e,r){e.exports=function(t,e){var r={identity:e},n=t.valueOf;return Object.defineProperty(t,"valueOf",{value:function(t){return t!==e?n.apply(this,arguments):r},writable:!0}),r}},{}],493:[function(t,e,r){var n=t("./create-store.js");e.exports=function(){var t=n();return{get:function(e,r){var n=t(e);return n.hasOwnProperty("value")?n.value:r},set:function(e,r){return t(e).value=r,this},has:function(e){return"value"in t(e)},delete:function(e){return delete t(e).value}}}},{"./create-store.js":491}],494:[function(t,e,r){var n=t("get-canvas-context");e.exports=function(t){return n("webgl",t)}},{"get-canvas-context":202}],495:[function(t,e,r){var n=t("../main"),i=t("object-assign"),a=n.instance();function o(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}o.prototype=new n.baseCalendar,i(o.prototype,{name:"Chinese",jdEpoch:1721425.5,hasYearZero:!1,minMonth:0,firstMonth:0,minDay:1,regionalOptions:{"":{name:"Chinese",epochs:["BEC","EC"],monthNumbers:function(t,e){if("string"==typeof t){var r=t.match(l);return r?r[0]:""}var n=this._validateYear(t),i=t.month(),a=""+this.toChineseMonth(n,i);return e&&a.length<2&&(a="0"+a),this.isIntercalaryMonth(n,i)&&(a+="i"),a},monthNames:function(t){if("string"==typeof t){var e=t.match(c);return e?e[0]:""}var r=this._validateYear(t),n=t.month(),i=["\u4e00\u6708","\u4e8c\u6708","\u4e09\u6708","\u56db\u6708","\u4e94\u6708","\u516d\u6708","\u4e03\u6708","\u516b\u6708","\u4e5d\u6708","\u5341\u6708","\u5341\u4e00\u6708","\u5341\u4e8c\u6708"][this.toChineseMonth(r,n)-1];return this.isIntercalaryMonth(r,n)&&(i="\u95f0"+i),i},monthNamesShort:function(t){if("string"==typeof t){var e=t.match(u);return e?e[0]:""}var r=this._validateYear(t),n=t.month(),i=["\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u5341\u4e00","\u5341\u4e8c"][this.toChineseMonth(r,n)-1];return this.isIntercalaryMonth(r,n)&&(i="\u95f0"+i),i},parseMonth:function(t,e){t=this._validateYear(t);var r,n=parseInt(e);if(isNaN(n))"\u95f0"===e[0]&&(r=!0,e=e.substring(1)),"\u6708"===e[e.length-1]&&(e=e.substring(0,e.length-1)),n=1+["\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u5341\u4e00","\u5341\u4e8c"].indexOf(e);else{var i=e[e.length-1];r="i"===i||"I"===i}return this.toMonthIndex(t,n,r)},dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],digits:null,dateFormat:"yyyy/mm/dd",firstDay:1,isRTL:!1}},_validateYear:function(t,e){if(t.year&&(t=t.year()),"number"!=typeof t||t<1888||t>2111)throw e.replace(/\{0\}/,this.local.name);return t},toMonthIndex:function(t,e,r){var i=this.intercalaryMonth(t);if(r&&e!==i||e<1||e>12)throw n.local.invalidMonth.replace(/\{0\}/,this.local.name);return i?!r&&e<=i?e-1:e:e-1},toChineseMonth:function(t,e){t.year&&(e=(t=t.year()).month());var r=this.intercalaryMonth(t);if(e<0||e>(r?12:11))throw n.local.invalidMonth.replace(/\{0\}/,this.local.name);return r?e>13},isIntercalaryMonth:function(t,e){t.year&&(e=(t=t.year()).month());var r=this.intercalaryMonth(t);return!!r&&r===e},leapYear:function(t){return 0!==this.intercalaryMonth(t)},weekOfYear:function(t,e,r){var i,o=this._validateYear(t,n.local.invalidyear),s=h[o-h[0]],l=s>>9&4095,c=s>>5&15,u=31&s;(i=a.newDate(l,c,u)).add(4-(i.dayOfWeek()||7),"d");var f=this.toJD(t,e,r)-i.toJD();return 1+Math.floor(f/7)},monthsInYear:function(t){return this.leapYear(t)?13:12},daysInMonth:function(t,e){t.year&&(e=t.month(),t=t.year()),t=this._validateYear(t);var r=f[t-f[0]];if(e>(r>>13?12:11))throw n.local.invalidMonth.replace(/\{0\}/,this.local.name);return r&1<<12-e?30:29},weekDay:function(t,e,r){return(this.dayOfWeek(t,e,r)||7)<6},toJD:function(t,e,r){var i=this._validate(t,s,r,n.local.invalidDate);t=this._validateYear(i.year()),e=i.month(),r=i.day();var o=this.isIntercalaryMonth(t,e),s=this.toChineseMonth(t,e),l=function(t,e,r,n,i){var a,o,s;if("object"==typeof t)o=t,a=e||{};else{var l="number"==typeof t&&t>=1888&&t<=2111;if(!l)throw new Error("Lunar year outside range 1888-2111");var c="number"==typeof e&&e>=1&&e<=12;if(!c)throw new Error("Lunar month outside range 1 - 12");var u,p="number"==typeof r&&r>=1&&r<=30;if(!p)throw new Error("Lunar day outside range 1 - 30");"object"==typeof n?(u=!1,a=n):(u=!!n,a=i||{}),o={year:t,month:e,day:r,isIntercalary:u}}s=o.day-1;var d,g=f[o.year-f[0]],m=g>>13;d=m?o.month>m?o.month:o.isIntercalary?o.month:o.month-1:o.month-1;for(var v=0;v>9&4095,(x>>5&15)-1,(31&x)+s);return a.year=b.getFullYear(),a.month=1+b.getMonth(),a.day=b.getDate(),a}(t,s,r,o);return a.toJD(l.year,l.month,l.day)},fromJD:function(t){var e=a.fromJD(t),r=function(t,e,r,n){var i,a;if("object"==typeof t)i=t,a=e||{};else{var o="number"==typeof t&&t>=1888&&t<=2111;if(!o)throw new Error("Solar year outside range 1888-2111");var s="number"==typeof e&&e>=1&&e<=12;if(!s)throw new Error("Solar month outside range 1 - 12");var l="number"==typeof r&&r>=1&&r<=31;if(!l)throw new Error("Solar day outside range 1 - 31");i={year:t,month:e,day:r},a=n||{}}var c=h[i.year-h[0]],u=i.year<<9|i.month<<5|i.day;a.year=u>=c?i.year:i.year-1,c=h[a.year-h[0]];var p,d=new Date(c>>9&4095,(c>>5&15)-1,31&c),g=new Date(i.year,i.month-1,i.day);p=Math.round((g-d)/864e5);var m,v=f[a.year-f[0]];for(m=0;m<13;m++){var y=v&1<<12-m?30:29;if(p>13;!x||m=2&&n<=6},extraInfo:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);return{century:o[Math.floor((i.year()-1)/100)+1]||""}},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);return t=i.year()+(i.year()<0?1:0),e=i.month(),(r=i.day())+(e>1?16:0)+(e>2?32*(e-2):0)+400*(t-1)+this.jdEpoch-1},fromJD:function(t){t=Math.floor(t+.5)-Math.floor(this.jdEpoch)-1;var e=Math.floor(t/400)+1;t-=400*(e-1),t+=t>15?16:0;var r=Math.floor(t/32)+1,n=t-32*(r-1)+1;return this.newDate(e<=0?e-1:e,r,n)}});var o={20:"Fruitbat",21:"Anchovy"};n.calendars.discworld=a},{"../main":509,"object-assign":397}],498:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}a.prototype=new n.baseCalendar,i(a.prototype,{name:"Ethiopian",jdEpoch:1724220.5,daysPerMonth:[30,30,30,30,30,30,30,30,30,30,30,30,5],hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,regionalOptions:{"":{name:"Ethiopian",epochs:["BEE","EE"],monthNames:["Meskerem","Tikemet","Hidar","Tahesas","Tir","Yekatit","Megabit","Miazia","Genbot","Sene","Hamle","Nehase","Pagume"],monthNamesShort:["Mes","Tik","Hid","Tah","Tir","Yek","Meg","Mia","Gen","Sen","Ham","Neh","Pag"],dayNames:["Ehud","Segno","Maksegno","Irob","Hamus","Arb","Kidame"],dayNamesShort:["Ehu","Seg","Mak","Iro","Ham","Arb","Kid"],dayNamesMin:["Eh","Se","Ma","Ir","Ha","Ar","Ki"],digits:null,dateFormat:"dd/mm/yyyy",firstDay:0,isRTL:!1}},leapYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);return(t=e.year()+(e.year()<0?1:0))%4==3||t%4==-1},monthsInYear:function(t){return this._validate(t,this.minMonth,this.minDay,n.local.invalidYear||n.regionalOptions[""].invalidYear),13},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(-n.dayOfWeek(),"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInMonth:function(t,e){var r=this._validate(t,e,this.minDay,n.local.invalidMonth);return this.daysPerMonth[r.month()-1]+(13===r.month()&&this.leapYear(r.year())?1:0)},weekDay:function(t,e,r){return(this.dayOfWeek(t,e,r)||7)<6},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);return(t=i.year())<0&&t++,i.day()+30*(i.month()-1)+365*(t-1)+Math.floor(t/4)+this.jdEpoch-1},fromJD:function(t){var e=Math.floor(t)+.5-this.jdEpoch,r=Math.floor((e-Math.floor((e+366)/1461))/365)+1;r<=0&&r--,e=Math.floor(t)+.5-this.newDate(r,1,1).toJD();var n=Math.floor(e/30)+1,i=e-30*(n-1)+1;return this.newDate(r,n,i)}}),n.calendars.ethiopian=a},{"../main":509,"object-assign":397}],499:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}function o(t,e){return t-e*Math.floor(t/e)}a.prototype=new n.baseCalendar,i(a.prototype,{name:"Hebrew",jdEpoch:347995.5,daysPerMonth:[30,29,30,29,30,29,30,29,30,29,30,29,29],hasYearZero:!1,minMonth:1,firstMonth:7,minDay:1,regionalOptions:{"":{name:"Hebrew",epochs:["BAM","AM"],monthNames:["Nisan","Iyar","Sivan","Tammuz","Av","Elul","Tishrei","Cheshvan","Kislev","Tevet","Shevat","Adar","Adar II"],monthNamesShort:["Nis","Iya","Siv","Tam","Av","Elu","Tis","Che","Kis","Tev","She","Ada","Ad2"],dayNames:["Yom Rishon","Yom Sheni","Yom Shlishi","Yom Revi'i","Yom Chamishi","Yom Shishi","Yom Shabbat"],dayNamesShort:["Ris","She","Shl","Rev","Cha","Shi","Sha"],dayNamesMin:["Ri","She","Shl","Re","Ch","Shi","Sha"],digits:null,dateFormat:"dd/mm/yyyy",firstDay:0,isRTL:!1}},leapYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);return this._leapYear(e.year())},_leapYear:function(t){return o(7*(t=t<0?t+1:t)+1,19)<7},monthsInYear:function(t){return this._validate(t,this.minMonth,this.minDay,n.local.invalidYear),this._leapYear(t.year?t.year():t)?13:12},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(-n.dayOfWeek(),"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInYear:function(t){return t=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear).year(),this.toJD(-1===t?1:t+1,7,1)-this.toJD(t,7,1)},daysInMonth:function(t,e){return t.year&&(e=t.month(),t=t.year()),this._validate(t,e,this.minDay,n.local.invalidMonth),12===e&&this.leapYear(t)?30:8===e&&5===o(this.daysInYear(t),10)?30:9===e&&3===o(this.daysInYear(t),10)?29:this.daysPerMonth[e-1]},weekDay:function(t,e,r){return 6!==this.dayOfWeek(t,e,r)},extraInfo:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);return{yearType:(this.leapYear(i)?"embolismic":"common")+" "+["deficient","regular","complete"][this.daysInYear(i)%10-3]}},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);t=i.year(),e=i.month(),r=i.day();var a=t<=0?t+1:t,o=this.jdEpoch+this._delay1(a)+this._delay2(a)+r+1;if(e<7){for(var s=7;s<=this.monthsInYear(t);s++)o+=this.daysInMonth(t,s);for(s=1;s=this.toJD(-1===e?1:e+1,7,1);)e++;for(var r=tthis.toJD(e,r,this.daysInMonth(e,r));)r++;var n=t-this.toJD(e,r,1)+1;return this.newDate(e,r,n)}}),n.calendars.hebrew=a},{"../main":509,"object-assign":397}],500:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}a.prototype=new n.baseCalendar,i(a.prototype,{name:"Islamic",jdEpoch:1948439.5,daysPerMonth:[30,29,30,29,30,29,30,29,30,29,30,29],hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,regionalOptions:{"":{name:"Islamic",epochs:["BH","AH"],monthNames:["Muharram","Safar","Rabi' al-awwal","Rabi' al-thani","Jumada al-awwal","Jumada al-thani","Rajab","Sha'aban","Ramadan","Shawwal","Dhu al-Qi'dah","Dhu al-Hijjah"],monthNamesShort:["Muh","Saf","Rab1","Rab2","Jum1","Jum2","Raj","Sha'","Ram","Shaw","DhuQ","DhuH"],dayNames:["Yawm al-ahad","Yawm al-ithnayn","Yawm ath-thulaathaa'","Yawm al-arbi'aa'","Yawm al-kham\u012bs","Yawm al-jum'a","Yawm as-sabt"],dayNamesShort:["Aha","Ith","Thu","Arb","Kha","Jum","Sab"],dayNamesMin:["Ah","It","Th","Ar","Kh","Ju","Sa"],digits:null,dateFormat:"yyyy/mm/dd",firstDay:6,isRTL:!1}},leapYear:function(t){return(11*this._validate(t,this.minMonth,this.minDay,n.local.invalidYear).year()+14)%30<11},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(-n.dayOfWeek(),"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInYear:function(t){return this.leapYear(t)?355:354},daysInMonth:function(t,e){var r=this._validate(t,e,this.minDay,n.local.invalidMonth);return this.daysPerMonth[r.month()-1]+(12===r.month()&&this.leapYear(r.year())?1:0)},weekDay:function(t,e,r){return 5!==this.dayOfWeek(t,e,r)},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);return t=i.year(),e=i.month(),t=t<=0?t+1:t,(r=i.day())+Math.ceil(29.5*(e-1))+354*(t-1)+Math.floor((3+11*t)/30)+this.jdEpoch-1},fromJD:function(t){t=Math.floor(t)+.5;var e=Math.floor((30*(t-this.jdEpoch)+10646)/10631);e=e<=0?e-1:e;var r=Math.min(12,Math.ceil((t-29-this.toJD(e,1,1))/29.5)+1),n=t-this.toJD(e,r,1)+1;return this.newDate(e,r,n)}}),n.calendars.islamic=a},{"../main":509,"object-assign":397}],501:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}a.prototype=new n.baseCalendar,i(a.prototype,{name:"Julian",jdEpoch:1721423.5,daysPerMonth:[31,28,31,30,31,30,31,31,30,31,30,31],hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,regionalOptions:{"":{name:"Julian",epochs:["BC","AD"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],digits:null,dateFormat:"mm/dd/yyyy",firstDay:0,isRTL:!1}},leapYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);return(t=e.year()<0?e.year()+1:e.year())%4==0},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(4-(n.dayOfWeek()||7),"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInMonth:function(t,e){var r=this._validate(t,e,this.minDay,n.local.invalidMonth);return this.daysPerMonth[r.month()-1]+(2===r.month()&&this.leapYear(r.year())?1:0)},weekDay:function(t,e,r){return(this.dayOfWeek(t,e,r)||7)<6},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);return t=i.year(),e=i.month(),r=i.day(),t<0&&t++,e<=2&&(t--,e+=12),Math.floor(365.25*(t+4716))+Math.floor(30.6001*(e+1))+r-1524.5},fromJD:function(t){var e=Math.floor(t+.5)+1524,r=Math.floor((e-122.1)/365.25),n=Math.floor(365.25*r),i=Math.floor((e-n)/30.6001),a=i-Math.floor(i<14?1:13),o=r-Math.floor(a>2?4716:4715),s=e-n-Math.floor(30.6001*i);return o<=0&&o--,this.newDate(o,a,s)}}),n.calendars.julian=a},{"../main":509,"object-assign":397}],502:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}function o(t,e){return t-e*Math.floor(t/e)}function s(t,e){return o(t-1,e)+1}a.prototype=new n.baseCalendar,i(a.prototype,{name:"Mayan",jdEpoch:584282.5,hasYearZero:!0,minMonth:0,firstMonth:0,minDay:0,regionalOptions:{"":{name:"Mayan",epochs:["",""],monthNames:["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17"],monthNamesShort:["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17"],dayNames:["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19"],dayNamesShort:["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19"],dayNamesMin:["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19"],digits:null,dateFormat:"YYYY.m.d",firstDay:0,isRTL:!1,haabMonths:["Pop","Uo","Zip","Zotz","Tzec","Xul","Yaxkin","Mol","Chen","Yax","Zac","Ceh","Mac","Kankin","Muan","Pax","Kayab","Cumku","Uayeb"],tzolkinMonths:["Imix","Ik","Akbal","Kan","Chicchan","Cimi","Manik","Lamat","Muluc","Oc","Chuen","Eb","Ben","Ix","Men","Cib","Caban","Etznab","Cauac","Ahau"]}},leapYear:function(t){return this._validate(t,this.minMonth,this.minDay,n.local.invalidYear),!1},formatYear:function(t){t=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear).year();var e=Math.floor(t/400);return t%=400,t+=t<0?400:0,e+"."+Math.floor(t/20)+"."+t%20},forYear:function(t){if((t=t.split(".")).length<3)throw"Invalid Mayan year";for(var e=0,r=0;r19||r>0&&n<0)throw"Invalid Mayan year";e=20*e+n}return e},monthsInYear:function(t){return this._validate(t,this.minMonth,this.minDay,n.local.invalidYear),18},weekOfYear:function(t,e,r){return this._validate(t,e,r,n.local.invalidDate),0},daysInYear:function(t){return this._validate(t,this.minMonth,this.minDay,n.local.invalidYear),360},daysInMonth:function(t,e){return this._validate(t,e,this.minDay,n.local.invalidMonth),20},daysInWeek:function(){return 5},dayOfWeek:function(t,e,r){return this._validate(t,e,r,n.local.invalidDate).day()},weekDay:function(t,e,r){return this._validate(t,e,r,n.local.invalidDate),!0},extraInfo:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate).toJD(),a=this._toHaab(i),o=this._toTzolkin(i);return{haabMonthName:this.local.haabMonths[a[0]-1],haabMonth:a[0],haabDay:a[1],tzolkinDayName:this.local.tzolkinMonths[o[0]-1],tzolkinDay:o[0],tzolkinTrecena:o[1]}},_toHaab:function(t){var e=o((t-=this.jdEpoch)+8+340,365);return[Math.floor(e/20)+1,o(e,20)]},_toTzolkin:function(t){return[s((t-=this.jdEpoch)+20,20),s(t+4,13)]},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);return i.day()+20*i.month()+360*i.year()+this.jdEpoch},fromJD:function(t){t=Math.floor(t)+.5-this.jdEpoch;var e=Math.floor(t/360);t%=360,t+=t<0?360:0;var r=Math.floor(t/20),n=t%20;return this.newDate(e,r,n)}}),n.calendars.mayan=a},{"../main":509,"object-assign":397}],503:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}a.prototype=new n.baseCalendar;var o=n.instance("gregorian");i(a.prototype,{name:"Nanakshahi",jdEpoch:2257673.5,daysPerMonth:[31,31,31,31,31,30,30,30,30,30,30,30],hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,regionalOptions:{"":{name:"Nanakshahi",epochs:["BN","AN"],monthNames:["Chet","Vaisakh","Jeth","Harh","Sawan","Bhadon","Assu","Katak","Maghar","Poh","Magh","Phagun"],monthNamesShort:["Che","Vai","Jet","Har","Saw","Bha","Ass","Kat","Mgr","Poh","Mgh","Pha"],dayNames:["Somvaar","Mangalvar","Budhvaar","Veervaar","Shukarvaar","Sanicharvaar","Etvaar"],dayNamesShort:["Som","Mangal","Budh","Veer","Shukar","Sanichar","Et"],dayNamesMin:["So","Ma","Bu","Ve","Sh","Sa","Et"],digits:null,dateFormat:"dd-mm-yyyy",firstDay:0,isRTL:!1}},leapYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear||n.regionalOptions[""].invalidYear);return o.leapYear(e.year()+(e.year()<1?1:0)+1469)},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(1-(n.dayOfWeek()||7),"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInMonth:function(t,e){var r=this._validate(t,e,this.minDay,n.local.invalidMonth);return this.daysPerMonth[r.month()-1]+(12===r.month()&&this.leapYear(r.year())?1:0)},weekDay:function(t,e,r){return(this.dayOfWeek(t,e,r)||7)<6},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidMonth);(t=i.year())<0&&t++;for(var a=i.day(),s=1;s=this.toJD(e+1,1,1);)e++;for(var r=t-Math.floor(this.toJD(e,1,1)+.5)+1,n=1;r>this.daysInMonth(e,n);)r-=this.daysInMonth(e,n),n++;return this.newDate(e,n,r)}}),n.calendars.nanakshahi=a},{"../main":509,"object-assign":397}],504:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}a.prototype=new n.baseCalendar,i(a.prototype,{name:"Nepali",jdEpoch:1700709.5,daysPerMonth:[31,31,32,32,31,30,30,29,30,29,30,30],hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,daysPerYear:365,regionalOptions:{"":{name:"Nepali",epochs:["BBS","ABS"],monthNames:["Baisakh","Jestha","Ashadh","Shrawan","Bhadra","Ashwin","Kartik","Mangsir","Paush","Mangh","Falgun","Chaitra"],monthNamesShort:["Bai","Je","As","Shra","Bha","Ash","Kar","Mang","Pau","Ma","Fal","Chai"],dayNames:["Aaitabaar","Sombaar","Manglbaar","Budhabaar","Bihibaar","Shukrabaar","Shanibaar"],dayNamesShort:["Aaita","Som","Mangl","Budha","Bihi","Shukra","Shani"],dayNamesMin:["Aai","So","Man","Bu","Bi","Shu","Sha"],digits:null,dateFormat:"dd/mm/yyyy",firstDay:1,isRTL:!1}},leapYear:function(t){return this.daysInYear(t)!==this.daysPerYear},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(-n.dayOfWeek(),"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInYear:function(t){if(t=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear).year(),void 0===this.NEPALI_CALENDAR_DATA[t])return this.daysPerYear;for(var e=0,r=this.minMonth;r<=12;r++)e+=this.NEPALI_CALENDAR_DATA[t][r];return e},daysInMonth:function(t,e){return t.year&&(e=t.month(),t=t.year()),this._validate(t,e,this.minDay,n.local.invalidMonth),void 0===this.NEPALI_CALENDAR_DATA[t]?this.daysPerMonth[e-1]:this.NEPALI_CALENDAR_DATA[t][e]},weekDay:function(t,e,r){return 6!==this.dayOfWeek(t,e,r)},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);t=i.year(),e=i.month(),r=i.day();var a=n.instance(),o=0,s=e,l=t;this._createMissingCalendarData(t);var c=t-(s>9||9===s&&r>=this.NEPALI_CALENDAR_DATA[l][0]?56:57);for(9!==e&&(o=r,s--);9!==s;)s<=0&&(s=12,l--),o+=this.NEPALI_CALENDAR_DATA[l][s],s--;return 9===e?(o+=r-this.NEPALI_CALENDAR_DATA[l][0])<0&&(o+=a.daysInYear(c)):o+=this.NEPALI_CALENDAR_DATA[l][9]-this.NEPALI_CALENDAR_DATA[l][0],a.newDate(c,1,1).add(o,"d").toJD()},fromJD:function(t){var e=n.instance().fromJD(t),r=e.year(),i=e.dayOfYear(),a=r+56;this._createMissingCalendarData(a);for(var o=9,s=this.NEPALI_CALENDAR_DATA[a][0],l=this.NEPALI_CALENDAR_DATA[a][o]-s+1;i>l;)++o>12&&(o=1,a++),l+=this.NEPALI_CALENDAR_DATA[a][o];var c=this.NEPALI_CALENDAR_DATA[a][o]-(l-i);return this.newDate(a,o,c)},_createMissingCalendarData:function(t){var e=this.daysPerMonth.slice(0);e.unshift(17);for(var r=t-1;r0?474:473))%2820+474+38)%2816<682},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(-(n.dayOfWeek()+1)%7,"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInMonth:function(t,e){var r=this._validate(t,e,this.minDay,n.local.invalidMonth);return this.daysPerMonth[r.month()-1]+(12===r.month()&&this.leapYear(r.year())?1:0)},weekDay:function(t,e,r){return 5!==this.dayOfWeek(t,e,r)},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);t=i.year(),e=i.month(),r=i.day();var a=t-(t>=0?474:473),s=474+o(a,2820);return r+(e<=7?31*(e-1):30*(e-1)+6)+Math.floor((682*s-110)/2816)+365*(s-1)+1029983*Math.floor(a/2820)+this.jdEpoch-1},fromJD:function(t){var e=(t=Math.floor(t)+.5)-this.toJD(475,1,1),r=Math.floor(e/1029983),n=o(e,1029983),i=2820;if(1029982!==n){var a=Math.floor(n/366),s=o(n,366);i=Math.floor((2134*a+2816*s+2815)/1028522)+a+1}var l=i+2820*r+474;l=l<=0?l-1:l;var c=t-this.toJD(l,1,1)+1,u=c<=186?Math.ceil(c/31):Math.ceil((c-6)/30),f=t-this.toJD(l,u,1)+1;return this.newDate(l,u,f)}}),n.calendars.persian=a,n.calendars.jalali=a},{"../main":509,"object-assign":397}],506:[function(t,e,r){var n=t("../main"),i=t("object-assign"),a=n.instance();function o(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}o.prototype=new n.baseCalendar,i(o.prototype,{name:"Taiwan",jdEpoch:2419402.5,yearsOffset:1911,daysPerMonth:[31,28,31,30,31,30,31,31,30,31,30,31],hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,regionalOptions:{"":{name:"Taiwan",epochs:["BROC","ROC"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],digits:null,dateFormat:"yyyy/mm/dd",firstDay:1,isRTL:!1}},leapYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);t=this._t2gYear(e.year());return a.leapYear(t)},weekOfYear:function(t,e,r){var i=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);t=this._t2gYear(i.year());return a.weekOfYear(t,i.month(),i.day())},daysInMonth:function(t,e){var r=this._validate(t,e,this.minDay,n.local.invalidMonth);return this.daysPerMonth[r.month()-1]+(2===r.month()&&this.leapYear(r.year())?1:0)},weekDay:function(t,e,r){return(this.dayOfWeek(t,e,r)||7)<6},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);t=this._t2gYear(i.year());return a.toJD(t,i.month(),i.day())},fromJD:function(t){var e=a.fromJD(t),r=this._g2tYear(e.year());return this.newDate(r,e.month(),e.day())},_t2gYear:function(t){return t+this.yearsOffset+(t>=-this.yearsOffset&&t<=-1?1:0)},_g2tYear:function(t){return t-this.yearsOffset-(t>=1&&t<=this.yearsOffset?1:0)}}),n.calendars.taiwan=o},{"../main":509,"object-assign":397}],507:[function(t,e,r){var n=t("../main"),i=t("object-assign"),a=n.instance();function o(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}o.prototype=new n.baseCalendar,i(o.prototype,{name:"Thai",jdEpoch:1523098.5,yearsOffset:543,daysPerMonth:[31,28,31,30,31,30,31,31,30,31,30,31],hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,regionalOptions:{"":{name:"Thai",epochs:["BBE","BE"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],digits:null,dateFormat:"dd/mm/yyyy",firstDay:0,isRTL:!1}},leapYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);t=this._t2gYear(e.year());return a.leapYear(t)},weekOfYear:function(t,e,r){var i=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);t=this._t2gYear(i.year());return a.weekOfYear(t,i.month(),i.day())},daysInMonth:function(t,e){var r=this._validate(t,e,this.minDay,n.local.invalidMonth);return this.daysPerMonth[r.month()-1]+(2===r.month()&&this.leapYear(r.year())?1:0)},weekDay:function(t,e,r){return(this.dayOfWeek(t,e,r)||7)<6},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate);t=this._t2gYear(i.year());return a.toJD(t,i.month(),i.day())},fromJD:function(t){var e=a.fromJD(t),r=this._g2tYear(e.year());return this.newDate(r,e.month(),e.day())},_t2gYear:function(t){return t-this.yearsOffset-(t>=1&&t<=this.yearsOffset?1:0)},_g2tYear:function(t){return t+this.yearsOffset+(t>=-this.yearsOffset&&t<=-1?1:0)}}),n.calendars.thai=o},{"../main":509,"object-assign":397}],508:[function(t,e,r){var n=t("../main"),i=t("object-assign");function a(t){this.local=this.regionalOptions[t||""]||this.regionalOptions[""]}a.prototype=new n.baseCalendar,i(a.prototype,{name:"UmmAlQura",hasYearZero:!1,minMonth:1,firstMonth:1,minDay:1,regionalOptions:{"":{name:"Umm al-Qura",epochs:["BH","AH"],monthNames:["Al-Muharram","Safar","Rabi' al-awwal","Rabi' Al-Thani","Jumada Al-Awwal","Jumada Al-Thani","Rajab","Sha'aban","Ramadan","Shawwal","Dhu al-Qi'dah","Dhu al-Hijjah"],monthNamesShort:["Muh","Saf","Rab1","Rab2","Jum1","Jum2","Raj","Sha'","Ram","Shaw","DhuQ","DhuH"],dayNames:["Yawm al-Ahad","Yawm al-Ithnain","Yawm al-Thal\u0101th\u0101\u2019","Yawm al-Arba\u2018\u0101\u2019","Yawm al-Kham\u012bs","Yawm al-Jum\u2018a","Yawm al-Sabt"],dayNamesMin:["Ah","Ith","Th","Ar","Kh","Ju","Sa"],digits:null,dateFormat:"yyyy/mm/dd",firstDay:6,isRTL:!0}},leapYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,n.local.invalidYear);return 355===this.daysInYear(e.year())},weekOfYear:function(t,e,r){var n=this.newDate(t,e,r);return n.add(-n.dayOfWeek(),"d"),Math.floor((n.dayOfYear()-1)/7)+1},daysInYear:function(t){for(var e=0,r=1;r<=12;r++)e+=this.daysInMonth(t,r);return e},daysInMonth:function(t,e){for(var r=this._validate(t,e,this.minDay,n.local.invalidMonth).toJD()-24e5+.5,i=0,a=0;ar)return o[i]-o[i-1];i++}return 30},weekDay:function(t,e,r){return 5!==this.dayOfWeek(t,e,r)},toJD:function(t,e,r){var i=this._validate(t,e,r,n.local.invalidDate),a=12*(i.year()-1)+i.month()-15292;return i.day()+o[a-1]-1+24e5-.5},fromJD:function(t){for(var e=t-24e5+.5,r=0,n=0;ne);n++)r++;var i=r+15292,a=Math.floor((i-1)/12),s=a+1,l=i-12*a,c=e-o[r-1]+1;return this.newDate(s,l,c)},isValid:function(t,e,r){var i=n.baseCalendar.prototype.isValid.apply(this,arguments);return i&&(i=(t=null!=t.year?t.year:t)>=1276&&t<=1500),i},_validate:function(t,e,r,i){var a=n.baseCalendar.prototype._validate.apply(this,arguments);if(a.year<1276||a.year>1500)throw i.replace(/\{0\}/,this.local.name);return a}}),n.calendars.ummalqura=a;var o=[20,50,79,109,138,168,197,227,256,286,315,345,374,404,433,463,492,522,551,581,611,641,670,700,729,759,788,818,847,877,906,936,965,995,1024,1054,1083,1113,1142,1172,1201,1231,1260,1290,1320,1350,1379,1409,1438,1468,1497,1527,1556,1586,1615,1645,1674,1704,1733,1763,1792,1822,1851,1881,1910,1940,1969,1999,2028,2058,2087,2117,2146,2176,2205,2235,2264,2294,2323,2353,2383,2413,2442,2472,2501,2531,2560,2590,2619,2649,2678,2708,2737,2767,2796,2826,2855,2885,2914,2944,2973,3003,3032,3062,3091,3121,3150,3180,3209,3239,3268,3298,3327,3357,3386,3416,3446,3476,3505,3535,3564,3594,3623,3653,3682,3712,3741,3771,3800,3830,3859,3889,3918,3948,3977,4007,4036,4066,4095,4125,4155,4185,4214,4244,4273,4303,4332,4362,4391,4421,4450,4480,4509,4539,4568,4598,4627,4657,4686,4716,4745,4775,4804,4834,4863,4893,4922,4952,4981,5011,5040,5070,5099,5129,5158,5188,5218,5248,5277,5307,5336,5366,5395,5425,5454,5484,5513,5543,5572,5602,5631,5661,5690,5720,5749,5779,5808,5838,5867,5897,5926,5956,5985,6015,6044,6074,6103,6133,6162,6192,6221,6251,6281,6311,6340,6370,6399,6429,6458,6488,6517,6547,6576,6606,6635,6665,6694,6724,6753,6783,6812,6842,6871,6901,6930,6960,6989,7019,7048,7078,7107,7137,7166,7196,7225,7255,7284,7314,7344,7374,7403,7433,7462,7492,7521,7551,7580,7610,7639,7669,7698,7728,7757,7787,7816,7846,7875,7905,7934,7964,7993,8023,8053,8083,8112,8142,8171,8201,8230,8260,8289,8319,8348,8378,8407,8437,8466,8496,8525,8555,8584,8614,8643,8673,8702,8732,8761,8791,8821,8850,8880,8909,8938,8968,8997,9027,9056,9086,9115,9145,9175,9205,9234,9264,9293,9322,9352,9381,9410,9440,9470,9499,9529,9559,9589,9618,9648,9677,9706,9736,9765,9794,9824,9853,9883,9913,9943,9972,10002,10032,10061,10090,10120,10149,10178,10208,10237,10267,10297,10326,10356,10386,10415,10445,10474,10504,10533,10562,10592,10621,10651,10680,10710,10740,10770,10799,10829,10858,10888,10917,10947,10976,11005,11035,11064,11094,11124,11153,11183,11213,11242,11272,11301,11331,11360,11389,11419,11448,11478,11507,11537,11567,11596,11626,11655,11685,11715,11744,11774,11803,11832,11862,11891,11921,11950,11980,12010,12039,12069,12099,12128,12158,12187,12216,12246,12275,12304,12334,12364,12393,12423,12453,12483,12512,12542,12571,12600,12630,12659,12688,12718,12747,12777,12807,12837,12866,12896,12926,12955,12984,13014,13043,13072,13102,13131,13161,13191,13220,13250,13280,13310,13339,13368,13398,13427,13456,13486,13515,13545,13574,13604,13634,13664,13693,13723,13752,13782,13811,13840,13870,13899,13929,13958,13988,14018,14047,14077,14107,14136,14166,14195,14224,14254,14283,14313,14342,14372,14401,14431,14461,14490,14520,14550,14579,14609,14638,14667,14697,14726,14756,14785,14815,14844,14874,14904,14933,14963,14993,15021,15051,15081,15110,15140,15169,15199,15228,15258,15287,15317,15347,15377,15406,15436,15465,15494,15524,15553,15582,15612,15641,15671,15701,15731,15760,15790,15820,15849,15878,15908,15937,15966,15996,16025,16055,16085,16114,16144,16174,16204,16233,16262,16292,16321,16350,16380,16409,16439,16468,16498,16528,16558,16587,16617,16646,16676,16705,16734,16764,16793,16823,16852,16882,16912,16941,16971,17001,17030,17060,17089,17118,17148,17177,17207,17236,17266,17295,17325,17355,17384,17414,17444,17473,17502,17532,17561,17591,17620,17650,17679,17709,17738,17768,17798,17827,17857,17886,17916,17945,17975,18004,18034,18063,18093,18122,18152,18181,18211,18241,18270,18300,18330,18359,18388,18418,18447,18476,18506,18535,18565,18595,18625,18654,18684,18714,18743,18772,18802,18831,18860,18890,18919,18949,18979,19008,19038,19068,19098,19127,19156,19186,19215,19244,19274,19303,19333,19362,19392,19422,19452,19481,19511,19540,19570,19599,19628,19658,19687,19717,19746,19776,19806,19836,19865,19895,19924,19954,19983,20012,20042,20071,20101,20130,20160,20190,20219,20249,20279,20308,20338,20367,20396,20426,20455,20485,20514,20544,20573,20603,20633,20662,20692,20721,20751,20780,20810,20839,20869,20898,20928,20957,20987,21016,21046,21076,21105,21135,21164,21194,21223,21253,21282,21312,21341,21371,21400,21430,21459,21489,21519,21548,21578,21607,21637,21666,21696,21725,21754,21784,21813,21843,21873,21902,21932,21962,21991,22021,22050,22080,22109,22138,22168,22197,22227,22256,22286,22316,22346,22375,22405,22434,22464,22493,22522,22552,22581,22611,22640,22670,22700,22730,22759,22789,22818,22848,22877,22906,22936,22965,22994,23024,23054,23083,23113,23143,23173,23202,23232,23261,23290,23320,23349,23379,23408,23438,23467,23497,23527,23556,23586,23616,23645,23674,23704,23733,23763,23792,23822,23851,23881,23910,23940,23970,23999,24029,24058,24088,24117,24147,24176,24206,24235,24265,24294,24324,24353,24383,24413,24442,24472,24501,24531,24560,24590,24619,24648,24678,24707,24737,24767,24796,24826,24856,24885,24915,24944,24974,25003,25032,25062,25091,25121,25150,25180,25210,25240,25269,25299,25328,25358,25387,25416,25446,25475,25505,25534,25564,25594,25624,25653,25683,25712,25742,25771,25800,25830,25859,25888,25918,25948,25977,26007,26037,26067,26096,26126,26155,26184,26214,26243,26272,26302,26332,26361,26391,26421,26451,26480,26510,26539,26568,26598,26627,26656,26686,26715,26745,26775,26805,26834,26864,26893,26923,26952,26982,27011,27041,27070,27099,27129,27159,27188,27218,27248,27277,27307,27336,27366,27395,27425,27454,27484,27513,27542,27572,27602,27631,27661,27691,27720,27750,27779,27809,27838,27868,27897,27926,27956,27985,28015,28045,28074,28104,28134,28163,28193,28222,28252,28281,28310,28340,28369,28399,28428,28458,28488,28517,28547,28577,28607,28636,28665,28695,28724,28754,28783,28813,28843,28872,28901,28931,28960,28990,29019,29049,29078,29108,29137,29167,29196,29226,29255,29285,29315,29345,29375,29404,29434,29463,29492,29522,29551,29580,29610,29640,29669,29699,29729,29759,29788,29818,29847,29876,29906,29935,29964,29994,30023,30053,30082,30112,30141,30171,30200,30230,30259,30289,30318,30348,30378,30408,30437,30467,30496,30526,30555,30585,30614,30644,30673,30703,30732,30762,30791,30821,30850,30880,30909,30939,30968,30998,31027,31057,31086,31116,31145,31175,31204,31234,31263,31293,31322,31352,31381,31411,31441,31471,31500,31530,31559,31589,31618,31648,31676,31706,31736,31766,31795,31825,31854,31884,31913,31943,31972,32002,32031,32061,32090,32120,32150,32180,32209,32239,32268,32298,32327,32357,32386,32416,32445,32475,32504,32534,32563,32593,32622,32652,32681,32711,32740,32770,32799,32829,32858,32888,32917,32947,32976,33006,33035,33065,33094,33124,33153,33183,33213,33243,33272,33302,33331,33361,33390,33420,33450,33479,33509,33539,33568,33598,33627,33657,33686,33716,33745,33775,33804,33834,33863,33893,33922,33952,33981,34011,34040,34069,34099,34128,34158,34187,34217,34247,34277,34306,34336,34365,34395,34424,34454,34483,34512,34542,34571,34601,34631,34660,34690,34719,34749,34778,34808,34837,34867,34896,34926,34955,34985,35015,35044,35074,35103,35133,35162,35192,35222,35251,35280,35310,35340,35370,35399,35429,35458,35488,35517,35547,35576,35605,35635,35665,35694,35723,35753,35782,35811,35841,35871,35901,35930,35960,35989,36019,36048,36078,36107,36136,36166,36195,36225,36254,36284,36314,36343,36373,36403,36433,36462,36492,36521,36551,36580,36610,36639,36669,36698,36728,36757,36786,36816,36845,36875,36904,36934,36963,36993,37022,37052,37081,37111,37141,37170,37200,37229,37259,37288,37318,37347,37377,37406,37436,37465,37495,37524,37554,37584,37613,37643,37672,37701,37731,37760,37790,37819,37849,37878,37908,37938,37967,37997,38027,38056,38085,38115,38144,38174,38203,38233,38262,38292,38322,38351,38381,38410,38440,38469,38499,38528,38558,38587,38617,38646,38676,38705,38735,38764,38794,38823,38853,38882,38912,38941,38971,39001,39030,39059,39089,39118,39148,39178,39208,39237,39267,39297,39326,39355,39385,39414,39444,39473,39503,39532,39562,39592,39621,39650,39680,39709,39739,39768,39798,39827,39857,39886,39916,39946,39975,40005,40035,40064,40094,40123,40153,40182,40212,40241,40271,40300,40330,40359,40389,40418,40448,40477,40507,40536,40566,40595,40625,40655,40685,40714,40744,40773,40803,40832,40862,40892,40921,40951,40980,41009,41039,41068,41098,41127,41157,41186,41216,41245,41275,41304,41334,41364,41393,41422,41452,41481,41511,41540,41570,41599,41629,41658,41688,41718,41748,41777,41807,41836,41865,41894,41924,41953,41983,42012,42042,42072,42102,42131,42161,42190,42220,42249,42279,42308,42337,42367,42397,42426,42456,42485,42515,42545,42574,42604,42633,42662,42692,42721,42751,42780,42810,42839,42869,42899,42929,42958,42988,43017,43046,43076,43105,43135,43164,43194,43223,43253,43283,43312,43342,43371,43401,43430,43460,43489,43519,43548,43578,43607,43637,43666,43696,43726,43755,43785,43814,43844,43873,43903,43932,43962,43991,44021,44050,44080,44109,44139,44169,44198,44228,44258,44287,44317,44346,44375,44405,44434,44464,44493,44523,44553,44582,44612,44641,44671,44700,44730,44759,44788,44818,44847,44877,44906,44936,44966,44996,45025,45055,45084,45114,45143,45172,45202,45231,45261,45290,45320,45350,45380,45409,45439,45468,45498,45527,45556,45586,45615,45644,45674,45704,45733,45763,45793,45823,45852,45882,45911,45940,45970,45999,46028,46058,46088,46117,46147,46177,46206,46236,46265,46295,46324,46354,46383,46413,46442,46472,46501,46531,46560,46590,46620,46649,46679,46708,46738,46767,46797,46826,46856,46885,46915,46944,46974,47003,47033,47063,47092,47122,47151,47181,47210,47240,47269,47298,47328,47357,47387,47417,47446,47476,47506,47535,47565,47594,47624,47653,47682,47712,47741,47771,47800,47830,47860,47890,47919,47949,47978,48008,48037,48066,48096,48125,48155,48184,48214,48244,48273,48303,48333,48362,48392,48421,48450,48480,48509,48538,48568,48598,48627,48657,48687,48717,48746,48776,48805,48834,48864,48893,48922,48952,48982,49011,49041,49071,49100,49130,49160,49189,49218,49248,49277,49306,49336,49365,49395,49425,49455,49484,49514,49543,49573,49602,49632,49661,49690,49720,49749,49779,49809,49838,49868,49898,49927,49957,49986,50016,50045,50075,50104,50133,50163,50192,50222,50252,50281,50311,50340,50370,50400,50429,50459,50488,50518,50547,50576,50606,50635,50665,50694,50724,50754,50784,50813,50843,50872,50902,50931,50960,50990,51019,51049,51078,51108,51138,51167,51197,51227,51256,51286,51315,51345,51374,51403,51433,51462,51492,51522,51552,51582,51611,51641,51670,51699,51729,51758,51787,51816,51846,51876,51906,51936,51965,51995,52025,52054,52083,52113,52142,52171,52200,52230,52260,52290,52319,52349,52379,52408,52438,52467,52497,52526,52555,52585,52614,52644,52673,52703,52733,52762,52792,52822,52851,52881,52910,52939,52969,52998,53028,53057,53087,53116,53146,53176,53205,53235,53264,53294,53324,53353,53383,53412,53441,53471,53500,53530,53559,53589,53619,53648,53678,53708,53737,53767,53796,53825,53855,53884,53913,53943,53973,54003,54032,54062,54092,54121,54151,54180,54209,54239,54268,54297,54327,54357,54387,54416,54446,54476,54505,54535,54564,54593,54623,54652,54681,54711,54741,54770,54800,54830,54859,54889,54919,54948,54977,55007,55036,55066,55095,55125,55154,55184,55213,55243,55273,55302,55332,55361,55391,55420,55450,55479,55508,55538,55567,55597,55627,55657,55686,55716,55745,55775,55804,55834,55863,55892,55922,55951,55981,56011,56040,56070,56100,56129,56159,56188,56218,56247,56276,56306,56335,56365,56394,56424,56454,56483,56513,56543,56572,56601,56631,56660,56690,56719,56749,56778,56808,56837,56867,56897,56926,56956,56985,57015,57044,57074,57103,57133,57162,57192,57221,57251,57280,57310,57340,57369,57399,57429,57458,57487,57517,57546,57576,57605,57634,57664,57694,57723,57753,57783,57813,57842,57871,57901,57930,57959,57989,58018,58048,58077,58107,58137,58167,58196,58226,58255,58285,58314,58343,58373,58402,58432,58461,58491,58521,58551,58580,58610,58639,58669,58698,58727,58757,58786,58816,58845,58875,58905,58934,58964,58994,59023,59053,59082,59111,59141,59170,59200,59229,59259,59288,59318,59348,59377,59407,59436,59466,59495,59525,59554,59584,59613,59643,59672,59702,59731,59761,59791,59820,59850,59879,59909,59939,59968,59997,60027,60056,60086,60115,60145,60174,60204,60234,60264,60293,60323,60352,60381,60411,60440,60469,60499,60528,60558,60588,60618,60648,60677,60707,60736,60765,60795,60824,60853,60883,60912,60942,60972,61002,61031,61061,61090,61120,61149,61179,61208,61237,61267,61296,61326,61356,61385,61415,61445,61474,61504,61533,61563,61592,61621,61651,61680,61710,61739,61769,61799,61828,61858,61888,61917,61947,61976,62006,62035,62064,62094,62123,62153,62182,62212,62242,62271,62301,62331,62360,62390,62419,62448,62478,62507,62537,62566,62596,62625,62655,62685,62715,62744,62774,62803,62832,62862,62891,62921,62950,62980,63009,63039,63069,63099,63128,63157,63187,63216,63246,63275,63305,63334,63363,63393,63423,63453,63482,63512,63541,63571,63600,63630,63659,63689,63718,63747,63777,63807,63836,63866,63895,63925,63955,63984,64014,64043,64073,64102,64131,64161,64190,64220,64249,64279,64309,64339,64368,64398,64427,64457,64486,64515,64545,64574,64603,64633,64663,64692,64722,64752,64782,64811,64841,64870,64899,64929,64958,64987,65017,65047,65076,65106,65136,65166,65195,65225,65254,65283,65313,65342,65371,65401,65431,65460,65490,65520,65549,65579,65608,65638,65667,65697,65726,65755,65785,65815,65844,65874,65903,65933,65963,65992,66022,66051,66081,66110,66140,66169,66199,66228,66258,66287,66317,66346,66376,66405,66435,66465,66494,66524,66553,66583,66612,66641,66671,66700,66730,66760,66789,66819,66849,66878,66908,66937,66967,66996,67025,67055,67084,67114,67143,67173,67203,67233,67262,67292,67321,67351,67380,67409,67439,67468,67497,67527,67557,67587,67617,67646,67676,67705,67735,67764,67793,67823,67852,67882,67911,67941,67971,68e3,68030,68060,68089,68119,68148,68177,68207,68236,68266,68295,68325,68354,68384,68414,68443,68473,68502,68532,68561,68591,68620,68650,68679,68708,68738,68768,68797,68827,68857,68886,68916,68946,68975,69004,69034,69063,69092,69122,69152,69181,69211,69240,69270,69300,69330,69359,69388,69418,69447,69476,69506,69535,69565,69595,69624,69654,69684,69713,69743,69772,69802,69831,69861,69890,69919,69949,69978,70008,70038,70067,70097,70126,70156,70186,70215,70245,70274,70303,70333,70362,70392,70421,70451,70481,70510,70540,70570,70599,70629,70658,70687,70717,70746,70776,70805,70835,70864,70894,70924,70954,70983,71013,71042,71071,71101,71130,71159,71189,71218,71248,71278,71308,71337,71367,71397,71426,71455,71485,71514,71543,71573,71602,71632,71662,71691,71721,71751,71781,71810,71839,71869,71898,71927,71957,71986,72016,72046,72075,72105,72135,72164,72194,72223,72253,72282,72311,72341,72370,72400,72429,72459,72489,72518,72548,72577,72607,72637,72666,72695,72725,72754,72784,72813,72843,72872,72902,72931,72961,72991,73020,73050,73080,73109,73139,73168,73197,73227,73256,73286,73315,73345,73375,73404,73434,73464,73493,73523,73552,73581,73611,73640,73669,73699,73729,73758,73788,73818,73848,73877,73907,73936,73965,73995,74024,74053,74083,74113,74142,74172,74202,74231,74261,74291,74320,74349,74379,74408,74437,74467,74497,74526,74556,74586,74615,74645,74675,74704,74733,74763,74792,74822,74851,74881,74910,74940,74969,74999,75029,75058,75088,75117,75147,75176,75206,75235,75264,75294,75323,75353,75383,75412,75442,75472,75501,75531,75560,75590,75619,75648,75678,75707,75737,75766,75796,75826,75856,75885,75915,75944,75974,76003,76032,76062,76091,76121,76150,76180,76210,76239,76269,76299,76328,76358,76387,76416,76446,76475,76505,76534,76564,76593,76623,76653,76682,76712,76741,76771,76801,76830,76859,76889,76918,76948,76977,77007,77036,77066,77096,77125,77155,77185,77214,77243,77273,77302,77332,77361,77390,77420,77450,77479,77509,77539,77569,77598,77627,77657,77686,77715,77745,77774,77804,77833,77863,77893,77923,77952,77982,78011,78041,78070,78099,78129,78158,78188,78217,78247,78277,78307,78336,78366,78395,78425,78454,78483,78513,78542,78572,78601,78631,78661,78690,78720,78750,78779,78808,78838,78867,78897,78926,78956,78985,79015,79044,79074,79104,79133,79163,79192,79222,79251,79281,79310,79340,79369,79399,79428,79458,79487,79517,79546,79576,79606,79635,79665,79695,79724,79753,79783,79812,79841,79871,79900,79930,79960,79990]},{"../main":509,"object-assign":397}],509:[function(t,e,r){var n=t("object-assign");function i(){this.regionalOptions=[],this.regionalOptions[""]={invalidCalendar:"Calendar {0} not found",invalidDate:"Invalid {0} date",invalidMonth:"Invalid {0} month",invalidYear:"Invalid {0} year",differentCalendars:"Cannot mix {0} and {1} dates"},this.local=this.regionalOptions[""],this.calendars={},this._localCals={}}function a(t,e,r,n){if(this._calendar=t,this._year=e,this._month=r,this._day=n,0===this._calendar._validateLevel&&!this._calendar.isValid(this._year,this._month,this._day))throw(c.local.invalidDate||c.regionalOptions[""].invalidDate).replace(/\{0\}/,this._calendar.local.name)}function o(t,e){return"000000".substring(0,e-(t=""+t).length)+t}function s(){this.shortYearCutoff="+10"}function l(t){this.local=this.regionalOptions[t]||this.regionalOptions[""]}n(i.prototype,{instance:function(t,e){t=(t||"gregorian").toLowerCase(),e=e||"";var r=this._localCals[t+"-"+e];if(!r&&this.calendars[t]&&(r=new this.calendars[t](e),this._localCals[t+"-"+e]=r),!r)throw(this.local.invalidCalendar||this.regionalOptions[""].invalidCalendar).replace(/\{0\}/,t);return r},newDate:function(t,e,r,n,i){return(n=(null!=t&&t.year?t.calendar():"string"==typeof n?this.instance(n,i):n)||this.instance()).newDate(t,e,r)},substituteDigits:function(t){return function(e){return(e+"").replace(/[0-9]/g,function(e){return t[e]})}},substituteChineseDigits:function(t,e){return function(r){for(var n="",i=0;r>0;){var a=r%10;n=(0===a?"":t[a]+e[i])+n,i++,r=Math.floor(r/10)}return 0===n.indexOf(t[1]+e[1])&&(n=n.substr(1)),n||t[0]}}}),n(a.prototype,{newDate:function(t,e,r){return this._calendar.newDate(null==t?this:t,e,r)},year:function(t){return 0===arguments.length?this._year:this.set(t,"y")},month:function(t){return 0===arguments.length?this._month:this.set(t,"m")},day:function(t){return 0===arguments.length?this._day:this.set(t,"d")},date:function(t,e,r){if(!this._calendar.isValid(t,e,r))throw(c.local.invalidDate||c.regionalOptions[""].invalidDate).replace(/\{0\}/,this._calendar.local.name);return this._year=t,this._month=e,this._day=r,this},leapYear:function(){return this._calendar.leapYear(this)},epoch:function(){return this._calendar.epoch(this)},formatYear:function(){return this._calendar.formatYear(this)},monthOfYear:function(){return this._calendar.monthOfYear(this)},weekOfYear:function(){return this._calendar.weekOfYear(this)},daysInYear:function(){return this._calendar.daysInYear(this)},dayOfYear:function(){return this._calendar.dayOfYear(this)},daysInMonth:function(){return this._calendar.daysInMonth(this)},dayOfWeek:function(){return this._calendar.dayOfWeek(this)},weekDay:function(){return this._calendar.weekDay(this)},extraInfo:function(){return this._calendar.extraInfo(this)},add:function(t,e){return this._calendar.add(this,t,e)},set:function(t,e){return this._calendar.set(this,t,e)},compareTo:function(t){if(this._calendar.name!==t._calendar.name)throw(c.local.differentCalendars||c.regionalOptions[""].differentCalendars).replace(/\{0\}/,this._calendar.local.name).replace(/\{1\}/,t._calendar.local.name);var e=this._year!==t._year?this._year-t._year:this._month!==t._month?this.monthOfYear()-t.monthOfYear():this._day-t._day;return 0===e?0:e<0?-1:1},calendar:function(){return this._calendar},toJD:function(){return this._calendar.toJD(this)},fromJD:function(t){return this._calendar.fromJD(t)},toJSDate:function(){return this._calendar.toJSDate(this)},fromJSDate:function(t){return this._calendar.fromJSDate(t)},toString:function(){return(this.year()<0?"-":"")+o(Math.abs(this.year()),4)+"-"+o(this.month(),2)+"-"+o(this.day(),2)}}),n(s.prototype,{_validateLevel:0,newDate:function(t,e,r){return null==t?this.today():(t.year&&(this._validate(t,e,r,c.local.invalidDate||c.regionalOptions[""].invalidDate),r=t.day(),e=t.month(),t=t.year()),new a(this,t,e,r))},today:function(){return this.fromJSDate(new Date)},epoch:function(t){return this._validate(t,this.minMonth,this.minDay,c.local.invalidYear||c.regionalOptions[""].invalidYear).year()<0?this.local.epochs[0]:this.local.epochs[1]},formatYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,c.local.invalidYear||c.regionalOptions[""].invalidYear);return(e.year()<0?"-":"")+o(Math.abs(e.year()),4)},monthsInYear:function(t){return this._validate(t,this.minMonth,this.minDay,c.local.invalidYear||c.regionalOptions[""].invalidYear),12},monthOfYear:function(t,e){var r=this._validate(t,e,this.minDay,c.local.invalidMonth||c.regionalOptions[""].invalidMonth);return(r.month()+this.monthsInYear(r)-this.firstMonth)%this.monthsInYear(r)+this.minMonth},fromMonthOfYear:function(t,e){var r=(e+this.firstMonth-2*this.minMonth)%this.monthsInYear(t)+this.minMonth;return this._validate(t,r,this.minDay,c.local.invalidMonth||c.regionalOptions[""].invalidMonth),r},daysInYear:function(t){var e=this._validate(t,this.minMonth,this.minDay,c.local.invalidYear||c.regionalOptions[""].invalidYear);return this.leapYear(e)?366:365},dayOfYear:function(t,e,r){var n=this._validate(t,e,r,c.local.invalidDate||c.regionalOptions[""].invalidDate);return n.toJD()-this.newDate(n.year(),this.fromMonthOfYear(n.year(),this.minMonth),this.minDay).toJD()+1},daysInWeek:function(){return 7},dayOfWeek:function(t,e,r){var n=this._validate(t,e,r,c.local.invalidDate||c.regionalOptions[""].invalidDate);return(Math.floor(this.toJD(n))+2)%this.daysInWeek()},extraInfo:function(t,e,r){return this._validate(t,e,r,c.local.invalidDate||c.regionalOptions[""].invalidDate),{}},add:function(t,e,r){return this._validate(t,this.minMonth,this.minDay,c.local.invalidDate||c.regionalOptions[""].invalidDate),this._correctAdd(t,this._add(t,e,r),e,r)},_add:function(t,e,r){if(this._validateLevel++,"d"===r||"w"===r){var n=t.toJD()+e*("w"===r?this.daysInWeek():1),i=t.calendar().fromJD(n);return this._validateLevel--,[i.year(),i.month(),i.day()]}try{var a=t.year()+("y"===r?e:0),o=t.monthOfYear()+("m"===r?e:0);i=t.day();"y"===r?(t.month()!==this.fromMonthOfYear(a,o)&&(o=this.newDate(a,t.month(),this.minDay).monthOfYear()),o=Math.min(o,this.monthsInYear(a)),i=Math.min(i,this.daysInMonth(a,this.fromMonthOfYear(a,o)))):"m"===r&&(!function(t){for(;oe-1+t.minMonth;)a++,o-=e,e=t.monthsInYear(a)}(this),i=Math.min(i,this.daysInMonth(a,this.fromMonthOfYear(a,o))));var s=[a,this.fromMonthOfYear(a,o),i];return this._validateLevel--,s}catch(t){throw this._validateLevel--,t}},_correctAdd:function(t,e,r,n){if(!(this.hasYearZero||"y"!==n&&"m"!==n||0!==e[0]&&t.year()>0==e[0]>0)){var i={y:[1,1,"y"],m:[1,this.monthsInYear(-1),"m"],w:[this.daysInWeek(),this.daysInYear(-1),"d"],d:[1,this.daysInYear(-1),"d"]}[n],a=r<0?-1:1;e=this._add(t,r*i[0]+a*i[1],i[2])}return t.date(e[0],e[1],e[2])},set:function(t,e,r){this._validate(t,this.minMonth,this.minDay,c.local.invalidDate||c.regionalOptions[""].invalidDate);var n="y"===r?e:t.year(),i="m"===r?e:t.month(),a="d"===r?e:t.day();return"y"!==r&&"m"!==r||(a=Math.min(a,this.daysInMonth(n,i))),t.date(n,i,a)},isValid:function(t,e,r){this._validateLevel++;var n=this.hasYearZero||0!==t;if(n){var i=this.newDate(t,e,this.minDay);n=e>=this.minMonth&&e-this.minMonth=this.minDay&&r-this.minDay13.5?13:1),c=i-(l>2.5?4716:4715);return c<=0&&c--,this.newDate(c,l,s)},toJSDate:function(t,e,r){var n=this._validate(t,e,r,c.local.invalidDate||c.regionalOptions[""].invalidDate),i=new Date(n.year(),n.month()-1,n.day());return i.setHours(0),i.setMinutes(0),i.setSeconds(0),i.setMilliseconds(0),i.setHours(i.getHours()>12?i.getHours()+2:0),i},fromJSDate:function(t){return this.newDate(t.getFullYear(),t.getMonth()+1,t.getDate())}});var c=e.exports=new i;c.cdate=a,c.baseCalendar=s,c.calendars.gregorian=l},{"object-assign":397}],510:[function(t,e,r){var n=t("object-assign"),i=t("./main");n(i.regionalOptions[""],{invalidArguments:"Invalid arguments",invalidFormat:"Cannot format a date from another calendar",missingNumberAt:"Missing number at position {0}",unknownNameAt:"Unknown name at position {0}",unexpectedLiteralAt:"Unexpected literal at position {0}",unexpectedText:"Additional text found at end"}),i.local=i.regionalOptions[""],n(i.cdate.prototype,{formatDate:function(t,e){return"string"!=typeof t&&(e=t,t=""),this._calendar.formatDate(t||"",this,e)}}),n(i.baseCalendar.prototype,{UNIX_EPOCH:i.instance().newDate(1970,1,1).toJD(),SECS_PER_DAY:86400,TICKS_EPOCH:i.instance().jdEpoch,TICKS_PER_DAY:864e9,ATOM:"yyyy-mm-dd",COOKIE:"D, dd M yyyy",FULL:"DD, MM d, yyyy",ISO_8601:"yyyy-mm-dd",JULIAN:"J",RFC_822:"D, d M yy",RFC_850:"DD, dd-M-yy",RFC_1036:"D, d M yy",RFC_1123:"D, d M yyyy",RFC_2822:"D, d M yyyy",RSS:"D, d M yy",TICKS:"!",TIMESTAMP:"@",W3C:"yyyy-mm-dd",formatDate:function(t,e,r){if("string"!=typeof t&&(r=e,e=t,t=""),!e)return"";if(e.calendar()!==this)throw i.local.invalidFormat||i.regionalOptions[""].invalidFormat;t=t||this.local.dateFormat;for(var n,a,o,s,l=(r=r||{}).dayNamesShort||this.local.dayNamesShort,c=r.dayNames||this.local.dayNames,u=r.monthNumbers||this.local.monthNumbers,f=r.monthNamesShort||this.local.monthNamesShort,h=r.monthNames||this.local.monthNames,p=(r.calculateWeek||this.local.calculateWeek,function(e,r){for(var n=1;w+n1}),d=function(t,e,r,n){var i=""+e;if(p(t,n))for(;i.length1},x=function(t,r){var n=y(t,r),a=[2,3,n?4:2,n?4:2,10,11,20]["oyYJ@!".indexOf(t)+1],o=new RegExp("^-?\\d{1,"+a+"}"),s=e.substring(A).match(o);if(!s)throw(i.local.missingNumberAt||i.regionalOptions[""].missingNumberAt).replace(/\{0\}/,A);return A+=s[0].length,parseInt(s[0],10)},b=this,_=function(){if("function"==typeof l){y("m");var t=l.call(b,e.substring(A));return A+=t.length,t}return x("m")},w=function(t,r,n,a){for(var o=y(t,a)?n:r,s=0;s-1){p=1,d=g;for(var C=this.daysInMonth(h,p);d>C;C=this.daysInMonth(h,p))p++,d-=C}return f>-1?this.fromJD(f):this.newDate(h,p,d)},determineDate:function(t,e,r,n,i){r&&"object"!=typeof r&&(i=n,n=r,r=null),"string"!=typeof n&&(i=n,n="");var a=this;return e=e?e.newDate():null,t=null==t?e:"string"==typeof t?function(t){try{return a.parseDate(n,t,i)}catch(t){}for(var e=((t=t.toLowerCase()).match(/^c/)&&r?r.newDate():null)||a.today(),o=/([+-]?[0-9]+)\s*(d|w|m|y)?/g,s=o.exec(t);s;)e.add(parseInt(s[1],10),s[2]||"d"),s=o.exec(t);return e}(t):"number"==typeof t?isNaN(t)||t===1/0||t===-1/0?e:a.today().add(t,"d"):a.newDate(t)}})},{"./main":509,"object-assign":397}],511:[function(t,e,r){e.exports=t("cwise-compiler")({args:["array",{offset:[1],array:0},"scalar","scalar","index"],pre:{body:"{}",args:[],thisVars:[],localVars:[]},post:{body:"{}",args:[],thisVars:[],localVars:[]},body:{body:"{\n var _inline_1_da = _inline_1_arg0_ - _inline_1_arg3_\n var _inline_1_db = _inline_1_arg1_ - _inline_1_arg3_\n if((_inline_1_da >= 0) !== (_inline_1_db >= 0)) {\n _inline_1_arg2_.push(_inline_1_arg4_[0] + 0.5 + 0.5 * (_inline_1_da + _inline_1_db) / (_inline_1_da - _inline_1_db))\n }\n }",args:[{name:"_inline_1_arg0_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_1_arg1_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_1_arg2_",lvalue:!1,rvalue:!0,count:1},{name:"_inline_1_arg3_",lvalue:!1,rvalue:!0,count:2},{name:"_inline_1_arg4_",lvalue:!1,rvalue:!0,count:1}],thisVars:[],localVars:["_inline_1_da","_inline_1_db"]},funcName:"zeroCrossings"})},{"cwise-compiler":117}],512:[function(t,e,r){"use strict";e.exports=function(t,e){var r=[];return e=+e||0,n(t.hi(t.shape[0]-1),r,e),r};var n=t("./lib/zc-core")},{"./lib/zc-core":511}],513:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../../plots/cartesian/axes"),a=t("./common_defaults"),o=t("./attributes");e.exports=function(t,e,r,s,l){function c(r,i){return n.coerce(t,e,o,r,i)}s=s||{};var u=c("visible",!(l=l||{}).itemIsNotPlainObject),f=c("clicktoshow");if(!u&&!f)return e;a(t,e,r,c);for(var h=e.showarrow,p=["x","y"],d=[-10,-30],g={_fullLayout:r},m=0;m<2;m++){var v=p[m],y=i.coerceRef(t,e,g,v,"","paper");if(i.coercePosition(e,g,c,y,v,.5),h){var x="a"+v,b=i.coerceRef(t,e,g,x,"pixel");"pixel"!==b&&b!==y&&(b=e[x]="pixel");var _="pixel"===b?d[m]:.4;i.coercePosition(e,g,c,b,x,_)}c(v+"anchor"),c(v+"shift")}if(n.noneOrAll(t,e,["x","y"]),h&&n.noneOrAll(t,e,["ax","ay"]),f){var w=c("xclick"),k=c("yclick");e._xclick=void 0===w?e.x:i.cleanPosition(w,g,e.xref),e._yclick=void 0===k?e.y:i.cleanPosition(k,g,e.yref)}return e}},{"../../lib":660,"../../plots/cartesian/axes":706,"./attributes":515,"./common_defaults":518}],514:[function(t,e,r){"use strict";e.exports=[{path:"",backoff:0},{path:"M-2.4,-3V3L0.6,0Z",backoff:.6},{path:"M-3.7,-2.5V2.5L1.3,0Z",backoff:1.3},{path:"M-4.45,-3L-1.65,-0.2V0.2L-4.45,3L1.55,0Z",backoff:1.55},{path:"M-2.2,-2.2L-0.2,-0.2V0.2L-2.2,2.2L-1.4,3L1.6,0L-1.4,-3Z",backoff:1.6},{path:"M-4.4,-2.1L-0.6,-0.2V0.2L-4.4,2.1L-4,3L2,0L-4,-3Z",backoff:2},{path:"M2,0A2,2 0 1,1 0,-2A2,2 0 0,1 2,0Z",backoff:0,noRotate:!0},{path:"M2,2V-2H-2V2Z",backoff:0,noRotate:!0}]},{}],515:[function(t,e,r){"use strict";var n=t("./arrow_paths"),i=t("../../plots/font_attributes"),a=t("../../plots/cartesian/constants");e.exports={_isLinkedToArray:"annotation",visible:{valType:"boolean",dflt:!0,editType:"calcIfAutorange+arraydraw"},text:{valType:"string",editType:"calcIfAutorange+arraydraw"},textangle:{valType:"angle",dflt:0,editType:"calcIfAutorange+arraydraw"},font:i({editType:"calcIfAutorange+arraydraw",colorEditType:"arraydraw"}),width:{valType:"number",min:1,dflt:null,editType:"calcIfAutorange+arraydraw"},height:{valType:"number",min:1,dflt:null,editType:"calcIfAutorange+arraydraw"},opacity:{valType:"number",min:0,max:1,dflt:1,editType:"arraydraw"},align:{valType:"enumerated",values:["left","center","right"],dflt:"center",editType:"arraydraw"},valign:{valType:"enumerated",values:["top","middle","bottom"],dflt:"middle",editType:"arraydraw"},bgcolor:{valType:"color",dflt:"rgba(0,0,0,0)",editType:"arraydraw"},bordercolor:{valType:"color",dflt:"rgba(0,0,0,0)",editType:"arraydraw"},borderpad:{valType:"number",min:0,dflt:1,editType:"calcIfAutorange+arraydraw"},borderwidth:{valType:"number",min:0,dflt:1,editType:"calcIfAutorange+arraydraw"},showarrow:{valType:"boolean",dflt:!0,editType:"calcIfAutorange+arraydraw"},arrowcolor:{valType:"color",editType:"arraydraw"},arrowhead:{valType:"integer",min:0,max:n.length,dflt:1,editType:"arraydraw"},startarrowhead:{valType:"integer",min:0,max:n.length,dflt:1,editType:"arraydraw"},arrowside:{valType:"flaglist",flags:["end","start"],extras:["none"],dflt:"end",editType:"arraydraw"},arrowsize:{valType:"number",min:.3,dflt:1,editType:"calcIfAutorange+arraydraw"},startarrowsize:{valType:"number",min:.3,dflt:1,editType:"calcIfAutorange+arraydraw"},arrowwidth:{valType:"number",min:.1,editType:"calcIfAutorange+arraydraw"},standoff:{valType:"number",min:0,dflt:0,editType:"calcIfAutorange+arraydraw"},startstandoff:{valType:"number",min:0,dflt:0,editType:"calcIfAutorange+arraydraw"},ax:{valType:"any",editType:"calcIfAutorange+arraydraw"},ay:{valType:"any",editType:"calcIfAutorange+arraydraw"},axref:{valType:"enumerated",dflt:"pixel",values:["pixel",a.idRegex.x.toString()],editType:"calc"},ayref:{valType:"enumerated",dflt:"pixel",values:["pixel",a.idRegex.y.toString()],editType:"calc"},xref:{valType:"enumerated",values:["paper",a.idRegex.x.toString()],editType:"calc"},x:{valType:"any",editType:"calcIfAutorange+arraydraw"},xanchor:{valType:"enumerated",values:["auto","left","center","right"],dflt:"auto",editType:"calcIfAutorange+arraydraw"},xshift:{valType:"number",dflt:0,editType:"calcIfAutorange+arraydraw"},yref:{valType:"enumerated",values:["paper",a.idRegex.y.toString()],editType:"calc"},y:{valType:"any",editType:"calcIfAutorange+arraydraw"},yanchor:{valType:"enumerated",values:["auto","top","middle","bottom"],dflt:"auto",editType:"calcIfAutorange+arraydraw"},yshift:{valType:"number",dflt:0,editType:"calcIfAutorange+arraydraw"},clicktoshow:{valType:"enumerated",values:[!1,"onoff","onout"],dflt:!1,editType:"arraydraw"},xclick:{valType:"any",editType:"arraydraw"},yclick:{valType:"any",editType:"arraydraw"},hovertext:{valType:"string",editType:"arraydraw"},hoverlabel:{bgcolor:{valType:"color",editType:"arraydraw"},bordercolor:{valType:"color",editType:"arraydraw"},font:i({editType:"arraydraw"}),editType:"arraydraw"},captureevents:{valType:"boolean",editType:"arraydraw"},editType:"calc",_deprecated:{ref:{valType:"string",editType:"calc"}}}},{"../../plots/cartesian/constants":711,"../../plots/font_attributes":732,"./arrow_paths":514}],516:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../../plots/cartesian/axes"),a=t("./draw").draw;function o(t){var e=t._fullLayout;n.filterVisible(e.annotations).forEach(function(e){var r,n,a,o,s=i.getFromId(t,e.xref),l=i.getFromId(t,e.yref),c=3*e.arrowsize*e.arrowwidth||0,u=3*e.startarrowsize*e.arrowwidth||0;s&&s.autorange&&(r=c+e.xshift,n=c-e.xshift,a=u+e.xshift,o=u-e.xshift,e.axref===e.xref?(i.expand(s,[s.r2c(e.x)],{ppadplus:r,ppadminus:n}),i.expand(s,[s.r2c(e.ax)],{ppadplus:Math.max(e._xpadplus,a),ppadminus:Math.max(e._xpadminus,o)})):(a=e.ax?a+e.ax:a,o=e.ax?o-e.ax:o,i.expand(s,[s.r2c(e.x)],{ppadplus:Math.max(e._xpadplus,r,a),ppadminus:Math.max(e._xpadminus,n,o)}))),l&&l.autorange&&(r=c-e.yshift,n=c+e.yshift,a=u-e.yshift,o=u+e.yshift,e.ayref===e.yref?(i.expand(l,[l.r2c(e.y)],{ppadplus:r,ppadminus:n}),i.expand(l,[l.r2c(e.ay)],{ppadplus:Math.max(e._ypadplus,a),ppadminus:Math.max(e._ypadminus,o)})):(a=e.ay?a+e.ay:a,o=e.ay?o-e.ay:o,i.expand(l,[l.r2c(e.y)],{ppadplus:Math.max(e._ypadplus,r,a),ppadminus:Math.max(e._ypadminus,n,o)})))})}e.exports=function(t){var e=t._fullLayout,r=n.filterVisible(e.annotations);if(r.length&&t._fullData.length){var s={};for(var l in r.forEach(function(t){s[t.xref]=1,s[t.yref]=1}),s){var c=i.getFromId(t,l);if(c&&c.autorange)return n.syncOrAsync([a,o],t)}}}},{"../../lib":660,"../../plots/cartesian/axes":706,"./draw":521}],517:[function(t,e,r){"use strict";var n=t("../../registry");function i(t,e){var r,n,i,o,s,l,c,u=t._fullLayout.annotations,f=[],h=[],p=[],d=(e||[]).length;for(r=0;r0||r.explicitOff.length>0},onClick:function(t,e){var r,a=i(t,e),o=a.on,s=a.off.concat(a.explicitOff),l={};if(!o.length&&!s.length)return;for(r=0;r2/3?"right":"center"),{center:0,middle:0,left:.5,bottom:-.5,right:-.5,top:.5}[e]}e._w=I,e._h=B;for(var V=!1,U=["x","y"],q=0;q1)&&(K===J?((ot=Q.r2fraction(e["a"+Z]))<0||ot>1)&&(V=!0):V=!0,V))continue;H=Q._offset+Q.r2p(e[Z]),Y=.5}else"x"===Z?(W=e[Z],H=x.l+x.w*W):(W=1-e[Z],H=x.t+x.h*W),Y=e.showarrow?.5:W;if(e.showarrow){at.head=H;var st=e["a"+Z];X=tt*j(.5,e.xanchor)-et*j(.5,e.yanchor),K===J?(at.tail=Q._offset+Q.r2p(st),G=X):(at.tail=H+st,G=X+st),at.text=at.tail+X;var lt=y["x"===Z?"width":"height"];if("paper"===J&&(at.head=o.constrain(at.head,1,lt-1)),"pixel"===K){var ct=-Math.max(at.tail-3,at.text),ut=Math.min(at.tail+3,at.text)-lt;ct>0?(at.tail+=ct,at.text+=ct):ut>0&&(at.tail-=ut,at.text-=ut)}at.tail+=it,at.head+=it}else G=X=rt*j(Y,nt),at.text=H+X;at.text+=it,X+=it,G+=it,e["_"+Z+"padplus"]=rt/2+G,e["_"+Z+"padminus"]=rt/2-G,e["_"+Z+"size"]=rt,e["_"+Z+"shift"]=X}if(V)C.remove();else{var ft=0,ht=0;if("left"!==e.align&&(ft=(I-S)*("center"===e.align?.5:1)),"top"!==e.valign&&(ht=(B-L)*("middle"===e.valign?.5:1)),u)n.select("svg").attr({x:z+ft-1,y:z+ht}).call(c.setClipUrl,D?_:null);else{var pt=z+ht-m.top,dt=z+ft-m.left;R.call(f.positionText,dt,pt).call(c.setClipUrl,D?_:null)}O.select("rect").call(c.setRect,z,z,I,B),P.call(c.setRect,E/2,E/2,F-E,N-E),C.call(c.setTranslate,Math.round(w.x.text-F/2),Math.round(w.y.text-N/2)),A.attr({transform:"rotate("+k+","+w.x.text+","+w.y.text+")"});var gt,mt,vt=function(r,n){M.selectAll(".annotation-arrow-g").remove();var u=w.x.head,f=w.y.head,h=w.x.tail+r,m=w.y.tail+n,y=w.x.text+r,_=w.y.text+n,T=o.rotationXYMatrix(k,y,_),S=o.apply2DTransform(T),E=o.apply2DTransform2(T),L=+P.attr("width"),z=+P.attr("height"),D=y-.5*L,O=D+L,I=_-.5*z,R=I+z,B=[[D,I,D,R],[D,R,O,R],[O,R,O,I],[O,I,D,I]].map(E);if(!B.reduce(function(t,e){return t^!!o.segmentsIntersect(u,f,u+1e6,f+1e6,e[0],e[1],e[2],e[3])},!1)){B.forEach(function(t){var e=o.segmentsIntersect(h,m,u,f,t[0],t[1],t[2],t[3]);e&&(h=e.x,m=e.y)});var F=e.arrowwidth,N=e.arrowcolor,j=e.arrowside,V=M.append("g").style({opacity:l.opacity(N)}).classed("annotation-arrow-g",!0),U=V.append("path").attr("d","M"+h+","+m+"L"+u+","+f).style("stroke-width",F+"px").call(l.stroke,l.rgb(N));if(d(U,j,e),b.annotationPosition&&U.node().parentNode&&!a){var q=u,H=f;if(e.standoff){var G=Math.sqrt(Math.pow(u-h,2)+Math.pow(f-m,2));q+=e.standoff*(h-u)/G,H+=e.standoff*(m-f)/G}var W,Y,X,Z=V.append("path").classed("annotation-arrow",!0).classed("anndrag",!0).classed("cursor-move",!0).attr({d:"M3,3H-3V-3H3ZM0,0L"+(h-q)+","+(m-H),transform:"translate("+q+","+H+")"}).style("stroke-width",F+6+"px").call(l.stroke,"rgba(0,0,0,0)").call(l.fill,"rgba(0,0,0,0)");p.init({element:Z.node(),gd:t,prepFn:function(){var t=c.getTranslate(C);Y=t.x,X=t.y,W={},s&&s.autorange&&(W[s._name+".autorange"]=!0),g&&g.autorange&&(W[g._name+".autorange"]=!0)},moveFn:function(t,r){var n=S(Y,X),i=n[0]+t,a=n[1]+r;C.call(c.setTranslate,i,a),W[v+".x"]=s?s.p2r(s.r2p(e.x)+t):e.x+t/x.w,W[v+".y"]=g?g.p2r(g.r2p(e.y)+r):e.y-r/x.h,e.axref===e.xref&&(W[v+".ax"]=s.p2r(s.r2p(e.ax)+t)),e.ayref===e.yref&&(W[v+".ay"]=g.p2r(g.r2p(e.ay)+r)),V.attr("transform","translate("+t+","+r+")"),A.attr({transform:"rotate("+k+","+i+","+a+")"})},doneFn:function(){i.call("relayout",t,W);var e=document.querySelector(".js-notes-box-panel");e&&e.redraw(e.selectedObj)}})}}};if(e.showarrow&&vt(0,0),T)p.init({element:C.node(),gd:t,prepFn:function(){mt=A.attr("transform"),gt={}},moveFn:function(t,r){var n="pointer";if(e.showarrow)e.axref===e.xref?gt[v+".ax"]=s.p2r(s.r2p(e.ax)+t):gt[v+".ax"]=e.ax+t,e.ayref===e.yref?gt[v+".ay"]=g.p2r(g.r2p(e.ay)+r):gt[v+".ay"]=e.ay+r,vt(t,r);else{if(a)return;if(s)gt[v+".x"]=s.p2r(s.r2p(e.x)+t);else{var i=e._xsize/x.w,o=e.x+(e._xshift-e.xshift)/x.w-i/2;gt[v+".x"]=p.align(o+t/x.w,i,0,1,e.xanchor)}if(g)gt[v+".y"]=g.p2r(g.r2p(e.y)+r);else{var l=e._ysize/x.h,c=e.y-(e._yshift+e.yshift)/x.h-l/2;gt[v+".y"]=p.align(c-r/x.h,l,0,1,e.yanchor)}s&&g||(n=p.getCursor(s?.5:gt[v+".x"],g?.5:gt[v+".y"],e.xanchor,e.yanchor))}A.attr({transform:"translate("+t+","+r+")"+mt}),h(C,n)},doneFn:function(){h(C),i.call("relayout",t,gt);var e=document.querySelector(".js-notes-box-panel");e&&e.redraw(e.selectedObj)}})}}}e.exports={draw:function(t){var e=t._fullLayout;e._infolayer.selectAll(".annotation").remove();for(var r=0;r=0,m=e.indexOf("end")>=0,v=f.backoff*p+r.standoff,y=h.backoff*d+r.startstandoff;if("line"===u.nodeName){o={x:+t.attr("x1"),y:+t.attr("y1")},s={x:+t.attr("x2"),y:+t.attr("y2")};var x=o.x-s.x,b=o.y-s.y;if(c=(l=Math.atan2(b,x))+Math.PI,v&&y&&v+y>Math.sqrt(x*x+b*b))return void z();if(v){if(v*v>x*x+b*b)return void z();var _=v*Math.cos(l),w=v*Math.sin(l);s.x+=_,s.y+=w,t.attr({x2:s.x,y2:s.y})}if(y){if(y*y>x*x+b*b)return void z();var k=y*Math.cos(l),M=y*Math.sin(l);o.x-=k,o.y-=M,t.attr({x1:o.x,y1:o.y})}}else if("path"===u.nodeName){var A=u.getTotalLength(),T="";if(A1){c=!0;break}}c?t.fullLayout._infolayer.select(".annotation-"+t.id+'[data-index="'+s+'"]').remove():(l._pdata=i(t.glplot.cameraParams,[e.xaxis.r2l(l.x)*r[0],e.yaxis.r2l(l.y)*r[1],e.zaxis.r2l(l.z)*r[2]]),n(t.graphDiv,l,s,t.id,l._xa,l._ya))}}},{"../../plots/gl3d/project":757,"../annotations/draw":521}],528:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("../../lib");e.exports={moduleType:"component",name:"annotations3d",schema:{subplots:{scene:{annotations:t("./attributes")}}},layoutAttributes:t("./attributes"),handleDefaults:t("./defaults"),includeBasePlot:function(t,e){var r=n.subplotsRegistry.gl3d;if(!r)return;for(var a=r.attrRegex,o=Object.keys(t),s=0;s=0))return t;if(3===o)n[o]>1&&(n[o]=1);else if(n[o]>=1)return t}var s=Math.round(255*n[0])+", "+Math.round(255*n[1])+", "+Math.round(255*n[2]);return a?"rgba("+s+", "+n[3]+")":"rgb("+s+")"}a.tinyRGB=function(t){var e=t.toRgb();return"rgb("+Math.round(e.r)+", "+Math.round(e.g)+", "+Math.round(e.b)+")"},a.rgb=function(t){return a.tinyRGB(n(t))},a.opacity=function(t){return t?n(t).getAlpha():0},a.addOpacity=function(t,e){var r=n(t).toRgb();return"rgba("+Math.round(r.r)+", "+Math.round(r.g)+", "+Math.round(r.b)+", "+e+")"},a.combine=function(t,e){var r=n(t).toRgb();if(1===r.a)return n(t).toRgbString();var i=n(e||l).toRgb(),a=1===i.a?i:{r:255*(1-i.a)+i.r*i.a,g:255*(1-i.a)+i.g*i.a,b:255*(1-i.a)+i.b*i.a},o={r:a.r*(1-r.a)+r.r*r.a,g:a.g*(1-r.a)+r.g*r.a,b:a.b*(1-r.a)+r.b*r.a};return n(o).toRgbString()},a.contrast=function(t,e,r){var i=n(t);return 1!==i.getAlpha()&&(i=n(a.combine(t,l))),(i.isDark()?e?i.lighten(e):l:r?i.darken(r):s).toString()},a.stroke=function(t,e){var r=n(e);t.style({stroke:a.tinyRGB(r),"stroke-opacity":r.getAlpha()})},a.fill=function(t,e){var r=n(e);t.style({fill:a.tinyRGB(r),"fill-opacity":r.getAlpha()})},a.clean=function(t){if(t&&"object"==typeof t){var e,r,n,i,o=Object.keys(t);for(e=0;e0?A>=P:A<=P));T++)A>O&&A0?A>=P:A<=P));T++)A>S[0]&&A1){var nt=Math.pow(10,Math.floor(Math.log(rt)/Math.LN10));tt*=nt*c.roundUp(rt/nt,[2,5,10]),(Math.abs(r.levels.start)/r.levels.size+1e-6)%1<2e-6&&(Q.tick0=0)}Q.dtick=tt}Q.domain=[X+G,X+U-G],Q.setScale();var it=c.ensureSingle(b._infolayer,"g",e,function(t){t.classed(_.colorbar,!0).each(function(){var t=n.select(this);t.append("rect").classed(_.cbbg,!0),t.append("g").classed(_.cbfills,!0),t.append("g").classed(_.cblines,!0),t.append("g").classed(_.cbaxis,!0).classed(_.crisp,!0),t.append("g").classed(_.cbtitleunshift,!0).append("g").classed(_.cbtitle,!0),t.append("rect").classed(_.cboutline,!0),t.select(".cbtitle").datum(0)})});it.attr("transform","translate("+Math.round(M.l)+","+Math.round(M.t)+")");var at=it.select(".cbtitleunshift").attr("transform","translate(-"+Math.round(M.l)+",-"+Math.round(M.t)+")");Q._axislayer=it.select(".cbaxis");var ot=0;if(-1!==["top","bottom"].indexOf(r.titleside)){var st,lt=M.l+(r.x+q)*M.w,ct=Q.titlefont.size;st="top"===r.titleside?(1-(X+U-G))*M.h+M.t+3+.75*ct:(1-(X+G))*M.h+M.t-3-.25*ct,gt(Q._id+"title",{attributes:{x:lt,y:st,"text-anchor":"start"}})}var ut,ft,ht,pt=c.syncOrAsync([a.previousPromises,function(){if(-1!==["top","bottom"].indexOf(r.titleside)){var e=it.select(".cbtitle"),a=e.select("text"),o=[-r.outlinewidth/2,r.outlinewidth/2],l=e.select(".h"+Q._id+"title-math-group").node(),u=15.6;if(a.node()&&(u=parseInt(a.node().style.fontSize,10)*m),l?(ot=h.bBox(l).height)>u&&(o[1]-=(ot-u)/2):a.node()&&!a.classed(_.jsPlaceholder)&&(ot=h.bBox(a.node()).height),ot){if(ot+=5,"top"===r.titleside)Q.domain[1]-=ot/M.h,o[1]*=-1;else{Q.domain[0]+=ot/M.h;var f=g.lineCount(a);o[1]+=(1-f)*u}e.attr("transform","translate("+o+")"),Q.setScale()}}it.selectAll(".cbfills,.cblines").attr("transform","translate(0,"+Math.round(M.h*(1-Q.domain[1]))+")"),Q._axislayer.attr("transform","translate(0,"+Math.round(-M.t)+")");var p=it.select(".cbfills").selectAll("rect.cbfill").data(E);p.enter().append("rect").classed(_.cbfill,!0).style("stroke","none"),p.exit().remove(),p.each(function(t,e){var r=[0===e?S[0]:(E[e]+E[e-1])/2,e===E.length-1?S[1]:(E[e]+E[e+1])/2].map(Q.c2p).map(Math.round);e!==E.length-1&&(r[1]+=r[1]>r[0]?1:-1);var a=z(t).replace("e-",""),o=i(a).toHexString();n.select(this).attr({x:W,width:Math.max(N,2),y:n.min(r),height:Math.max(n.max(r)-n.min(r),2),fill:o})});var d=it.select(".cblines").selectAll("path.cbline").data(r.line.color&&r.line.width?C:[]);return d.enter().append("path").classed(_.cbline,!0),d.exit().remove(),d.each(function(t){n.select(this).attr("d","M"+W+","+(Math.round(Q.c2p(t))+r.line.width/2%1)+"h"+N).call(h.lineGroupStyle,r.line.width,L(t),r.line.dash)}),Q._axislayer.selectAll("g."+Q._id+"tick,path").remove(),Q._pos=W+N+(r.outlinewidth||0)/2-("outside"===r.ticks?1:0),Q.side="right",c.syncOrAsync([function(){return s.doTicksSingle(t,Q,!0)},function(){if(-1===["top","bottom"].indexOf(r.titleside)){var e=Q.titlefont.size,i=Q._offset+Q._length/2,a=M.l+(Q.position||0)*M.w+("right"===Q.side?10+e*(Q.showticklabels?1:.5):-10-e*(Q.showticklabels?.5:0));gt("h"+Q._id+"title",{avoid:{selection:n.select(t).selectAll("g."+Q._id+"tick"),side:r.titleside,offsetLeft:M.l,offsetTop:0,maxShift:b.width},attributes:{x:a,y:i,"text-anchor":"middle"},transform:{rotate:"-90",offset:0}})}}])},a.previousPromises,function(){var n=N+r.outlinewidth/2+h.bBox(Q._axislayer.node()).width;if((R=at.select("text")).node()&&!R.classed(_.jsPlaceholder)){var i,o=at.select(".h"+Q._id+"title-math-group").node();i=o&&-1!==["top","bottom"].indexOf(r.titleside)?h.bBox(o).width:h.bBox(at.node()).right-W-M.l,n=Math.max(n,i)}var s=2*r.xpad+n+r.borderwidth+r.outlinewidth/2,l=Z-J;it.select(".cbbg").attr({x:W-r.xpad-(r.borderwidth+r.outlinewidth)/2,y:J-H,width:Math.max(s,2),height:Math.max(l+2*H,2)}).call(p.fill,r.bgcolor).call(p.stroke,r.bordercolor).style({"stroke-width":r.borderwidth}),it.selectAll(".cboutline").attr({x:W,y:J+r.ypad+("top"===r.titleside?ot:0),width:Math.max(N,2),height:Math.max(l-2*r.ypad-ot,2)}).call(p.stroke,r.outlinecolor).style({fill:"None","stroke-width":r.outlinewidth});var c=({center:.5,right:1}[r.xanchor]||0)*s;it.attr("transform","translate("+(M.l-c)+","+M.t+")"),a.autoMargin(t,e,{x:r.x,y:r.y,l:s*({right:1,center:.5}[r.xanchor]||0),r:s*({left:1,center:.5}[r.xanchor]||0),t:l*({bottom:1,middle:.5}[r.yanchor]||0),b:l*({top:1,middle:.5}[r.yanchor]||0)})}],t);if(pt&&pt.then&&(t._promises||[]).push(pt),t._context.edits.colorbarPosition)l.init({element:it.node(),gd:t,prepFn:function(){ut=it.attr("transform"),f(it)},moveFn:function(t,e){it.attr("transform",ut+" translate("+t+","+e+")"),ft=l.align(Y+t/M.w,j,0,1,r.xanchor),ht=l.align(X-e/M.h,U,0,1,r.yanchor);var n=l.getCursor(ft,ht,r.xanchor,r.yanchor);f(it,n)},doneFn:function(){f(it),void 0!==ft&&void 0!==ht&&o.call("restyle",t,{"colorbar.x":ft,"colorbar.y":ht},k().index)}});return pt}function dt(t,e){return c.coerce(K,Q,x,t,e)}function gt(e,r){var n,i=k();n=o.traceIs(i,"markerColorscale")?"marker.colorbar.title":"colorbar.title";var a={propContainer:Q,propName:n,traceIndex:i.index,placeholder:b._dfltTitle.colorbar,containerGroup:it.select(".cbtitle")},s="h"===e.charAt(0)?e.substr(1):"h"+e;it.selectAll("."+s+",."+s+"-math-group").remove(),d.draw(t,e,u(a,r||{}))}b._infolayer.selectAll("g."+e).remove()}function k(){var r,n,i=e.substr(2);for(r=0;r=0?i.Reds:i.Blues,s.reversescale?a(y):y),l.autocolorscale||f("autocolorscale",!1))}},{"../../lib":660,"./flip_scale":544,"./scales":551}],540:[function(t,e,r){"use strict";var n=t("./attributes"),i=t("../../lib/extend").extendFlat;t("./scales.js");e.exports=function(t,e,r){return{color:{valType:"color",arrayOk:!0,editType:e||"style"},colorscale:i({},n.colorscale,{}),cauto:i({},n.zauto,{impliedEdits:{cmin:void 0,cmax:void 0}}),cmax:i({},n.zmax,{editType:e||n.zmax.editType,impliedEdits:{cauto:!1}}),cmin:i({},n.zmin,{editType:e||n.zmin.editType,impliedEdits:{cauto:!1}}),autocolorscale:i({},n.autocolorscale,{dflt:!1===r?r:n.autocolorscale.dflt}),reversescale:i({},n.reversescale,{})}}},{"../../lib/extend":649,"./attributes":538,"./scales.js":551}],541:[function(t,e,r){"use strict";var n=t("./scales");e.exports=n.RdBu},{"./scales":551}],542:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../../lib"),a=t("../colorbar/has_colorbar"),o=t("../colorbar/defaults"),s=t("./is_valid_scale"),l=t("./flip_scale");e.exports=function(t,e,r,c,u){var f,h=u.prefix,p=u.cLetter,d=h.slice(0,h.length-1),g=h?i.nestedProperty(t,d).get()||{}:t,m=h?i.nestedProperty(e,d).get()||{}:e,v=g[p+"min"],y=g[p+"max"],x=g.colorscale;c(h+p+"auto",!(n(v)&&n(y)&&v=0;i--,a++)e=t[i],n[a]=[1-e[0],e[1]];return n}},{}],545:[function(t,e,r){"use strict";var n=t("./scales"),i=t("./default_scale"),a=t("./is_valid_scale_array");e.exports=function(t,e){if(e||(e=i),!t)return e;function r(){try{t=n[t]||JSON.parse(t)}catch(r){t=e}}return"string"==typeof t&&(r(),"string"==typeof t&&r()),a(t)?t:e}},{"./default_scale":541,"./is_valid_scale_array":549,"./scales":551}],546:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../../lib"),a=t("./is_valid_scale");e.exports=function(t,e){var r=e?i.nestedProperty(t,e).get()||{}:t,o=r.color,s=!1;if(i.isArrayOrTypedArray(o))for(var l=0;l4/3-s?o:s}},{}],553:[function(t,e,r){"use strict";var n=t("../../lib"),i=[["sw-resize","s-resize","se-resize"],["w-resize","move","e-resize"],["nw-resize","n-resize","ne-resize"]];e.exports=function(t,e,r,a){return t="left"===r?0:"center"===r?1:"right"===r?2:n.constrain(Math.floor(3*t),0,2),e="bottom"===a?0:"middle"===a?1:"top"===a?2:n.constrain(Math.floor(3*e),0,2),i[e][t]}},{"../../lib":660}],554:[function(t,e,r){"use strict";var n=t("mouse-event-offset"),i=t("has-hover"),a=t("has-passive-events"),o=t("../../registry"),s=t("../../lib"),l=t("../../plots/cartesian/constants"),c=t("../../constants/interactions"),u=e.exports={};u.align=t("./align"),u.getCursor=t("./cursor");var f=t("./unhover");function h(){var t=document.createElement("div");t.className="dragcover";var e=t.style;return e.position="fixed",e.left=0,e.right=0,e.top=0,e.bottom=0,e.zIndex=999999999,e.background="none",document.body.appendChild(t),t}function p(t){return n(t.changedTouches?t.changedTouches[0]:t,document.body)}u.unhover=f.wrapped,u.unhoverRaw=f.raw,u.init=function(t){var e,r,n,f,d,g,m,v,y=t.gd,x=1,b=c.DBLCLICKDELAY,_=t.element;y._mouseDownTime||(y._mouseDownTime=0),_.style.pointerEvents="all",_.onmousedown=k,a?(_._ontouchstart&&_.removeEventListener("touchstart",_._ontouchstart),_._ontouchstart=k,_.addEventListener("touchstart",k,{passive:!1})):_.ontouchstart=k;var w=t.clampFn||function(t,e,r){return Math.abs(t)b&&(x=Math.max(x-1,1)),y._dragged)t.doneFn&&t.doneFn();else if(t.clickFn&&t.clickFn(x,g),!v){var r;try{r=new MouseEvent("click",e)}catch(t){var n=p(e);(r=document.createEvent("MouseEvents")).initMouseEvent("click",e.bubbles,e.cancelable,e.view,e.detail,e.screenX,e.screenY,n[0],n[1],e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,e.relatedTarget)}m.dispatchEvent(r)}!function(t){t._dragging=!1,t._replotPending&&o.call("plot",t)}(y),y._dragged=!1}else y._dragged=!1}},u.coverSlip=h},{"../../constants/interactions":636,"../../lib":660,"../../plots/cartesian/constants":711,"../../registry":790,"./align":552,"./cursor":553,"./unhover":555,"has-hover":354,"has-passive-events":355,"mouse-event-offset":379}],555:[function(t,e,r){"use strict";var n=t("../../lib/events"),i=t("../../lib/throttle"),a=t("../../lib/get_graph_div"),o=t("../fx/constants"),s=e.exports={};s.wrapped=function(t,e,r){(t=a(t))._fullLayout&&i.clear(t._fullLayout._uid+o.HOVERID),s.raw(t,e,r)},s.raw=function(t,e){var r=t._fullLayout,i=t._hoverdata;e||(e={}),e.target&&!1===n.triggerHandler(t,"plotly_beforehover",e)||(r._hoverlayer.selectAll("g").remove(),r._hoverlayer.selectAll("line").remove(),r._hoverlayer.selectAll("circle").remove(),t._hoverdata=void 0,e.target&&i&&t.emit("plotly_unhover",{event:e,points:i}))}},{"../../lib/events":648,"../../lib/get_graph_div":655,"../../lib/throttle":685,"../fx/constants":569}],556:[function(t,e,r){"use strict";r.dash={valType:"string",values:["solid","dot","dash","longdash","dashdot","longdashdot"],dflt:"solid",editType:"style"}},{}],557:[function(t,e,r){"use strict";var n=t("d3"),i=t("fast-isnumeric"),a=t("tinycolor2"),o=t("../../registry"),s=t("../color"),l=t("../colorscale"),c=t("../../lib"),u=t("../../lib/svg_text_utils"),f=t("../../constants/xmlns_namespaces"),h=t("../../constants/alignment").LINE_SPACING,p=t("../../constants/interactions").DESELECTDIM,d=t("../../traces/scatter/subtypes"),g=t("../../traces/scatter/make_bubble_size_func"),m=e.exports={};m.font=function(t,e,r,n){c.isPlainObject(e)&&(n=e.color,r=e.size,e=e.family),e&&t.style("font-family",e),r+1&&t.style("font-size",r+"px"),n&&t.call(s.fill,n)},m.setPosition=function(t,e,r){t.attr("x",e).attr("y",r)},m.setSize=function(t,e,r){t.attr("width",e).attr("height",r)},m.setRect=function(t,e,r,n,i){t.call(m.setPosition,e,r).call(m.setSize,n,i)},m.translatePoint=function(t,e,r,n){var a=r.c2p(t.x),o=n.c2p(t.y);return!!(i(a)&&i(o)&&e.node())&&("text"===e.node().nodeName?e.attr("x",a).attr("y",o):e.attr("transform","translate("+a+","+o+")"),!0)},m.translatePoints=function(t,e,r){t.each(function(t){var i=n.select(this);m.translatePoint(t,i,e,r)})},m.hideOutsideRangePoint=function(t,e,r,n,i,a){e.attr("display",r.isPtWithinRange(t,i)&&n.isPtWithinRange(t,a)?null:"none")},m.hideOutsideRangePoints=function(t,e){if(e._hasClipOnAxisFalse){var r=e.xaxis,i=e.yaxis;t.each(function(e){var a=e[0].trace,o=a.xcalendar,s=a.ycalendar,l="bar"===a.type?".bartext":".point,.textpoint";t.selectAll(l).each(function(t){m.hideOutsideRangePoint(t,n.select(this),r,i,o,s)})})}},m.crispRound=function(t,e,r){return e&&i(e)?t._context.staticPlot?e:e<1?1:Math.round(e):r||0},m.singleLineStyle=function(t,e,r,n,i){e.style("fill","none");var a=(((t||[])[0]||{}).trace||{}).line||{},o=r||a.width||0,l=i||a.dash||"";s.stroke(e,n||a.color),m.dashLine(e,l,o)},m.lineGroupStyle=function(t,e,r,i){t.style("fill","none").each(function(t){var a=(((t||[])[0]||{}).trace||{}).line||{},o=e||a.width||0,l=i||a.dash||"";n.select(this).call(s.stroke,r||a.color).call(m.dashLine,l,o)})},m.dashLine=function(t,e,r){r=+r||0,e=m.dashStyle(e,r),t.style({"stroke-dasharray":e,"stroke-width":r+"px"})},m.dashStyle=function(t,e){e=+e||1;var r=Math.max(e,3);return"solid"===t?t="":"dot"===t?t=r+"px,"+r+"px":"dash"===t?t=3*r+"px,"+3*r+"px":"longdash"===t?t=5*r+"px,"+5*r+"px":"dashdot"===t?t=3*r+"px,"+r+"px,"+r+"px,"+r+"px":"longdashdot"===t&&(t=5*r+"px,"+2*r+"px,"+r+"px,"+2*r+"px"),t},m.singleFillStyle=function(t){var e=(((n.select(t.node()).data()[0]||[])[0]||{}).trace||{}).fillcolor;e&&t.call(s.fill,e)},m.fillGroupStyle=function(t){t.style("stroke-width",0).each(function(e){var r=n.select(this);try{r.call(s.fill,e[0].trace.fillcolor)}catch(e){c.error(e,t),r.remove()}})};var v=t("./symbol_defs");m.symbolNames=[],m.symbolFuncs=[],m.symbolNeedLines={},m.symbolNoDot={},m.symbolNoFill={},m.symbolList=[],Object.keys(v).forEach(function(t){var e=v[t];m.symbolList=m.symbolList.concat([e.n,t,e.n+100,t+"-open"]),m.symbolNames[e.n]=t,m.symbolFuncs[e.n]=e.f,e.needLine&&(m.symbolNeedLines[e.n]=!0),e.noDot?m.symbolNoDot[e.n]=!0:m.symbolList=m.symbolList.concat([e.n+200,t+"-dot",e.n+300,t+"-open-dot"]),e.noFill&&(m.symbolNoFill[e.n]=!0)});var y=m.symbolNames.length,x="M0,0.5L0.5,0L0,-0.5L-0.5,0Z";function b(t,e){var r=t%100;return m.symbolFuncs[r](e)+(t>=200?x:"")}m.symbolNumber=function(t){if("string"==typeof t){var e=0;t.indexOf("-open")>0&&(e=100,t=t.replace("-open","")),t.indexOf("-dot")>0&&(e+=200,t=t.replace("-dot","")),(t=m.symbolNames.indexOf(t))>=0&&(t+=e)}return t%100>=y||t>=400?0:Math.floor(Math.max(t,0))};var _={x1:1,x2:0,y1:0,y2:0},w={x1:0,x2:0,y1:1,y2:0};m.gradient=function(t,e,r,i,o,l){var u=e._fullLayout._defs.select(".gradients").selectAll("#"+r).data([i+o+l],c.identity);u.exit().remove(),u.enter().append("radial"===i?"radialGradient":"linearGradient").each(function(){var t=n.select(this);"horizontal"===i?t.attr(_):"vertical"===i&&t.attr(w),t.attr("id",r);var e=a(o),c=a(l);t.append("stop").attr({offset:"0%","stop-color":s.tinyRGB(c),"stop-opacity":c.getAlpha()}),t.append("stop").attr({offset:"100%","stop-color":s.tinyRGB(e),"stop-opacity":e.getAlpha()})}),t.style({fill:"url(#"+r+")","fill-opacity":null})},m.initGradients=function(t){c.ensureSingle(t._fullLayout._defs,"g","gradients").selectAll("linearGradient,radialGradient").remove()},m.pointStyle=function(t,e,r){if(t.size()){var i=m.makePointStyleFns(e);t.each(function(t){m.singlePointStyle(t,n.select(this),e,i,r)})}},m.singlePointStyle=function(t,e,r,n,i){var a=r.marker,o=a.line;if(e.style("opacity",n.selectedOpacityFn?n.selectedOpacityFn(t):void 0===t.mo?a.opacity:t.mo),n.ms2mrc){var l;l="various"===t.ms||"various"===a.size?3:n.ms2mrc(t.ms),t.mrc=l,n.selectedSizeFn&&(l=t.mrc=n.selectedSizeFn(t));var u=m.symbolNumber(t.mx||a.symbol)||0;t.om=u%200>=100,e.attr("d",b(u,l))}var f,h,p,d=!1;if(t.so?(p=o.outlierwidth,h=o.outliercolor,f=a.outliercolor):(p=(t.mlw+1||o.width+1||(t.trace?t.trace.marker.line.width:0)+1)-1,h="mlc"in t?t.mlcc=n.lineScale(t.mlc):c.isArrayOrTypedArray(o.color)?s.defaultLine:o.color,c.isArrayOrTypedArray(a.color)&&(f=s.defaultLine,d=!0),f="mc"in t?t.mcc=n.markerScale(t.mc):a.color||"rgba(0,0,0,0)",n.selectedColorFn&&(f=n.selectedColorFn(t))),t.om)e.call(s.stroke,f).style({"stroke-width":(p||1)+"px",fill:"none"});else{e.style("stroke-width",p+"px");var g=a.gradient,v=t.mgt;if(v?d=!0:v=g&&g.type,v&&"none"!==v){var y=t.mgc;y?d=!0:y=g.color;var x="g"+i._fullLayout._uid+"-"+r.uid;d&&(x+="-"+t.i),e.call(m.gradient,i,x,v,f,y)}else e.call(s.fill,f);p&&e.call(s.stroke,h)}},m.makePointStyleFns=function(t){var e={},r=t.marker;return e.markerScale=m.tryColorscale(r,""),e.lineScale=m.tryColorscale(r,"line"),o.traceIs(t,"symbols")&&(e.ms2mrc=d.isBubble(t)?g(t):function(){return(r.size||6)/2}),t.selectedpoints&&c.extendFlat(e,m.makeSelectedPointStyleFns(t)),e},m.makeSelectedPointStyleFns=function(t){var e={},r=t.selected||{},n=t.unselected||{},i=t.marker||{},a=r.marker||{},s=n.marker||{},l=i.opacity,u=a.opacity,f=s.opacity,h=void 0!==u,d=void 0!==f;(c.isArrayOrTypedArray(l)||h||d)&&(e.selectedOpacityFn=function(t){var e=void 0===t.mo?i.opacity:t.mo;return t.selected?h?u:e:d?f:p*e});var g=i.color,m=a.color,v=s.color;(m||v)&&(e.selectedColorFn=function(t){var e=t.mcc||g;return t.selected?m||e:v||e});var y=i.size,x=a.size,b=s.size,_=void 0!==x,w=void 0!==b;return o.traceIs(t,"symbols")&&(_||w)&&(e.selectedSizeFn=function(t){var e=t.mrc||y/2;return t.selected?_?x/2:e:w?b/2:e}),e},m.makeSelectedTextStyleFns=function(t){var e={},r=t.selected||{},n=t.unselected||{},i=t.textfont||{},a=r.textfont||{},o=n.textfont||{},l=i.color,c=a.color,u=o.color;return e.selectedTextColorFn=function(t){var e=t.tc||l;return t.selected?c||e:u||(c?e:s.addOpacity(e,p))},e},m.selectedPointStyle=function(t,e){if(t.size()&&e.selectedpoints){var r=m.makeSelectedPointStyleFns(e),i=e.marker||{},a=[];r.selectedOpacityFn&&a.push(function(t,e){t.style("opacity",r.selectedOpacityFn(e))}),r.selectedColorFn&&a.push(function(t,e){s.fill(t,r.selectedColorFn(e))}),r.selectedSizeFn&&a.push(function(t,e){var n=e.mx||i.symbol||0,a=r.selectedSizeFn(e);t.attr("d",b(m.symbolNumber(n),a)),e.mrc2=a}),a.length&&t.each(function(t){for(var e=n.select(this),r=0;r0?r:0}m.textPointStyle=function(t,e,r){if(t.size()){var i;if(e.selectedpoints){var a=m.makeSelectedTextStyleFns(e);i=a.selectedTextColorFn}t.each(function(t){var a=n.select(this),o=c.extractOption(t,e,"tx","text");if(o||0===o){var s=t.tp||e.textposition,l=A(t,e),f=i?i(t):t.tc||e.textfont.color;a.call(m.font,t.tf||e.textfont.family,l,f).text(o).call(u.convertToTspans,r).call(M,s,l,t.mrc)}else a.remove()})}},m.selectedTextStyle=function(t,e){if(t.size()&&e.selectedpoints){var r=m.makeSelectedTextStyleFns(e);t.each(function(t){var i=n.select(this),a=r.selectedTextColorFn(t),o=t.tp||e.textposition,l=A(t,e);s.fill(i,a),M(i,o,l,t.mrc2||t.mrc)})}};var T=.5;function S(t,e,r,i){var a=t[0]-e[0],o=t[1]-e[1],s=r[0]-e[0],l=r[1]-e[1],c=Math.pow(a*a+o*o,T/2),u=Math.pow(s*s+l*l,T/2),f=(u*u*a-c*c*s)*i,h=(u*u*o-c*c*l)*i,p=3*u*(c+u),d=3*c*(c+u);return[[n.round(e[0]+(p&&f/p),2),n.round(e[1]+(p&&h/p),2)],[n.round(e[0]-(d&&f/d),2),n.round(e[1]-(d&&h/d),2)]]}m.smoothopen=function(t,e){if(t.length<3)return"M"+t.join("L");var r,n="M"+t[0],i=[];for(r=1;r=1e4&&(m.savedBBoxes={},L=0),r&&(m.savedBBoxes[r]=v),L++,c.extendFlat({},v)},m.setClipUrl=function(t,e){if(e){if(void 0===m.baseUrl){var r=n.select("base");r.size()&&r.attr("href")?m.baseUrl=window.location.href.split("#")[0]:m.baseUrl=""}t.attr("clip-path","url("+m.baseUrl+"#"+e+")")}else t.attr("clip-path",null)},m.getTranslate=function(t){var e=(t[t.attr?"attr":"getAttribute"]("transform")||"").replace(/.*\btranslate\((-?\d*\.?\d*)[^-\d]*(-?\d*\.?\d*)[^\d].*/,function(t,e,r){return[e,r].join(" ")}).split(" ");return{x:+e[0]||0,y:+e[1]||0}},m.setTranslate=function(t,e,r){var n=t.attr?"attr":"getAttribute",i=t.attr?"attr":"setAttribute",a=t[n]("transform")||"";return e=e||0,r=r||0,a=a.replace(/(\btranslate\(.*?\);?)/,"").trim(),a=(a+=" translate("+e+", "+r+")").trim(),t[i]("transform",a),a},m.getScale=function(t){var e=(t[t.attr?"attr":"getAttribute"]("transform")||"").replace(/.*\bscale\((\d*\.?\d*)[^\d]*(\d*\.?\d*)[^\d].*/,function(t,e,r){return[e,r].join(" ")}).split(" ");return{x:+e[0]||1,y:+e[1]||1}},m.setScale=function(t,e,r){var n=t.attr?"attr":"getAttribute",i=t.attr?"attr":"setAttribute",a=t[n]("transform")||"";return e=e||1,r=r||1,a=a.replace(/(\bscale\(.*?\);?)/,"").trim(),a=(a+=" scale("+e+", "+r+")").trim(),t[i]("transform",a),a};var P=/\s*sc.*/;m.setPointGroupScale=function(t,e,r){if(e=e||1,r=r||1,t){var n=1===e&&1===r?"":" scale("+e+","+r+")";t.each(function(){var t=(this.getAttribute("transform")||"").replace(P,"");t=(t+=n).trim(),this.setAttribute("transform",t)})}};var D=/translate\([^)]*\)\s*$/;m.setTextPointsScale=function(t,e,r){t&&t.each(function(){var t,i=n.select(this),a=i.select("text");if(a.node()){var o=parseFloat(a.attr("x")||0),s=parseFloat(a.attr("y")||0),l=(i.attr("transform")||"").match(D);t=1===e&&1===r?[]:["translate("+o+","+s+")","scale("+e+","+r+")","translate("+-o+","+-s+")"],l&&t.push(l),i.attr("transform",t.join(" "))}})}},{"../../constants/alignment":632,"../../constants/interactions":636,"../../constants/xmlns_namespaces":639,"../../lib":660,"../../lib/svg_text_utils":684,"../../registry":790,"../../traces/scatter/make_bubble_size_func":1007,"../../traces/scatter/subtypes":1012,"../color":532,"../colorscale":547,"./symbol_defs":558,d3:131,"fast-isnumeric":197,tinycolor2:473}],558:[function(t,e,r){"use strict";var n=t("d3");e.exports={circle:{n:0,f:function(t){var e=n.round(t,2);return"M"+e+",0A"+e+","+e+" 0 1,1 0,-"+e+"A"+e+","+e+" 0 0,1 "+e+",0Z"}},square:{n:1,f:function(t){var e=n.round(t,2);return"M"+e+","+e+"H-"+e+"V-"+e+"H"+e+"Z"}},diamond:{n:2,f:function(t){var e=n.round(1.3*t,2);return"M"+e+",0L0,"+e+"L-"+e+",0L0,-"+e+"Z"}},cross:{n:3,f:function(t){var e=n.round(.4*t,2),r=n.round(1.2*t,2);return"M"+r+","+e+"H"+e+"V"+r+"H-"+e+"V"+e+"H-"+r+"V-"+e+"H-"+e+"V-"+r+"H"+e+"V-"+e+"H"+r+"Z"}},x:{n:4,f:function(t){var e=n.round(.8*t/Math.sqrt(2),2),r="l"+e+","+e,i="l"+e+",-"+e,a="l-"+e+",-"+e,o="l-"+e+","+e;return"M0,"+e+r+i+a+i+a+o+a+o+r+o+r+"Z"}},"triangle-up":{n:5,f:function(t){var e=n.round(2*t/Math.sqrt(3),2);return"M-"+e+","+n.round(t/2,2)+"H"+e+"L0,-"+n.round(t,2)+"Z"}},"triangle-down":{n:6,f:function(t){var e=n.round(2*t/Math.sqrt(3),2);return"M-"+e+",-"+n.round(t/2,2)+"H"+e+"L0,"+n.round(t,2)+"Z"}},"triangle-left":{n:7,f:function(t){var e=n.round(2*t/Math.sqrt(3),2);return"M"+n.round(t/2,2)+",-"+e+"V"+e+"L-"+n.round(t,2)+",0Z"}},"triangle-right":{n:8,f:function(t){var e=n.round(2*t/Math.sqrt(3),2);return"M-"+n.round(t/2,2)+",-"+e+"V"+e+"L"+n.round(t,2)+",0Z"}},"triangle-ne":{n:9,f:function(t){var e=n.round(.6*t,2),r=n.round(1.2*t,2);return"M-"+r+",-"+e+"H"+e+"V"+r+"Z"}},"triangle-se":{n:10,f:function(t){var e=n.round(.6*t,2),r=n.round(1.2*t,2);return"M"+e+",-"+r+"V"+e+"H-"+r+"Z"}},"triangle-sw":{n:11,f:function(t){var e=n.round(.6*t,2),r=n.round(1.2*t,2);return"M"+r+","+e+"H-"+e+"V-"+r+"Z"}},"triangle-nw":{n:12,f:function(t){var e=n.round(.6*t,2),r=n.round(1.2*t,2);return"M-"+e+","+r+"V-"+e+"H"+r+"Z"}},pentagon:{n:13,f:function(t){var e=n.round(.951*t,2),r=n.round(.588*t,2),i=n.round(-t,2),a=n.round(-.309*t,2);return"M"+e+","+a+"L"+r+","+n.round(.809*t,2)+"H-"+r+"L-"+e+","+a+"L0,"+i+"Z"}},hexagon:{n:14,f:function(t){var e=n.round(t,2),r=n.round(t/2,2),i=n.round(t*Math.sqrt(3)/2,2);return"M"+i+",-"+r+"V"+r+"L0,"+e+"L-"+i+","+r+"V-"+r+"L0,-"+e+"Z"}},hexagon2:{n:15,f:function(t){var e=n.round(t,2),r=n.round(t/2,2),i=n.round(t*Math.sqrt(3)/2,2);return"M-"+r+","+i+"H"+r+"L"+e+",0L"+r+",-"+i+"H-"+r+"L-"+e+",0Z"}},octagon:{n:16,f:function(t){var e=n.round(.924*t,2),r=n.round(.383*t,2);return"M-"+r+",-"+e+"H"+r+"L"+e+",-"+r+"V"+r+"L"+r+","+e+"H-"+r+"L-"+e+","+r+"V-"+r+"Z"}},star:{n:17,f:function(t){var e=1.4*t,r=n.round(.225*e,2),i=n.round(.951*e,2),a=n.round(.363*e,2),o=n.round(.588*e,2),s=n.round(-e,2),l=n.round(-.309*e,2),c=n.round(.118*e,2),u=n.round(.809*e,2);return"M"+r+","+l+"H"+i+"L"+a+","+c+"L"+o+","+u+"L0,"+n.round(.382*e,2)+"L-"+o+","+u+"L-"+a+","+c+"L-"+i+","+l+"H-"+r+"L0,"+s+"Z"}},hexagram:{n:18,f:function(t){var e=n.round(.66*t,2),r=n.round(.38*t,2),i=n.round(.76*t,2);return"M-"+i+",0l-"+r+",-"+e+"h"+i+"l"+r+",-"+e+"l"+r+","+e+"h"+i+"l-"+r+","+e+"l"+r+","+e+"h-"+i+"l-"+r+","+e+"l-"+r+",-"+e+"h-"+i+"Z"}},"star-triangle-up":{n:19,f:function(t){var e=n.round(t*Math.sqrt(3)*.8,2),r=n.round(.8*t,2),i=n.round(1.6*t,2),a=n.round(4*t,2),o="A "+a+","+a+" 0 0 1 ";return"M-"+e+","+r+o+e+","+r+o+"0,-"+i+o+"-"+e+","+r+"Z"}},"star-triangle-down":{n:20,f:function(t){var e=n.round(t*Math.sqrt(3)*.8,2),r=n.round(.8*t,2),i=n.round(1.6*t,2),a=n.round(4*t,2),o="A "+a+","+a+" 0 0 1 ";return"M"+e+",-"+r+o+"-"+e+",-"+r+o+"0,"+i+o+e+",-"+r+"Z"}},"star-square":{n:21,f:function(t){var e=n.round(1.1*t,2),r=n.round(2*t,2),i="A "+r+","+r+" 0 0 1 ";return"M-"+e+",-"+e+i+"-"+e+","+e+i+e+","+e+i+e+",-"+e+i+"-"+e+",-"+e+"Z"}},"star-diamond":{n:22,f:function(t){var e=n.round(1.4*t,2),r=n.round(1.9*t,2),i="A "+r+","+r+" 0 0 1 ";return"M-"+e+",0"+i+"0,"+e+i+e+",0"+i+"0,-"+e+i+"-"+e+",0Z"}},"diamond-tall":{n:23,f:function(t){var e=n.round(.7*t,2),r=n.round(1.4*t,2);return"M0,"+r+"L"+e+",0L0,-"+r+"L-"+e+",0Z"}},"diamond-wide":{n:24,f:function(t){var e=n.round(1.4*t,2),r=n.round(.7*t,2);return"M0,"+r+"L"+e+",0L0,-"+r+"L-"+e+",0Z"}},hourglass:{n:25,f:function(t){var e=n.round(t,2);return"M"+e+","+e+"H-"+e+"L"+e+",-"+e+"H-"+e+"Z"},noDot:!0},bowtie:{n:26,f:function(t){var e=n.round(t,2);return"M"+e+","+e+"V-"+e+"L-"+e+","+e+"V-"+e+"Z"},noDot:!0},"circle-cross":{n:27,f:function(t){var e=n.round(t,2);return"M0,"+e+"V-"+e+"M"+e+",0H-"+e+"M"+e+",0A"+e+","+e+" 0 1,1 0,-"+e+"A"+e+","+e+" 0 0,1 "+e+",0Z"},needLine:!0,noDot:!0},"circle-x":{n:28,f:function(t){var e=n.round(t,2),r=n.round(t/Math.sqrt(2),2);return"M"+r+","+r+"L-"+r+",-"+r+"M"+r+",-"+r+"L-"+r+","+r+"M"+e+",0A"+e+","+e+" 0 1,1 0,-"+e+"A"+e+","+e+" 0 0,1 "+e+",0Z"},needLine:!0,noDot:!0},"square-cross":{n:29,f:function(t){var e=n.round(t,2);return"M0,"+e+"V-"+e+"M"+e+",0H-"+e+"M"+e+","+e+"H-"+e+"V-"+e+"H"+e+"Z"},needLine:!0,noDot:!0},"square-x":{n:30,f:function(t){var e=n.round(t,2);return"M"+e+","+e+"L-"+e+",-"+e+"M"+e+",-"+e+"L-"+e+","+e+"M"+e+","+e+"H-"+e+"V-"+e+"H"+e+"Z"},needLine:!0,noDot:!0},"diamond-cross":{n:31,f:function(t){var e=n.round(1.3*t,2);return"M"+e+",0L0,"+e+"L-"+e+",0L0,-"+e+"ZM0,-"+e+"V"+e+"M-"+e+",0H"+e},needLine:!0,noDot:!0},"diamond-x":{n:32,f:function(t){var e=n.round(1.3*t,2),r=n.round(.65*t,2);return"M"+e+",0L0,"+e+"L-"+e+",0L0,-"+e+"ZM-"+r+",-"+r+"L"+r+","+r+"M-"+r+","+r+"L"+r+",-"+r},needLine:!0,noDot:!0},"cross-thin":{n:33,f:function(t){var e=n.round(1.4*t,2);return"M0,"+e+"V-"+e+"M"+e+",0H-"+e},needLine:!0,noDot:!0,noFill:!0},"x-thin":{n:34,f:function(t){var e=n.round(t,2);return"M"+e+","+e+"L-"+e+",-"+e+"M"+e+",-"+e+"L-"+e+","+e},needLine:!0,noDot:!0,noFill:!0},asterisk:{n:35,f:function(t){var e=n.round(1.2*t,2),r=n.round(.85*t,2);return"M0,"+e+"V-"+e+"M"+e+",0H-"+e+"M"+r+","+r+"L-"+r+",-"+r+"M"+r+",-"+r+"L-"+r+","+r},needLine:!0,noDot:!0,noFill:!0},hash:{n:36,f:function(t){var e=n.round(t/2,2),r=n.round(t,2);return"M"+e+","+r+"V-"+r+"m-"+r+",0V"+r+"M"+r+","+e+"H-"+r+"m0,-"+r+"H"+r},needLine:!0,noFill:!0},"y-up":{n:37,f:function(t){var e=n.round(1.2*t,2),r=n.round(1.6*t,2),i=n.round(.8*t,2);return"M-"+e+","+i+"L0,0M"+e+","+i+"L0,0M0,-"+r+"L0,0"},needLine:!0,noDot:!0,noFill:!0},"y-down":{n:38,f:function(t){var e=n.round(1.2*t,2),r=n.round(1.6*t,2),i=n.round(.8*t,2);return"M-"+e+",-"+i+"L0,0M"+e+",-"+i+"L0,0M0,"+r+"L0,0"},needLine:!0,noDot:!0,noFill:!0},"y-left":{n:39,f:function(t){var e=n.round(1.2*t,2),r=n.round(1.6*t,2),i=n.round(.8*t,2);return"M"+i+","+e+"L0,0M"+i+",-"+e+"L0,0M-"+r+",0L0,0"},needLine:!0,noDot:!0,noFill:!0},"y-right":{n:40,f:function(t){var e=n.round(1.2*t,2),r=n.round(1.6*t,2),i=n.round(.8*t,2);return"M-"+i+","+e+"L0,0M-"+i+",-"+e+"L0,0M"+r+",0L0,0"},needLine:!0,noDot:!0,noFill:!0},"line-ew":{n:41,f:function(t){var e=n.round(1.4*t,2);return"M"+e+",0H-"+e},needLine:!0,noDot:!0,noFill:!0},"line-ns":{n:42,f:function(t){var e=n.round(1.4*t,2);return"M0,"+e+"V-"+e},needLine:!0,noDot:!0,noFill:!0},"line-ne":{n:43,f:function(t){var e=n.round(t,2);return"M"+e+",-"+e+"L-"+e+","+e},needLine:!0,noDot:!0,noFill:!0},"line-nw":{n:44,f:function(t){var e=n.round(t,2);return"M"+e+","+e+"L-"+e+",-"+e},needLine:!0,noDot:!0,noFill:!0}}},{d3:131}],559:[function(t,e,r){"use strict";e.exports={visible:{valType:"boolean",editType:"calc"},type:{valType:"enumerated",values:["percent","constant","sqrt","data"],editType:"calc"},symmetric:{valType:"boolean",editType:"calc"},array:{valType:"data_array",editType:"calc"},arrayminus:{valType:"data_array",editType:"calc"},value:{valType:"number",min:0,dflt:10,editType:"calc"},valueminus:{valType:"number",min:0,dflt:10,editType:"calc"},traceref:{valType:"integer",min:0,dflt:0,editType:"style"},tracerefminus:{valType:"integer",min:0,dflt:0,editType:"style"},copy_ystyle:{valType:"boolean",editType:"plot"},copy_zstyle:{valType:"boolean",editType:"style"},color:{valType:"color",editType:"style"},thickness:{valType:"number",min:0,dflt:2,editType:"style"},width:{valType:"number",min:0,editType:"plot"},editType:"calc",_deprecated:{opacity:{valType:"number",editType:"style"}}}},{}],560:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../../registry"),a=t("../../plots/cartesian/axes"),o=t("./compute_error");function s(t,e,r,i){var s=e["error_"+i]||{},l=[];if(s.visible&&-1!==["linear","log"].indexOf(r.type)){for(var c=o(s),u=0;u0;t.each(function(t){var u,f=t[0].trace,h=f.error_x||{},p=f.error_y||{};f.ids&&(u=function(t){return t.id});var d=o.hasMarkers(f)&&f.marker.maxdisplayed>0;p.visible||h.visible||(t=[]);var g=n.select(this).selectAll("g.errorbar").data(t,u);if(g.exit().remove(),t.length){h.visible||g.selectAll("path.xerror").remove(),p.visible||g.selectAll("path.yerror").remove(),g.style("opacity",1);var m=g.enter().append("g").classed("errorbar",!0);c&&m.style("opacity",0).transition().duration(r.duration).style("opacity",1),a.setClipUrl(g,e.layerClipId),g.each(function(t){var e=n.select(this),a=function(t,e,r){var n={x:e.c2p(t.x),y:r.c2p(t.y)};void 0!==t.yh&&(n.yh=r.c2p(t.yh),n.ys=r.c2p(t.ys),i(n.ys)||(n.noYS=!0,n.ys=r.c2p(t.ys,!0)));void 0!==t.xh&&(n.xh=e.c2p(t.xh),n.xs=e.c2p(t.xs),i(n.xs)||(n.noXS=!0,n.xs=e.c2p(t.xs,!0)));return n}(t,s,l);if(!d||t.vis){var o,u=e.select("path.yerror");if(p.visible&&i(a.x)&&i(a.yh)&&i(a.ys)){var f=p.width;o="M"+(a.x-f)+","+a.yh+"h"+2*f+"m-"+f+",0V"+a.ys,a.noYS||(o+="m-"+f+",0h"+2*f),!u.size()?u=e.append("path").style("vector-effect","non-scaling-stroke").classed("yerror",!0):c&&(u=u.transition().duration(r.duration).ease(r.easing)),u.attr("d",o)}else u.remove();var g=e.select("path.xerror");if(h.visible&&i(a.y)&&i(a.xh)&&i(a.xs)){var m=(h.copy_ystyle?p:h).width;o="M"+a.xh+","+(a.y-m)+"v"+2*m+"m0,-"+m+"H"+a.xs,a.noXS||(o+="m0,-"+m+"v"+2*m),!g.size()?g=e.append("path").style("vector-effect","non-scaling-stroke").classed("xerror",!0):c&&(g=g.transition().duration(r.duration).ease(r.easing)),g.attr("d",o)}else g.remove()}})}})}},{"../../traces/scatter/subtypes":1012,"../drawing":557,d3:131,"fast-isnumeric":197}],565:[function(t,e,r){"use strict";var n=t("d3"),i=t("../color");e.exports=function(t){t.each(function(t){var e=t[0].trace,r=e.error_y||{},a=e.error_x||{},o=n.select(this);o.selectAll("path.yerror").style("stroke-width",r.thickness+"px").call(i.stroke,r.color),a.copy_ystyle&&(a=r),o.selectAll("path.xerror").style("stroke-width",a.thickness+"px").call(i.stroke,a.color)})}},{"../color":532,d3:131}],566:[function(t,e,r){"use strict";var n=t("../../plots/font_attributes");e.exports={hoverlabel:{bgcolor:{valType:"color",arrayOk:!0,editType:"none"},bordercolor:{valType:"color",arrayOk:!0,editType:"none"},font:n({arrayOk:!0,editType:"none"}),namelength:{valType:"integer",min:-1,arrayOk:!0,editType:"none"},editType:"calc"}}},{"../../plots/font_attributes":732}],567:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../../registry");function a(t,e,r,i){i=i||n.identity,Array.isArray(t)&&(e[0][r]=i(t))}e.exports=function(t){var e=t.calcdata,r=t._fullLayout;function o(t){return function(e){return n.coerceHoverinfo({hoverinfo:e},{_module:t._module},r)}}for(var s=0;s=0&&r.index-1&&o.length>y&&(o=y>3?o.substr(0,y-3)+"...":o.substr(0,y))}void 0!==t.zLabel?(void 0!==t.xLabel&&(c+="x: "+t.xLabel+"
"),void 0!==t.yLabel&&(c+="y: "+t.yLabel+"
"),c+=(c?"z: ":"")+t.zLabel):L&&t[i+"Label"]===M?c=t[("x"===i?"y":"x")+"Label"]||"":void 0===t.xLabel?void 0!==t.yLabel&&(c=t.yLabel):c=void 0===t.yLabel?t.xLabel:"("+t.xLabel+", "+t.yLabel+")",!t.text&&0!==t.text||Array.isArray(t.text)||(c+=(c?"
":"")+t.text),void 0!==t.extraText&&(c+=(c?"
":"")+t.extraText),""===c&&(""===o&&e.remove(),c=o);var x=e.select("text.nums").call(u.font,t.fontFamily||d,t.fontSize||g,t.fontColor||m).text(c).attr("data-notex",1).call(l.positionText,0,0).call(l.convertToTspans,r),b=e.select("text.name"),_=0;o&&o!==c?(b.call(u.font,t.fontFamily||d,t.fontSize||g,p).text(o).attr("data-notex",1).call(l.positionText,0,0).call(l.convertToTspans,r),_=b.node().getBoundingClientRect().width+2*k):(b.remove(),e.select("rect").remove()),e.select("path").style({fill:p,stroke:m});var A,T,z=x.node().getBoundingClientRect(),P=t.xa._offset+(t.x0+t.x1)/2,D=t.ya._offset+(t.y0+t.y1)/2,O=Math.abs(t.x1-t.x0),I=Math.abs(t.y1-t.y0),R=z.width+w+k+_;t.ty0=S-z.top,t.bx=z.width+2*k,t.by=z.height+2*k,t.anchor="start",t.txwidth=z.width,t.tx2width=_,t.offset=0,a?(t.pos=P,A=D+I/2+R<=E,T=D-I/2-R>=0,"top"!==t.idealAlign&&A||!T?A?(D+=I/2,t.anchor="start"):t.anchor="middle":(D-=I/2,t.anchor="end")):(t.pos=D,A=P+O/2+R<=C,T=P-O/2-R>=0,"left"!==t.idealAlign&&A||!T?A?(P+=O/2,t.anchor="start"):t.anchor="middle":(P-=O/2,t.anchor="end")),x.attr("text-anchor",t.anchor),_&&b.attr("text-anchor",t.anchor),e.attr("transform","translate("+P+","+D+")"+(a?"rotate("+v+")":""))}),R}function A(t,e){t.each(function(t){var r=n.select(this);if(t.del)r.remove();else{var i="end"===t.anchor?-1:1,a=r.select("text.nums"),o={start:1,end:-1,middle:0}[t.anchor],s=o*(w+k),c=s+o*(t.txwidth+k),f=0,h=t.offset;"middle"===t.anchor&&(s-=t.tx2width/2,c+=t.txwidth/2+k),e&&(h*=-_,f=t.offset*b),r.select("path").attr("d","middle"===t.anchor?"M-"+(t.bx/2+t.tx2width/2)+","+(h-t.by/2)+"h"+t.bx+"v"+t.by+"h-"+t.bx+"Z":"M0,0L"+(i*w+f)+","+(w+h)+"v"+(t.by/2-w)+"h"+i*t.bx+"v-"+t.by+"H"+(i*w+f)+"V"+(h-w)+"Z"),a.call(l.positionText,s+f,h+t.ty0-t.by/2+k),t.tx2width&&(r.select("text.name").call(l.positionText,c+o*k+f,h+t.ty0-t.by/2+k),r.select("rect").call(u.setRect,c+(o-1)*t.tx2width/2+f,h-t.by/2-1,t.tx2width,t.by+2))}})}function T(t,e){var r=t.index,n=t.trace||{},i=t.cd[0],a=t.cd[r]||{},s=Array.isArray(r)?function(t,e){return o.castOption(i,r,t)||o.extractOption({},n,"",e)}:function(t,e){return o.extractOption(a,n,t,e)};function l(e,r,n){var i=s(r,n);i&&(t[e]=i)}if(l("hoverinfo","hi","hoverinfo"),l("color","hbg","hoverlabel.bgcolor"),l("borderColor","hbc","hoverlabel.bordercolor"),l("fontFamily","htf","hoverlabel.font.family"),l("fontSize","hts","hoverlabel.font.size"),l("fontColor","htc","hoverlabel.font.color"),l("nameLength","hnl","hoverlabel.namelength"),t.posref="y"===e?t.xa._offset+(t.x0+t.x1)/2:t.ya._offset+(t.y0+t.y1)/2,t.x0=o.constrain(t.x0,0,t.xa._length),t.x1=o.constrain(t.x1,0,t.xa._length),t.y0=o.constrain(t.y0,0,t.ya._length),t.y1=o.constrain(t.y1,0,t.ya._length),void 0!==t.xLabelVal&&(t.xLabel="xLabel"in t?t.xLabel:p.hoverLabelText(t.xa,t.xLabelVal),t.xVal=t.xa.c2d(t.xLabelVal)),void 0!==t.yLabelVal&&(t.yLabel="yLabel"in t?t.yLabel:p.hoverLabelText(t.ya,t.yLabelVal),t.yVal=t.ya.c2d(t.yLabelVal)),void 0!==t.zLabelVal&&void 0===t.zLabel&&(t.zLabel=String(t.zLabelVal)),!(isNaN(t.xerr)||"log"===t.xa.type&&t.xerr<=0)){var c=p.tickText(t.xa,t.xa.c2l(t.xerr),"hover").text;void 0!==t.xerrneg?t.xLabel+=" +"+c+" / -"+p.tickText(t.xa,t.xa.c2l(t.xerrneg),"hover").text:t.xLabel+=" \xb1 "+c,"x"===e&&(t.distance+=1)}if(!(isNaN(t.yerr)||"log"===t.ya.type&&t.yerr<=0)){var u=p.tickText(t.ya,t.ya.c2l(t.yerr),"hover").text;void 0!==t.yerrneg?t.yLabel+=" +"+u+" / -"+p.tickText(t.ya,t.ya.c2l(t.yerrneg),"hover").text:t.yLabel+=" \xb1 "+u,"y"===e&&(t.distance+=1)}var f=t.hoverinfo||t.trace.hoverinfo;return"all"!==f&&(-1===(f=Array.isArray(f)?f:f.split("+")).indexOf("x")&&(t.xLabel=void 0),-1===f.indexOf("y")&&(t.yLabel=void 0),-1===f.indexOf("z")&&(t.zLabel=void 0),-1===f.indexOf("text")&&(t.text=void 0),-1===f.indexOf("name")&&(t.name=void 0)),t}function S(t,e){var r,n,i=e.container,o=e.fullLayout,s=e.event,l=!!t.hLinePoint,c=!!t.vLinePoint;if(i.selectAll(".spikeline").remove(),c||l){var h=f.combine(o.plot_bgcolor,o.paper_bgcolor);if(l){var p,d,g=t.hLinePoint;r=g&&g.xa,"cursor"===(n=g&&g.ya).spikesnap?(p=s.pointerX,d=s.pointerY):(p=r._offset+g.x,d=n._offset+g.y);var m,v,y=a.readability(g.color,h)<1.5?f.contrast(h):g.color,x=n.spikemode,b=n.spikethickness,_=n.spikecolor||y,w=n._boundingBox,k=(w.left+w.right)/2w[0]._length||tt<0||tt>k[0]._length)return h.unhoverRaw(t,e)}if(e.pointerX=$+w[0]._offset,e.pointerY=tt+k[0]._offset,I="xval"in e?g.flat(l,e.xval):g.p2c(w,$),R="yval"in e?g.flat(l,e.yval):g.p2c(k,tt),!i(I[0])||!i(R[0]))return o.warn("Fx.hover failed",e,t),h.unhoverRaw(t,e)}var nt=1/0;for(F=0;FY&&(J.splice(0,Y),nt=J[0].distance),y&&0!==Z&&0===J.length){W.distance=Z,W.index=!1;var lt=j._module.hoverPoints(W,H,G,"closest",u._hoverlayer);if(lt&&(lt=lt.filter(function(t){return t.spikeDistance<=Z})),lt&<.length){var ct,ut=lt.filter(function(t){return t.xa.showspikes});if(ut.length){var ft=ut[0];i(ft.x0)&&i(ft.y0)&&(ct=gt(ft),(!Q.vLinePoint||Q.vLinePoint.spikeDistance>ct.spikeDistance)&&(Q.vLinePoint=ct))}var ht=lt.filter(function(t){return t.ya.showspikes});if(ht.length){var pt=ht[0];i(pt.x0)&&i(pt.y0)&&(ct=gt(pt),(!Q.hLinePoint||Q.hLinePoint.spikeDistance>ct.spikeDistance)&&(Q.hLinePoint=ct))}}}}function dt(t,e){for(var r,n=null,i=1/0,a=0;a1,Ct=f.combine(u.plot_bgcolor||f.background,u.paper_bgcolor),Et={hovermode:O,rotateLabels:St,bgColor:Ct,container:u._hoverlayer,outerContainer:u._paperdiv,commonLabelOpts:u.hoverlabel,hoverdistance:u.hoverdistance},Lt=M(J,Et,t);if(function(t,e,r){var n,i,a,o,s,l,c,u=0,f=t.map(function(t,n){var i=t[e];return[{i:n,dp:0,pos:t.pos,posref:t.posref,size:t.by*("x"===i._id.charAt(0)?x:1)/2,pmin:0,pmax:"x"===i._id.charAt(0)?r.width:r.height}]}).sort(function(t,e){return t[0].posref-e[0].posref});function h(t){var e=t[0],r=t[t.length-1];if(i=e.pmin-e.pos-e.dp+e.size,a=r.pos+r.dp+r.size-e.pmax,i>.01){for(s=t.length-1;s>=0;s--)t[s].dp+=i;n=!1}if(!(a<.01)){if(i<-.01){for(s=t.length-1;s>=0;s--)t[s].dp-=a;n=!1}if(n){var c=0;for(o=0;oe.pmax&&c++;for(o=t.length-1;o>=0&&!(c<=0);o--)(l=t[o]).pos>e.pmax-1&&(l.del=!0,c--);for(o=0;o=0;s--)t[s].dp-=a;for(o=t.length-1;o>=0&&!(c<=0);o--)(l=t[o]).pos+l.dp+l.size>e.pmax&&(l.del=!0,c--)}}}for(;!n&&u<=t.length;){for(u++,n=!0,o=0;o.01&&g.pmin===m.pmin&&g.pmax===m.pmax){for(s=d.length-1;s>=0;s--)d[s].dp+=i;for(p.push.apply(p,d),f.splice(o+1,1),c=0,s=p.length-1;s>=0;s--)c+=p[s].dp;for(a=c/p.length,s=p.length-1;s>=0;s--)p[s].dp-=a;n=!1}else o++}f.forEach(h)}for(o=f.length-1;o>=0;o--){var v=f[o];for(s=v.length-1;s>=0;s--){var y=v[s],b=t[y.i];b.offset=y.dp,b.del=y.del}}}(J,St?"xa":"ya",u),A(Lt,St),e.target&&e.target.tagName){var zt=d.getComponentMethod("annotations","hasClickToShow")(t,At);c(n.select(e.target),zt?"pointer":"")}if(!e.target||a||!function(t,e,r){if(!r||r.length!==t._hoverdata.length)return!0;for(var n=r.length-1;n>=0;n--){var i=r[n],a=t._hoverdata[n];if(i.curveNumber!==a.curveNumber||String(i.pointNumber)!==String(a.pointNumber))return!0}return!1}(t,0,Mt))return;Mt&&t.emit("plotly_unhover",{event:e,points:Mt});t.emit("plotly_hover",{event:e,points:t._hoverdata,xaxes:w,yaxes:k,xvals:I,yvals:R})}(t,e,r,a)})},r.loneHover=function(t,e){var r={color:t.color||f.defaultLine,x0:t.x0||t.x||0,x1:t.x1||t.x||0,y0:t.y0||t.y||0,y1:t.y1||t.y||0,xLabel:t.xLabel,yLabel:t.yLabel,zLabel:t.zLabel,text:t.text,name:t.name,idealAlign:t.idealAlign,borderColor:t.borderColor,fontFamily:t.fontFamily,fontSize:t.fontSize,fontColor:t.fontColor,trace:{index:0,hoverinfo:""},xa:{_offset:0},ya:{_offset:0},index:0},i=n.select(e.container),a=e.outerContainer?n.select(e.outerContainer):i,o={hovermode:"closest",rotateLabels:!1,bgColor:e.bgColor||f.background,container:i,outerContainer:a},s=M([r],o,e.gd);return A(s,o.rotateLabels),s.node()}},{"../../lib":660,"../../lib/events":648,"../../lib/override_cursor":671,"../../lib/svg_text_utils":684,"../../plots/cartesian/axes":706,"../../registry":790,"../color":532,"../dragelement":554,"../drawing":557,"./constants":569,"./helpers":571,d3:131,"fast-isnumeric":197,tinycolor2:473}],573:[function(t,e,r){"use strict";var n=t("../../lib");e.exports=function(t,e,r,i){r("hoverlabel.bgcolor",(i=i||{}).bgcolor),r("hoverlabel.bordercolor",i.bordercolor),r("hoverlabel.namelength",i.namelength),n.coerceFont(r,"hoverlabel.font",i.font)}},{"../../lib":660}],574:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../lib"),a=t("../dragelement"),o=t("./helpers"),s=t("./layout_attributes");e.exports={moduleType:"component",name:"fx",constants:t("./constants"),schema:{layout:s},attributes:t("./attributes"),layoutAttributes:s,supplyLayoutGlobalDefaults:t("./layout_global_defaults"),supplyDefaults:t("./defaults"),supplyLayoutDefaults:t("./layout_defaults"),calc:t("./calc"),getDistanceFunction:o.getDistanceFunction,getClosest:o.getClosest,inbox:o.inbox,quadrature:o.quadrature,appendArrayPointValue:o.appendArrayPointValue,castHoverOption:function(t,e,r){return i.castOption(t,e,"hoverlabel."+r)},castHoverinfo:function(t,e,r){return i.castOption(t,r,"hoverinfo",function(r){return i.coerceHoverinfo({hoverinfo:r},{_module:t._module},e)})},hover:t("./hover").hover,unhover:a.unhover,loneHover:t("./hover").loneHover,loneUnhover:function(t){var e=i.isD3Selection(t)?t:n.select(t);e.selectAll("g.hovertext").remove(),e.selectAll(".spikeline").remove()},click:t("./click")}},{"../../lib":660,"../dragelement":554,"./attributes":566,"./calc":567,"./click":568,"./constants":569,"./defaults":570,"./helpers":571,"./hover":572,"./layout_attributes":575,"./layout_defaults":576,"./layout_global_defaults":577,d3:131}],575:[function(t,e,r){"use strict";var n=t("./constants"),i=t("../../plots/font_attributes")({editType:"none"});i.family.dflt=n.HOVERFONT,i.size.dflt=n.HOVERFONTSIZE,e.exports={dragmode:{valType:"enumerated",values:["zoom","pan","select","lasso","orbit","turntable"],dflt:"zoom",editType:"modebar"},hovermode:{valType:"enumerated",values:["x","y","closest",!1],editType:"modebar"},hoverdistance:{valType:"integer",min:-1,dflt:20,editType:"none"},spikedistance:{valType:"integer",min:-1,dflt:20,editType:"none"},hoverlabel:{bgcolor:{valType:"color",editType:"none"},bordercolor:{valType:"color",editType:"none"},font:i,namelength:{valType:"integer",min:-1,dflt:15,editType:"none"},editType:"none"},selectdirection:{valType:"enumerated",values:["h","v","d","any"],dflt:"any",editType:"none"}}},{"../../plots/font_attributes":732,"./constants":569}],576:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("./layout_attributes");e.exports=function(t,e,r){function a(r,a){return n.coerce(t,e,i,r,a)}var o;"select"===a("dragmode")&&a("selectdirection"),e._has("cartesian")?(e._isHoriz=function(t){for(var e=!0,r=0;r1){f||h||p||"independent"===k("pattern")&&(f=!0),g._hasSubplotGrid=f;var y,x,b="top to bottom"===k("roworder"),_=f?.2:.1,w=f?.3:.1;d&&e._splomGridDflt&&(y=e._splomGridDflt.xside,x=e._splomGridDflt.yside),g._domains={x:c("x",k,_,y,v),y:c("y",k,w,x,m,b)},e.grid=g}}function k(t,e){return n.coerce(r,g,s,t,e)}},contentDefaults:function(t,e){var r=e.grid;if(r&&r._domains){var n,i,a,o,s,c,f,h=t.grid||{},p=e._subplots,d=r._hasSubplotGrid,g=r.rows,m=r.columns,v="independent"===r.pattern,y=r._axisMap={};if(d){var x=h.subplots||[];c=r.subplots=new Array(g);var b=1;for(n=0;n=2/3},r.isCenterAnchor=function(t){return"center"===t.xanchor||"auto"===t.xanchor&&t.x>1/3&&t.x<2/3},r.isBottomAnchor=function(t){return"bottom"===t.yanchor||"auto"===t.yanchor&&t.y<=1/3},r.isMiddleAnchor=function(t){return"middle"===t.yanchor||"auto"===t.yanchor&&t.y>1/3&&t.y<2/3}},{}],585:[function(t,e,r){"use strict";var n=t("../../plots/font_attributes"),i=t("../color/attributes");e.exports={bgcolor:{valType:"color",editType:"legend"},bordercolor:{valType:"color",dflt:i.defaultLine,editType:"legend"},borderwidth:{valType:"number",min:0,dflt:0,editType:"legend"},font:n({editType:"legend"}),orientation:{valType:"enumerated",values:["v","h"],dflt:"v",editType:"legend"},traceorder:{valType:"flaglist",flags:["reversed","grouped"],extras:["normal"],editType:"legend"},tracegroupgap:{valType:"number",min:0,dflt:10,editType:"legend"},x:{valType:"number",min:-2,max:3,dflt:1.02,editType:"legend"},xanchor:{valType:"enumerated",values:["auto","left","center","right"],dflt:"left",editType:"legend"},y:{valType:"number",min:-2,max:3,dflt:1,editType:"legend"},yanchor:{valType:"enumerated",values:["auto","top","middle","bottom"],dflt:"auto",editType:"legend"},editType:"legend"}},{"../../plots/font_attributes":732,"../color/attributes":531}],586:[function(t,e,r){"use strict";e.exports={scrollBarWidth:6,scrollBarMinHeight:20,scrollBarColor:"#808BA4",scrollBarMargin:4}},{}],587:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("../../lib"),a=t("./attributes"),o=t("../../plots/layout_attributes"),s=t("./helpers");e.exports=function(t,e,r){for(var l,c,u,f,h=t.legend||{},p={},d=0,g="normal",m=0;m1)){if(e.legend=p,y("bgcolor",e.paper_bgcolor),y("bordercolor"),y("borderwidth"),i.coerceFont(y,"font",e.font),y("orientation"),"h"===p.orientation){var x=t.xaxis;x&&x.rangeslider&&x.rangeslider.visible?(l=0,u="left",c=1.1,f="bottom"):(l=0,u="left",c=-.1,f="top")}y("traceorder",g),s.isGrouped(e.legend)&&y("tracegroupgap"),y("x",l),y("xanchor",u),y("y",c),y("yanchor",f),i.noneOrAll(h,p,["x","y"])}}},{"../../lib":660,"../../plots/layout_attributes":759,"../../registry":790,"./attributes":585,"./helpers":591}],588:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../lib"),a=t("../../plots/plots"),o=t("../../registry"),s=t("../../lib/events"),l=t("../dragelement"),c=t("../drawing"),u=t("../color"),f=t("../../lib/svg_text_utils"),h=t("./handle_click"),p=t("./constants"),d=t("../../constants/interactions"),g=t("../../constants/alignment"),m=g.LINE_SPACING,v=g.FROM_TL,y=g.FROM_BR,x=t("./get_legend_data"),b=t("./style"),_=t("./helpers"),w=t("./anchor_utils"),k=d.DBLCLICKDELAY;function M(t,e,r,n,i){var a=r.data()[0][0].trace,o={event:i,node:r.node(),curveNumber:a.index,expandedIndex:a._expandedIndex,data:t.data,layout:t.layout,frames:t._transitionData._frames,config:t._context,fullData:t._fullData,fullLayout:t._fullLayout};if(a._group&&(o.group=a._group),"pie"===a.type&&(o.label=r.datum()[0].label),!1!==s.triggerHandler(t,"plotly_legendclick",o))if(1===n)e._clickTimeout=setTimeout(function(){h(r,t,n)},k);else if(2===n){e._clickTimeout&&clearTimeout(e._clickTimeout),t._legendMouseDownTime=0,!1!==s.triggerHandler(t,"plotly_legenddoubleclick",o)&&h(r,t,n)}}function A(t,e,r){var n=t.data()[0][0],a=e._fullLayout,s=n.trace,l=o.traceIs(s,"pie"),u=s.index,h=l?n.label:s.name,p=e._context.edits.legendText&&!l,d=i.ensureSingle(t,"text","legendtext");function g(r){f.convertToTspans(r,e,function(){!function(t,e){var r=t.data()[0][0];if(!r.trace.showlegend)return void t.remove();var n,i,a=t.select("g[class*=math-group]"),o=a.node(),s=e._fullLayout.legend.font.size*m;if(o){var l=c.bBox(o);n=l.height,i=l.width,c.setTranslate(a,0,n/4)}else{var u=t.select(".legendtext"),h=f.lineCount(u),p=u.node();n=s*h,i=p?c.bBox(p).width:0;var d=s*(.3+(1-h)/2);f.positionText(u,40,d)}n=Math.max(n,16)+3,r.height=n,r.width=i}(t,e)})}d.attr("text-anchor","start").classed("user-select-none",!0).call(c.font,a.legend.font).text(p?T(h,r):h),p?d.call(f.makeEditable,{gd:e,text:h}).call(g).on("edit",function(t){this.text(T(t,r)).call(g);var a=n.trace._fullInput||{},s={};if(o.hasTransform(a,"groupby")){var l=o.getTransformIndices(a,"groupby"),c=l[l.length-1],f=i.keyedContainer(a,"transforms["+c+"].styles","target","value.name");f.set(n.trace._group,t),s=f.constructUpdate()}else s.name=t;return o.call("restyle",e,s,u)}):g(d)}function T(t,e){var r=Math.max(4,e);if(t&&t.trim().length>=r/2)return t;for(var n=r-(t=t||"").length;n>0;n--)t+=" ";return t}function S(t,e){var r,a=1,o=i.ensureSingle(t,"rect","legendtoggle",function(t){t.style("cursor","pointer").attr("pointer-events","all").call(u.fill,"rgba(0,0,0,0)")});o.on("mousedown",function(){(r=(new Date).getTime())-e._legendMouseDownTimek&&(a=Math.max(a-1,1)),M(e,r,t,a,n.event)}})}function C(t,e,r){var i=t._fullLayout,a=i.legend,o=a.borderwidth,s=_.isGrouped(a),l=0;if(a._width=0,a._height=0,_.isVertical(a))s&&e.each(function(t,e){c.setTranslate(this,0,e*a.tracegroupgap)}),r.each(function(t){var e=t[0],r=e.height,n=e.width;c.setTranslate(this,o,5+o+a._height+r/2),a._height+=r,a._width=Math.max(a._width,n)}),a._width+=45+2*o,a._height+=10+2*o,s&&(a._height+=(a._lgroupsLength-1)*a.tracegroupgap),l=40;else if(s){for(var u=[a._width],f=e.data(),h=0,p=f.length;ho+w-k,r.each(function(t){var e=t[0],r=m?40+t[0].width:x;o+b+k+r>i.width-(i.margin.r+i.margin.l)&&(b=0,v+=y,a._height=a._height+y,y=0),c.setTranslate(this,o+b,5+o+e.height/2+v),a._width+=k+r,a._height=Math.max(a._height,e.height),b+=k+r,y=Math.max(e.height,y)}),a._width+=2*o,a._height+=10+2*o}a._width=Math.ceil(a._width),a._height=Math.ceil(a._height),r.each(function(e){var r=e[0],i=n.select(this).select(".legendtoggle");c.setRect(i,0,-r.height/2,(t._context.edits.legendText?0:a._width)+l,r.height)})}function E(t){var e=t._fullLayout.legend,r="left";w.isRightAnchor(e)?r="right":w.isCenterAnchor(e)&&(r="center");var n="top";w.isBottomAnchor(e)?n="bottom":w.isMiddleAnchor(e)&&(n="middle"),a.autoMargin(t,"legend",{x:e.x,y:e.y,l:e._width*v[r],r:e._width*y[r],b:e._height*y[n],t:e._height*v[n]})}e.exports=function(t){var e=t._fullLayout,r="legend"+e._uid;if(e._infolayer&&t.calcdata){t._legendMouseDownTime||(t._legendMouseDownTime=0);var s=e.legend,f=e.showlegend&&x(t.calcdata,s),h=e.hiddenlabels||[];if(!e.showlegend||!f.length)return e._infolayer.selectAll(".legend").remove(),e._topdefs.select("#"+r).remove(),void a.autoMargin(t,"legend");for(var d=0,g=0;gN?function(t){var e=t._fullLayout.legend,r="left";w.isRightAnchor(e)?r="right":w.isCenterAnchor(e)&&(r="center");a.autoMargin(t,"legend",{x:e.x,y:.5,l:e._width*v[r],r:e._width*y[r],b:0,t:0})}(t):E(t);var j=e._size,V=j.l+j.w*s.x,U=j.t+j.h*(1-s.y);w.isRightAnchor(s)?V-=s._width:w.isCenterAnchor(s)&&(V-=s._width/2),w.isBottomAnchor(s)?U-=s._height:w.isMiddleAnchor(s)&&(U-=s._height/2);var q=s._width,H=j.w;q>H?(V=j.l,q=H):(V+q>F&&(V=F-q),V<0&&(V=0),q=Math.min(F-V,s._width));var G,W,Y,X,Z=s._height,J=j.h;if(Z>J?(U=j.t,Z=J):(U+Z>N&&(U=N-Z),U<0&&(U=0),Z=Math.min(N-U,s._height)),c.setTranslate(z,V,U),I.on(".drag",null),z.on("wheel",null),s._height<=Z||t._context.staticPlot)D.attr({width:q-s.borderwidth,height:Z-s.borderwidth,x:s.borderwidth/2,y:s.borderwidth/2}),c.setTranslate(O,0,0),P.select("rect").attr({width:q-2*s.borderwidth,height:Z-2*s.borderwidth,x:s.borderwidth,y:s.borderwidth}),c.setClipUrl(O,r),c.setRect(I,0,0,0,0),delete s._scrollY;else{var K,Q,$=Math.max(p.scrollBarMinHeight,Z*Z/s._height),tt=Z-$-2*p.scrollBarMargin,et=s._height-Z,rt=tt/et,nt=Math.min(s._scrollY||0,et);D.attr({width:q-2*s.borderwidth+p.scrollBarWidth+p.scrollBarMargin,height:Z-s.borderwidth,x:s.borderwidth/2,y:s.borderwidth/2}),P.select("rect").attr({width:q-2*s.borderwidth+p.scrollBarWidth+p.scrollBarMargin,height:Z-2*s.borderwidth,x:s.borderwidth,y:s.borderwidth+nt}),c.setClipUrl(O,r),at(nt,$,rt),z.on("wheel",function(){at(nt=i.constrain(s._scrollY+n.event.deltaY/tt*et,0,et),$,rt),0!==nt&&nt!==et&&n.event.preventDefault()});var it=n.behavior.drag().on("dragstart",function(){K=n.event.sourceEvent.clientY,Q=nt}).on("drag",function(){var t=n.event.sourceEvent;2===t.buttons||t.ctrlKey||at(nt=i.constrain((t.clientY-K)/rt+Q,0,et),$,rt)});I.call(it)}if(t._context.edits.legendPosition)z.classed("cursor-move",!0),l.init({element:z.node(),gd:t,prepFn:function(){var t=c.getTranslate(z);Y=t.x,X=t.y},moveFn:function(t,e){var r=Y+t,n=X+e;c.setTranslate(z,r,n),G=l.align(r,0,j.l,j.l+j.w,s.xanchor),W=l.align(n,0,j.t+j.h,j.t,s.yanchor)},doneFn:function(){void 0!==G&&void 0!==W&&o.call("relayout",t,{"legend.x":G,"legend.y":W})},clickFn:function(r,n){var i=e._infolayer.selectAll("g.traces").filter(function(){var t=this.getBoundingClientRect();return n.clientX>=t.left&&n.clientX<=t.right&&n.clientY>=t.top&&n.clientY<=t.bottom});i.size()>0&&M(t,z,i,r,n)}})}function at(e,r,n){s._scrollY=t._fullLayout.legend._scrollY=e,c.setTranslate(O,0,-e),c.setRect(I,q,p.scrollBarMargin+e*n,p.scrollBarWidth,r),P.select("rect").attr({y:s.borderwidth+e})}}},{"../../constants/alignment":632,"../../constants/interactions":636,"../../lib":660,"../../lib/events":648,"../../lib/svg_text_utils":684,"../../plots/plots":768,"../../registry":790,"../color":532,"../dragelement":554,"../drawing":557,"./anchor_utils":584,"./constants":586,"./get_legend_data":589,"./handle_click":590,"./helpers":591,"./style":593,d3:131}],589:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("./helpers");e.exports=function(t,e){var r,a,o={},s=[],l=!1,c={},u=0;function f(t,r){if(""!==t&&i.isGrouped(e))-1===s.indexOf(t)?(s.push(t),l=!0,o[t]=[[r]]):o[t].push([r]);else{var n="~~i"+u;s.push(n),o[n]=[[r]],u++}}for(r=0;rr[1])return r[1]}return i}function d(t){return t[0]}if(u||f||h){var g={},m={};u&&(g.mc=p("marker.color",d),g.mo=p("marker.opacity",a.mean,[.2,1]),g.ms=p("marker.size",a.mean,[2,16]),g.mlc=p("marker.line.color",d),g.mlw=p("marker.line.width",a.mean,[0,5]),m.marker={sizeref:1,sizemin:1,sizemode:"diameter"}),h&&(m.line={width:p("line.width",d,[0,10])}),f&&(g.tx="Aa",g.tp=p("textposition",d),g.ts=10,g.tc=p("textfont.color",d),g.tf=p("textfont.family",d)),r=[a.minExtend(s,g)],(i=a.minExtend(c,m)).selectedpoints=null}var v=n.select(this).select("g.legendpoints"),y=v.selectAll("path.scatterpts").data(u?r:[]);y.enter().append("path").classed("scatterpts",!0).attr("transform","translate(20,0)"),y.exit().remove(),y.call(o.pointStyle,i,e),u&&(r[0].mrc=3);var x=v.selectAll("g.pointtext").data(f?r:[]);x.enter().append("g").classed("pointtext",!0).append("text").attr("transform","translate(20,0)"),x.exit().remove(),x.selectAll("text").call(o.textPointStyle,i,e)}).each(function(t){var e=t[0].trace,r=n.select(this).select("g.legendpoints").selectAll("path.legendcandle").data("candlestick"===e.type&&e.visible?[t,t]:[]);r.enter().append("path").classed("legendcandle",!0).attr("d",function(t,e){return e?"M-15,0H-8M-8,6V-6H8Z":"M15,0H8M8,-6V6H-8Z"}).attr("transform","translate(20,0)").style("stroke-miterlimit",1),r.exit().remove(),r.each(function(t,r){var i=e[r?"increasing":"decreasing"],a=i.line.width,o=n.select(this);o.style("stroke-width",a+"px").call(s.fill,i.fillcolor),a&&s.stroke(o,i.line.color)})}).each(function(t){var e=t[0].trace,r=n.select(this).select("g.legendpoints").selectAll("path.legendohlc").data("ohlc"===e.type&&e.visible?[t,t]:[]);r.enter().append("path").classed("legendohlc",!0).attr("d",function(t,e){return e?"M-15,0H0M-8,-6V0":"M15,0H0M8,6V0"}).attr("transform","translate(20,0)").style("stroke-miterlimit",1),r.exit().remove(),r.each(function(t,r){var i=e[r?"increasing":"decreasing"],a=i.line.width,l=n.select(this);l.style("fill","none").call(o.dashLine,i.line.dash,a),a&&s.stroke(l,i.line.color)})})}},{"../../lib":660,"../../registry":790,"../../traces/pie/style_one":976,"../../traces/scatter/subtypes":1012,"../color":532,"../drawing":557,d3:131}],594:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("../../plots/plots"),a=t("../../plots/cartesian/axis_ids"),o=t("../../lib"),s=t("../../../build/ploticon"),l=o._,c=e.exports={};function u(t,e){var r,i,o=e.currentTarget,s=o.getAttribute("data-attr"),l=o.getAttribute("data-val")||!0,c=t._fullLayout,u={},f=a.list(t,null,!0),h="on";if("zoom"===s){var p,d="in"===l?.5:2,g=(1+d)/2,m=(1-d)/2;for(i=0;i1?(_=["toggleHover"],w=["resetViews"]):f?(b=["zoomInGeo","zoomOutGeo"],_=["hoverClosestGeo"],w=["resetGeo"]):u?(_=["hoverClosest3d"],w=["resetCameraDefault3d","resetCameraLastSave3d"]):g?(_=["toggleHover"],w=["resetViewMapbox"]):_=p?["hoverClosestGl2d"]:h?["hoverClosestPie"]:["toggleHover"];c&&(_=["toggleSpikelines","hoverClosestCartesian","hoverCompareCartesian"]);!c&&!p||v||(b=["zoomIn2d","zoomOut2d","autoScale2d"],"resetViews"!==w[0]&&(w=["resetScale2d"]));u?k=["zoom3d","pan3d","orbitRotation","tableRotation"]:(c||p)&&!v||d?k=["zoom2d","pan2d"]:g||f?k=["pan2d"]:m&&(k=["zoom2d"]);(function(t){for(var e=!1,r=0;r0)){var d=function(t,e,r){for(var n=r.filter(function(r){return e[r].anchor===t._id}),i=0,a=0;a0?h+c:c;return{ppad:c,ppadplus:u?d:g,ppadminus:u?g:d}}return{ppad:c}}function u(t,e,r,n,i){var s="category"===t.type?t.r2c:t.d2c;if(void 0!==e)return[s(e),s(r)];if(n){var l,c,u,f,h=1/0,p=-1/0,d=n.match(a.segmentRE);for("date"===t.type&&(s=o.decodeDate(s)),l=0;lp&&(p=f)));return p>=h?[h,p]:void 0}}e.exports=function(t){var e=t._fullLayout,r=n.filterVisible(e.shapes);if(r.length&&t._fullData.length)for(var o=0;o10?t/2:10;return n.append("circle").attr({"data-line-point":"start-point",cx:W?Q(r.xanchor)+r.x0:Q(r.x0),cy:Y?$(r.yanchor)-r.y0:$(r.y0),r:a}).style(i).classed("cursor-grab",!0),n.append("circle").attr({"data-line-point":"end-point",cx:W?Q(r.xanchor)+r.x1:Q(r.x1),cy:Y?$(r.yanchor)-r.y1:$(r.y1),r:a}).style(i).classed("cursor-grab",!0),n}():e,nt={element:rt.node(),gd:t,prepFn:function(n){var i="shapes["+o+"]";W&&(_=Q(r.xanchor),S=i+".xanchor");Y&&(w=$(r.yanchor),C=i+".yanchor");"path"===r.type?(V=r.path,U=i+".path"):(v=W?r.x0:Q(r.x0),y=Y?r.y0:$(r.y0),x=W?r.x1:Q(r.x1),b=Y?r.y1:$(r.y1),k=i+".x0",M=i+".y0",A=i+".x1",T=i+".y1");vb?(E=y,D=i+".y0",B="y0",L=b,O=i+".y1",F="y1"):(E=b,D=i+".y1",B="y1",L=y,O=i+".y0",F="y0");m={},it(n),st(h,r),function(t,e,r){var n=e.xref,i=e.yref,o=a.getFromId(r,n),l=a.getFromId(r,i),c="";"paper"===n||o.autorange||(c+=n);"paper"===i||l.autorange||(c+=i);t.call(s.setClipUrl,c?"clip"+r._fullLayout._uid+c:null)}(e,r,t),nt.moveFn="move"===q?at:ot},doneFn:function(){c(e),lt(h),p(e,t,r),n.call("relayout",t,m)},clickFn:function(){lt(h)}};function it(t){if(X)q="path"===t.target.tagName?"move":"start-point"===t.target.attributes["data-line-point"].value?"resize-over-start-point":"resize-over-end-point";else{var r=nt.element.getBoundingClientRect(),n=r.right-r.left,i=r.bottom-r.top,a=t.clientX-r.left,o=t.clientY-r.top,s=!Z&&n>H&&i>G&&!t.shiftKey?l.getCursor(a/n,1-o/i):"move";c(e,s),q=s.split("-")[0]}}function at(n,i){if("path"===r.type){var a=function(t){return t},o=a,s=a;W?m[S]=r.xanchor=tt(_+n):(o=function(t){return tt(Q(t)+n)},J&&"date"===J.type&&(o=f.encodeDate(o))),Y?m[C]=r.yanchor=et(w+i):(s=function(t){return et($(t)+i)},K&&"date"===K.type&&(s=f.encodeDate(s))),r.path=g(V,o,s),m[U]=r.path}else W?m[S]=r.xanchor=tt(_+n):(m[k]=r.x0=tt(v+n),m[A]=r.x1=tt(x+n)),Y?m[C]=r.yanchor=et(w+i):(m[M]=r.y0=et(y+i),m[T]=r.y1=et(b+i));e.attr("d",d(t,r)),st(h,r)}function ot(n,i){if(Z){var a=function(t){return t},o=a,s=a;W?m[S]=r.xanchor=tt(_+n):(o=function(t){return tt(Q(t)+n)},J&&"date"===J.type&&(o=f.encodeDate(o))),Y?m[C]=r.yanchor=et(w+i):(s=function(t){return et($(t)+i)},K&&"date"===K.type&&(s=f.encodeDate(s))),r.path=g(V,o,s),m[U]=r.path}else if(X){if("resize-over-start-point"===q){var l=v+n,c=Y?y-i:y+i;m[k]=r.x0=W?l:tt(l),m[M]=r.y0=Y?c:et(c)}else if("resize-over-end-point"===q){var u=x+n,p=Y?b-i:b+i;m[A]=r.x1=W?u:tt(u),m[T]=r.y1=Y?p:et(p)}}else{var rt=~q.indexOf("n")?E+i:E,nt=~q.indexOf("s")?L+i:L,it=~q.indexOf("w")?z+n:z,at=~q.indexOf("e")?P+n:P;~q.indexOf("n")&&Y&&(rt=E-i),~q.indexOf("s")&&Y&&(nt=L-i),(!Y&&nt-rt>G||Y&&rt-nt>G)&&(m[D]=r[B]=Y?rt:et(rt),m[O]=r[F]=Y?nt:et(nt)),at-it>H&&(m[I]=r[N]=W?it:tt(it),m[R]=r[j]=W?at:tt(at))}e.attr("d",d(t,r)),st(h,r)}function st(t,e){(W||Y)&&function(){var r="path"!==e.type,n=t.selectAll(".visual-cue").data([0]);n.enter().append("path").attr({fill:"#fff","fill-rule":"evenodd",stroke:"#000","stroke-width":1}).classed("visual-cue",!0);var a=Q(W?e.xanchor:i.midRange(r?[e.x0,e.x1]:f.extractPathCoords(e.path,u.paramIsX))),o=$(Y?e.yanchor:i.midRange(r?[e.y0,e.y1]:f.extractPathCoords(e.path,u.paramIsY)));if(a=f.roundPositionForSharpStrokeRendering(a,1),o=f.roundPositionForSharpStrokeRendering(o,1),W&&Y){var s="M"+(a-1-1)+","+(o-1-1)+"h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z";n.attr("d",s)}else if(W){var l="M"+(a-1-1)+","+(o-9-1)+"v18 h2 v-18 Z";n.attr("d",l)}else{var c="M"+(a-9-1)+","+(o-1-1)+"h18 v2 h-18 Z";n.attr("d",c)}}()}function lt(t){t.selectAll(".visual-cue").remove()}l.init(nt),rt.node().onmousemove=it}(t,y,h,e,r)}}function p(t,e,r){var n=(r.xref+r.yref).replace(/paper/g,"");t.call(s.setClipUrl,n?"clip"+e._fullLayout._uid+n:null)}function d(t,e){var r,n,o,s,l,c,h,p,d=e.type,g=a.getFromId(t,e.xref),m=a.getFromId(t,e.yref),v=t._fullLayout._size;if(g?(r=f.shapePositionToRange(g),n=function(t){return g._offset+g.r2p(r(t,!0))}):n=function(t){return v.l+v.w*t},m?(o=f.shapePositionToRange(m),s=function(t){return m._offset+m.r2p(o(t,!0))}):s=function(t){return v.t+v.h*(1-t)},"path"===d)return g&&"date"===g.type&&(n=f.decodeDate(n)),m&&"date"===m.type&&(s=f.decodeDate(s)),function(t,e,r){var n=t.path,a=t.xsizemode,o=t.ysizemode,s=t.xanchor,l=t.yanchor;return n.replace(u.segmentRE,function(t){var n=0,c=t.charAt(0),f=u.paramIsX[c],h=u.paramIsY[c],p=u.numParams[c],d=t.substr(1).replace(u.paramRE,function(t){return f[n]?t="pixel"===a?e(s)+Number(t):e(t):h[n]&&(t="pixel"===o?r(l)-Number(t):r(t)),++n>p&&(t="X"),t});return n>p&&(d=d.replace(/[\s,]*X.*/,""),i.log("Ignoring extra params in segment "+t)),c+d})}(e,n,s);if("pixel"===e.xsizemode){var y=n(e.xanchor);l=y+e.x0,c=y+e.x1}else l=n(e.x0),c=n(e.x1);if("pixel"===e.ysizemode){var x=s(e.yanchor);h=x-e.y0,p=x-e.y1}else h=s(e.y0),p=s(e.y1);if("line"===d)return"M"+l+","+h+"L"+c+","+p;if("rect"===d)return"M"+l+","+h+"H"+c+"V"+p+"H"+l+"Z";var b=(l+c)/2,_=(h+p)/2,w=Math.abs(b-l),k=Math.abs(_-h),M="A"+w+","+k,A=b+w+","+_;return"M"+A+M+" 0 1,1 "+(b+","+(_-k))+M+" 0 0,1 "+A+"Z"}function g(t,e,r){return t.replace(u.segmentRE,function(t){var n=0,i=t.charAt(0),a=u.paramIsX[i],o=u.paramIsY[i],s=u.numParams[i];return i+t.substr(1).replace(u.paramRE,function(t){return n>=s?t:(a[n]?t=e(t):o[n]&&(t=r(t)),n++,t)})})}e.exports={draw:function(t){var e=t._fullLayout;for(var r in e._shapeUpperLayer.selectAll("path").remove(),e._shapeLowerLayer.selectAll("path").remove(),e._plots){var n=e._plots[r].shapelayer;n&&n.selectAll("path").remove()}for(var i=0;i0)&&(i("active"),i("x"),i("y"),n.noneOrAll(t,e,["x","y"]),i("xanchor"),i("yanchor"),i("len"),i("lenmode"),i("pad.t"),i("pad.r"),i("pad.b"),i("pad.l"),n.coerceFont(i,"font",r.font),i("currentvalue.visible")&&(i("currentvalue.xanchor"),i("currentvalue.prefix"),i("currentvalue.suffix"),i("currentvalue.offset"),n.coerceFont(i,"currentvalue.font",e.font)),i("transition.duration"),i("transition.easing"),i("bgcolor"),i("activebgcolor"),i("bordercolor"),i("borderwidth"),i("ticklen"),i("tickwidth"),i("tickcolor"),i("minorticklen"))}e.exports=function(t,e){i(t,e,{name:o,handleItemDefaults:l})}},{"../../lib":660,"../../plots/array_container_defaults":702,"./attributes":620,"./constants":621}],623:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../plots/plots"),a=t("../color"),o=t("../drawing"),s=t("../../lib"),l=t("../../lib/svg_text_utils"),c=t("../legend/anchor_utils"),u=t("./constants"),f=t("../../constants/alignment"),h=f.LINE_SPACING,p=f.FROM_TL,d=f.FROM_BR;function g(t){return t._index}function m(t,e){var r=o.tester.selectAll("g."+u.labelGroupClass).data(e.steps);r.enter().append("g").classed(u.labelGroupClass,!0);var a=0,s=0;r.each(function(t){var r=x(n.select(this),{step:t},e).node();if(r){var i=o.bBox(r);s=Math.max(s,i.height),a=Math.max(a,i.width)}}),r.remove();var f=e._dims={};f.inputAreaWidth=Math.max(u.railWidth,u.gripHeight);var h=t._fullLayout._size;f.lx=h.l+h.w*e.x,f.ly=h.t+h.h*(1-e.y),"fraction"===e.lenmode?f.outerLength=Math.round(h.w*e.len):f.outerLength=e.len,f.inputAreaStart=0,f.inputAreaLength=Math.round(f.outerLength-e.pad.l-e.pad.r);var g=(f.inputAreaLength-2*u.stepInset)/(e.steps.length-1),m=a+u.labelPadding;if(f.labelStride=Math.max(1,Math.ceil(m/g)),f.labelHeight=s,f.currentValueMaxWidth=0,f.currentValueHeight=0,f.currentValueTotalHeight=0,f.currentValueMaxLines=1,e.currentvalue.visible){var y=o.tester.append("g");r.each(function(t){var r=v(y,e,t.label),n=r.node()&&o.bBox(r.node())||{width:0,height:0},i=l.lineCount(r);f.currentValueMaxWidth=Math.max(f.currentValueMaxWidth,Math.ceil(n.width)),f.currentValueHeight=Math.max(f.currentValueHeight,Math.ceil(n.height)),f.currentValueMaxLines=Math.max(f.currentValueMaxLines,i)}),f.currentValueTotalHeight=f.currentValueHeight+e.currentvalue.offset,y.remove()}f.height=f.currentValueTotalHeight+u.tickOffset+e.ticklen+u.labelOffset+f.labelHeight+e.pad.t+e.pad.b;var b="left";c.isRightAnchor(e)&&(f.lx-=f.outerLength,b="right"),c.isCenterAnchor(e)&&(f.lx-=f.outerLength/2,b="center");var _="top";c.isBottomAnchor(e)&&(f.ly-=f.height,_="bottom"),c.isMiddleAnchor(e)&&(f.ly-=f.height/2,_="middle"),f.outerLength=Math.ceil(f.outerLength),f.height=Math.ceil(f.height),f.lx=Math.round(f.lx),f.ly=Math.round(f.ly),i.autoMargin(t,u.autoMarginIdRoot+e._index,{x:e.x,y:e.y,l:f.outerLength*p[b],r:f.outerLength*d[b],b:f.height*d[_],t:f.height*p[_]})}function v(t,e,r){if(e.currentvalue.visible){var n,i,a=e._dims;switch(e.currentvalue.xanchor){case"right":n=a.inputAreaLength-u.currentValueInset-a.currentValueMaxWidth,i="left";break;case"center":n=.5*a.inputAreaLength,i="middle";break;default:n=u.currentValueInset,i="left"}var c=s.ensureSingle(t,"text",u.labelClass,function(t){t.classed("user-select-none",!0).attr({"text-anchor":i,"data-notex":1})}),f=e.currentvalue.prefix?e.currentvalue.prefix:"";if("string"==typeof r)f+=r;else f+=e.steps[e.active].label;e.currentvalue.suffix&&(f+=e.currentvalue.suffix),c.call(o.font,e.currentvalue.font).text(f).call(l.convertToTspans,e._gd);var p=l.lineCount(c),d=(a.currentValueMaxLines+1-p)*e.currentvalue.font.size*h;return l.positionText(c,n,d),c}}function y(t,e,r){s.ensureSingle(t,"rect",u.gripRectClass,function(n){n.call(k,e,t,r).style("pointer-events","all")}).attr({width:u.gripWidth,height:u.gripHeight,rx:u.gripRadius,ry:u.gripRadius}).call(a.stroke,r.bordercolor).call(a.fill,r.bgcolor).style("stroke-width",r.borderwidth+"px")}function x(t,e,r){var n=s.ensureSingle(t,"text",u.labelClass,function(t){t.classed("user-select-none",!0).attr({"text-anchor":"middle","data-notex":1})});return n.call(o.font,r.font).text(e.step.label).call(l.convertToTspans,r._gd),n}function b(t,e){var r=s.ensureSingle(t,"g",u.labelsClass),i=e._dims,a=r.selectAll("g."+u.labelGroupClass).data(i.labelSteps);a.enter().append("g").classed(u.labelGroupClass,!0),a.exit().remove(),a.each(function(t){var r=n.select(this);r.call(x,t,e),o.setTranslate(r,T(e,t.fraction),u.tickOffset+e.ticklen+e.font.size*h+u.labelOffset+i.currentValueTotalHeight)})}function _(t,e,r,n,i){var a=Math.round(n*(r.steps.length-1));a!==r.active&&w(t,e,r,a,!0,i)}function w(t,e,r,n,a,o){var s=r.active;r._input.active=r.active=n;var l=r.steps[r.active];e.call(A,r,r.active/(r.steps.length-1),o),e.call(v,r),t.emit("plotly_sliderchange",{slider:r,step:r.steps[r.active],interaction:a,previousActive:s}),l&&l.method&&a&&(e._nextMethod?(e._nextMethod.step=l,e._nextMethod.doCallback=a,e._nextMethod.doTransition=o):(e._nextMethod={step:l,doCallback:a,doTransition:o},e._nextMethodRaf=window.requestAnimationFrame(function(){var r=e._nextMethod.step;r.method&&(r.execute&&i.executeAPICommand(t,r.method,r.args),e._nextMethod=null,e._nextMethodRaf=null)})))}function k(t,e,r){var i=r.node(),o=n.select(e);function s(){return r.data()[0]}t.on("mousedown",function(){var t=s();e.emit("plotly_sliderstart",{slider:t});var l=r.select("."+u.gripRectClass);n.event.stopPropagation(),n.event.preventDefault(),l.call(a.fill,t.activebgcolor);var c=S(t,n.mouse(i)[0]);_(e,r,t,c,!0),t._dragging=!0,o.on("mousemove",function(){var t=s(),a=S(t,n.mouse(i)[0]);_(e,r,t,a,!1)}),o.on("mouseup",function(){var t=s();t._dragging=!1,l.call(a.fill,t.bgcolor),o.on("mouseup",null),o.on("mousemove",null),e.emit("plotly_sliderend",{slider:t,step:t.steps[t.active]})})})}function M(t,e){var r=t.selectAll("rect."+u.tickRectClass).data(e.steps),i=e._dims;r.enter().append("rect").classed(u.tickRectClass,!0),r.exit().remove(),r.attr({width:e.tickwidth+"px","shape-rendering":"crispEdges"}),r.each(function(t,r){var s=r%i.labelStride==0,l=n.select(this);l.attr({height:s?e.ticklen:e.minorticklen}).call(a.fill,e.tickcolor),o.setTranslate(l,T(e,r/(e.steps.length-1))-.5*e.tickwidth,(s?u.tickOffset:u.minorTickOffset)+i.currentValueTotalHeight)})}function A(t,e,r,n){var i=t.select("rect."+u.gripRectClass),a=T(e,r);if(!e._invokingCommand){var o=i;n&&e.transition.duration>0&&(o=o.transition().duration(e.transition.duration).ease(e.transition.easing)),o.attr("transform","translate("+(a-.5*u.gripWidth)+","+e._dims.currentValueTotalHeight+")")}}function T(t,e){var r=t._dims;return r.inputAreaStart+u.stepInset+(r.inputAreaLength-2*u.stepInset)*Math.min(1,Math.max(0,e))}function S(t,e){var r=t._dims;return Math.min(1,Math.max(0,(e-u.stepInset-r.inputAreaStart)/(r.inputAreaLength-2*u.stepInset-2*r.inputAreaStart)))}function C(t,e,r){var n=r._dims,i=s.ensureSingle(t,"rect",u.railTouchRectClass,function(n){n.call(k,e,t,r).style("pointer-events","all")});i.attr({width:n.inputAreaLength,height:Math.max(n.inputAreaWidth,u.tickOffset+r.ticklen+n.labelHeight)}).call(a.fill,r.bgcolor).attr("opacity",0),o.setTranslate(i,0,n.currentValueTotalHeight)}function E(t,e){var r=e._dims,n=r.inputAreaLength-2*u.railInset,i=s.ensureSingle(t,"rect",u.railRectClass);i.attr({width:n,height:u.railWidth,rx:u.railRadius,ry:u.railRadius,"shape-rendering":"crispEdges"}).call(a.stroke,e.bordercolor).call(a.fill,e.bgcolor).style("stroke-width",e.borderwidth+"px"),o.setTranslate(i,u.railInset,.5*(r.inputAreaWidth-u.railWidth)+r.currentValueTotalHeight)}e.exports=function(t){var e=t._fullLayout,r=function(t,e){for(var r=t[u.name],n=[],i=0;i0?[0]:[]);if(a.enter().append("g").classed(u.containerClassName,!0).style("cursor","ew-resize"),a.exit().remove(),a.exit().size()&&function(t){for(var e=t._fullLayout._pushmargin||{},r=Object.keys(e),n=0;n=r.steps.length&&(r.active=0);e.call(v,r).call(E,r).call(b,r).call(M,r).call(C,t,r).call(y,t,r);var n=r._dims;o.setTranslate(e,n.lx+r.pad.l,n.ly+r.pad.t),e.call(A,r,r.active/(r.steps.length-1),!1),e.call(v,r)}(t,n.select(this),e)}})}}},{"../../constants/alignment":632,"../../lib":660,"../../lib/svg_text_utils":684,"../../plots/plots":768,"../color":532,"../drawing":557,"../legend/anchor_utils":584,"./constants":621,d3:131}],624:[function(t,e,r){"use strict";var n=t("./constants");e.exports={moduleType:"component",name:n.name,layoutAttributes:t("./attributes"),supplyLayoutDefaults:t("./defaults"),draw:t("./draw")}},{"./attributes":620,"./constants":621,"./defaults":622,"./draw":623}],625:[function(t,e,r){"use strict";var n=t("d3"),i=t("fast-isnumeric"),a=t("../../plots/plots"),o=t("../../registry"),s=t("../../lib"),l=t("../drawing"),c=t("../color"),u=t("../../lib/svg_text_utils"),f=t("../../constants/interactions");e.exports={draw:function(t,e,r){var p,d=r.propContainer,g=r.propName,m=r.placeholder,v=r.traceIndex,y=r.avoid||{},x=r.attributes,b=r.transform,_=r.containerGroup,w=t._fullLayout,k=d.titlefont||{},M=k.family,A=k.size,T=k.color,S=1,C=!1,E=(d.title||"").trim();"title"===g?p="titleText":-1!==g.indexOf("axis")?p="axisTitleText":g.indexOf(!0)&&(p="colorbarTitleText");var L=t._context.edits[p];""===E?S=0:E.replace(h," % ")===m.replace(h," % ")&&(S=.2,C=!0,L||(E=""));var z=E||L;_||(_=s.ensureSingle(w._infolayer,"g","g-"+e));var P=_.selectAll("text").data(z?[0]:[]);if(P.enter().append("text"),P.text(E).attr("class",e),P.exit().remove(),!z)return _;function D(t){s.syncOrAsync([O,I],t)}function O(e){var r;return b?(r="",b.rotate&&(r+="rotate("+[b.rotate,x.x,x.y]+")"),b.offset&&(r+="translate(0, "+b.offset+")")):r=null,e.attr("transform",r),e.style({"font-family":M,"font-size":n.round(A,2)+"px",fill:c.rgb(T),opacity:S*c.opacity(T),"font-weight":a.fontWeight}).attr(x).call(u.convertToTspans,t),a.previousPromises(t)}function I(t){var e=n.select(t.node().parentNode);if(y&&y.selection&&y.side&&E){e.attr("transform",null);var r=0,a={left:"right",right:"left",top:"bottom",bottom:"top"}[y.side],o=-1!==["left","top"].indexOf(y.side)?-1:1,c=i(y.pad)?y.pad:2,u=l.bBox(e.node()),f={left:0,top:0,right:w.width,bottom:w.height},h=y.maxShift||(f[y.side]-u[y.side])*("left"===y.side||"top"===y.side?-1:1);if(h<0)r=h;else{var p=y.offsetLeft||0,d=y.offsetTop||0;u.left-=p,u.right-=p,u.top-=d,u.bottom-=d,y.selection.each(function(){var t=l.bBox(this);s.bBoxIntersect(u,t,c)&&(r=Math.max(r,o*(t[y.side]-u[a])+c))}),r=Math.min(h,r)}if(r>0||h<0){var g={left:[-r,0],right:[r,0],top:[0,-r],bottom:[0,r]}[y.side];e.attr("transform","translate("+g+")")}}}P.call(D),L&&(E?P.on(".opacity",null):(S=0,C=!0,P.text(m).on("mouseover.opacity",function(){n.select(this).transition().duration(f.SHOW_PLACEHOLDER).style("opacity",1)}).on("mouseout.opacity",function(){n.select(this).transition().duration(f.HIDE_PLACEHOLDER).style("opacity",0)})),P.call(u.makeEditable,{gd:t}).on("edit",function(e){void 0!==v?o.call("restyle",t,g,e,v):o.call("relayout",t,g,e)}).on("cancel",function(){this.text(this.attr("data-unformatted")).call(D)}).on("input",function(t){this.text(t||" ").call(u.positionText,x.x,x.y)}));return P.classed("js-placeholder",C),_}};var h=/ [XY][0-9]* /},{"../../constants/interactions":636,"../../lib":660,"../../lib/svg_text_utils":684,"../../plots/plots":768,"../../registry":790,"../color":532,"../drawing":557,d3:131,"fast-isnumeric":197}],626:[function(t,e,r){"use strict";var n=t("../../plots/font_attributes"),i=t("../color/attributes"),a=t("../../lib/extend").extendFlat,o=t("../../plot_api/edit_types").overrideAll,s=t("../../plots/pad_attributes");e.exports=o({_isLinkedToArray:"updatemenu",_arrayAttrRegexps:[/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/],visible:{valType:"boolean"},type:{valType:"enumerated",values:["dropdown","buttons"],dflt:"dropdown"},direction:{valType:"enumerated",values:["left","right","up","down"],dflt:"down"},active:{valType:"integer",min:-1,dflt:0},showactive:{valType:"boolean",dflt:!0},buttons:{_isLinkedToArray:"button",method:{valType:"enumerated",values:["restyle","relayout","animate","update","skip"],dflt:"restyle"},args:{valType:"info_array",freeLength:!0,items:[{valType:"any"},{valType:"any"},{valType:"any"}]},label:{valType:"string",dflt:""},execute:{valType:"boolean",dflt:!0}},x:{valType:"number",min:-2,max:3,dflt:-.05},xanchor:{valType:"enumerated",values:["auto","left","center","right"],dflt:"right"},y:{valType:"number",min:-2,max:3,dflt:1},yanchor:{valType:"enumerated",values:["auto","top","middle","bottom"],dflt:"top"},pad:a({},s,{}),font:n({}),bgcolor:{valType:"color"},bordercolor:{valType:"color",dflt:i.borderLine},borderwidth:{valType:"number",min:0,dflt:1,editType:"arraydraw"}},"arraydraw","from-root")},{"../../lib/extend":649,"../../plot_api/edit_types":691,"../../plots/font_attributes":732,"../../plots/pad_attributes":767,"../color/attributes":531}],627:[function(t,e,r){"use strict";e.exports={name:"updatemenus",containerClassName:"updatemenu-container",headerGroupClassName:"updatemenu-header-group",headerClassName:"updatemenu-header",headerArrowClassName:"updatemenu-header-arrow",dropdownButtonGroupClassName:"updatemenu-dropdown-button-group",dropdownButtonClassName:"updatemenu-dropdown-button",buttonClassName:"updatemenu-button",itemRectClassName:"updatemenu-item-rect",itemTextClassName:"updatemenu-item-text",menuIndexAttrName:"updatemenu-active-index",autoMarginIdRoot:"updatemenu-",blankHeaderOpts:{label:" "},minWidth:30,minHeight:30,textPadX:24,arrowPadX:16,rx:2,ry:2,textOffsetX:12,textOffsetY:3,arrowOffsetX:4,gapButtonHeader:5,gapButton:2,activeColor:"#F4FAFF",hoverColor:"#F4FAFF",arrowSymbol:{left:"\u25c4",right:"\u25ba",up:"\u25b2",down:"\u25bc"}}},{}],628:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../../plots/array_container_defaults"),a=t("./attributes"),o=t("./constants").name,s=a.buttons;function l(t,e,r){function i(r,i){return n.coerce(t,e,a,r,i)}var o=function(t,e){var r,i,a=t.buttons||[],o=e.buttons=[];function l(t,e){return n.coerce(r,i,s,t,e)}for(var c=0;c0)&&(i("active"),i("direction"),i("type"),i("showactive"),i("x"),i("y"),n.noneOrAll(t,e,["x","y"]),i("xanchor"),i("yanchor"),i("pad.t"),i("pad.r"),i("pad.b"),i("pad.l"),n.coerceFont(i,"font",r.font),i("bgcolor",r.paper_bgcolor),i("bordercolor"),i("borderwidth"))}e.exports=function(t,e){i(t,e,{name:o,handleItemDefaults:l})}},{"../../lib":660,"../../plots/array_container_defaults":702,"./attributes":626,"./constants":627}],629:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../plots/plots"),a=t("../color"),o=t("../drawing"),s=t("../../lib"),l=t("../../lib/svg_text_utils"),c=t("../legend/anchor_utils"),u=t("../../constants/alignment").LINE_SPACING,f=t("./constants"),h=t("./scrollbox");function p(t){return t._index}function d(t,e){return+t.attr(f.menuIndexAttrName)===e._index}function g(t,e,r,n,i,a,o,s){e._input.active=e.active=o,"buttons"===e.type?v(t,n,null,null,e):"dropdown"===e.type&&(i.attr(f.menuIndexAttrName,"-1"),m(t,n,i,a,e),s||v(t,n,i,a,e))}function m(t,e,r,n,i){var a=s.ensureSingle(e,"g",f.headerClassName,function(t){t.style("pointer-events","all")}),l=i._dims,c=i.active,u=i.buttons[c]||f.blankHeaderOpts,h={y:i.pad.t,yPad:0,x:i.pad.l,xPad:0,index:0},p={width:l.headerWidth,height:l.headerHeight};a.call(y,i,u,t).call(A,i,h,p),s.ensureSingle(e,"text",f.headerArrowClassName,function(t){t.classed("user-select-none",!0).attr("text-anchor","end").call(o.font,i.font).text(f.arrowSymbol[i.direction])}).attr({x:l.headerWidth-f.arrowOffsetX+i.pad.l,y:l.headerHeight/2+f.textOffsetY+i.pad.t}),a.on("click",function(){r.call(T),r.attr(f.menuIndexAttrName,d(r,i)?-1:String(i._index)),v(t,e,r,n,i)}),a.on("mouseover",function(){a.call(w)}),a.on("mouseout",function(){a.call(k,i)}),o.setTranslate(e,l.lx,l.ly)}function v(t,e,r,a,o){r||(r=e).attr("pointer-events","all");var s=function(t){return-1==+t.attr(f.menuIndexAttrName)}(r)&&"buttons"!==o.type?[]:o.buttons,l="dropdown"===o.type?f.dropdownButtonClassName:f.buttonClassName,c=r.selectAll("g."+l).data(s),u=c.enter().append("g").classed(l,!0),h=c.exit();"dropdown"===o.type?(u.attr("opacity","0").transition().attr("opacity","1"),h.transition().attr("opacity","0").remove()):h.remove();var p=0,d=0,m=o._dims,v=-1!==["up","down"].indexOf(o.direction);"dropdown"===o.type&&(v?d=m.headerHeight+f.gapButtonHeader:p=m.headerWidth+f.gapButtonHeader),"dropdown"===o.type&&"up"===o.direction&&(d=-f.gapButtonHeader+f.gapButton-m.openHeight),"dropdown"===o.type&&"left"===o.direction&&(p=-f.gapButtonHeader+f.gapButton-m.openWidth);var x={x:m.lx+p+o.pad.l,y:m.ly+d+o.pad.t,yPad:f.gapButton,xPad:f.gapButton,index:0},b={l:x.x+o.borderwidth,t:x.y+o.borderwidth};c.each(function(s,l){var u=n.select(this);u.call(y,o,s,t).call(A,o,x),u.on("click",function(){n.event.defaultPrevented||(g(t,o,0,e,r,a,l),s.execute&&i.executeAPICommand(t,s.method,s.args),t.emit("plotly_buttonclicked",{menu:o,button:s,active:o.active}))}),u.on("mouseover",function(){u.call(w)}),u.on("mouseout",function(){u.call(k,o),c.call(_,o)})}),c.call(_,o),v?(b.w=Math.max(m.openWidth,m.headerWidth),b.h=x.y-b.t):(b.w=x.x-b.l,b.h=Math.max(m.openHeight,m.headerHeight)),b.direction=o.direction,a&&(c.size()?function(t,e,r,n,i,a){var o,s,l,c=i.direction,u="up"===c||"down"===c,h=i._dims,p=i.active;if(u)for(s=0,l=0;l0?[0]:[]);if(a.enter().append("g").classed(f.containerClassName,!0).style("cursor","pointer"),a.exit().remove(),a.exit().size()&&function(t){for(var e=t._fullLayout._pushmargin||{},r=Object.keys(e),n=0;nw,A=s.barLength+2*s.barPad,T=s.barWidth+2*s.barPad,S=d,C=m+v;C+T>c&&(C=c-T);var E=this.container.selectAll("rect.scrollbar-horizontal").data(M?[0]:[]);E.exit().on(".drag",null).remove(),E.enter().append("rect").classed("scrollbar-horizontal",!0).call(i.fill,s.barColor),M?(this.hbar=E.attr({rx:s.barRadius,ry:s.barRadius,x:S,y:C,width:A,height:T}),this._hbarXMin=S+A/2,this._hbarTranslateMax=w-A):(delete this.hbar,delete this._hbarXMin,delete this._hbarTranslateMax);var L=v>k,z=s.barWidth+2*s.barPad,P=s.barLength+2*s.barPad,D=d+g,O=m;D+z>l&&(D=l-z);var I=this.container.selectAll("rect.scrollbar-vertical").data(L?[0]:[]);I.exit().on(".drag",null).remove(),I.enter().append("rect").classed("scrollbar-vertical",!0).call(i.fill,s.barColor),L?(this.vbar=I.attr({rx:s.barRadius,ry:s.barRadius,x:D,y:O,width:z,height:P}),this._vbarYMin=O+P/2,this._vbarTranslateMax=k-P):(delete this.vbar,delete this._vbarYMin,delete this._vbarTranslateMax);var R=this.id,B=u-.5,F=L?f+z+.5:f+.5,N=h-.5,j=M?p+T+.5:p+.5,V=o._topdefs.selectAll("#"+R).data(M||L?[0]:[]);if(V.exit().remove(),V.enter().append("clipPath").attr("id",R).append("rect"),M||L?(this._clipRect=V.select("rect").attr({x:Math.floor(B),y:Math.floor(N),width:Math.ceil(F)-Math.floor(B),height:Math.ceil(j)-Math.floor(N)}),this.container.call(a.setClipUrl,R),this.bg.attr({x:d,y:m,width:g,height:v})):(this.bg.attr({width:0,height:0}),this.container.on("wheel",null).on(".drag",null).call(a.setClipUrl,null),delete this._clipRect),M||L){var U=n.behavior.drag().on("dragstart",function(){n.event.sourceEvent.preventDefault()}).on("drag",this._onBoxDrag.bind(this));this.container.on("wheel",null).on("wheel",this._onBoxWheel.bind(this)).on(".drag",null).call(U);var q=n.behavior.drag().on("dragstart",function(){n.event.sourceEvent.preventDefault(),n.event.sourceEvent.stopPropagation()}).on("drag",this._onBarDrag.bind(this));M&&this.hbar.on(".drag",null).call(q),L&&this.vbar.on(".drag",null).call(q)}this.setTranslate(e,r)},s.prototype.disable=function(){(this.hbar||this.vbar)&&(this.bg.attr({width:0,height:0}),this.container.on("wheel",null).on(".drag",null).call(a.setClipUrl,null),delete this._clipRect),this.hbar&&(this.hbar.on(".drag",null),this.hbar.remove(),delete this.hbar,delete this._hbarXMin,delete this._hbarTranslateMax),this.vbar&&(this.vbar.on(".drag",null),this.vbar.remove(),delete this.vbar,delete this._vbarYMin,delete this._vbarTranslateMax)},s.prototype._onBoxDrag=function(){var t=this.translateX,e=this.translateY;this.hbar&&(t-=n.event.dx),this.vbar&&(e-=n.event.dy),this.setTranslate(t,e)},s.prototype._onBoxWheel=function(){var t=this.translateX,e=this.translateY;this.hbar&&(t+=n.event.deltaY),this.vbar&&(e+=n.event.deltaY),this.setTranslate(t,e)},s.prototype._onBarDrag=function(){var t=this.translateX,e=this.translateY;if(this.hbar){var r=t+this._hbarXMin,i=r+this._hbarTranslateMax;t=(o.constrain(n.event.x,r,i)-r)/(i-r)*(this.position.w-this._box.w)}if(this.vbar){var a=e+this._vbarYMin,s=a+this._vbarTranslateMax;e=(o.constrain(n.event.y,a,s)-a)/(s-a)*(this.position.h-this._box.h)}this.setTranslate(t,e)},s.prototype.setTranslate=function(t,e){var r=this.position.w-this._box.w,n=this.position.h-this._box.h;if(t=o.constrain(t||0,0,r),e=o.constrain(e||0,0,n),this.translateX=t,this.translateY=e,this.container.call(a.setTranslate,this._box.l-this.position.l-t,this._box.t-this.position.t-e),this._clipRect&&this._clipRect.attr({x:Math.floor(this.position.l+t-.5),y:Math.floor(this.position.t+e-.5)}),this.hbar){var i=t/r;this.hbar.call(a.setTranslate,t+i*this._hbarTranslateMax,e)}if(this.vbar){var s=e/n;this.vbar.call(a.setTranslate,t,e+s*this._vbarTranslateMax)}}},{"../../lib":660,"../color":532,"../drawing":557,d3:131}],632:[function(t,e,r){"use strict";e.exports={FROM_BL:{left:0,center:.5,right:1,bottom:0,middle:.5,top:1},FROM_TL:{left:0,center:.5,right:1,bottom:1,middle:.5,top:0},FROM_BR:{left:1,center:.5,right:0,bottom:0,middle:.5,top:1},LINE_SPACING:1.3,MID_SHIFT:.35,OPPOSITE_SIDE:{left:"right",right:"left",top:"bottom",bottom:"top"}}},{}],633:[function(t,e,r){"use strict";e.exports={COMPARISON_OPS:["=","!=","<",">=",">","<="],COMPARISON_OPS2:["=","<",">=",">","<="],INTERVAL_OPS:["[]","()","[)","(]","][",")(","](",")["],SET_OPS:["{}","}{"],CONSTRAINT_REDUCTION:{"=":"=","<":"<","<=":"<",">":">",">=":">","[]":"[]","()":"[]","[)":"[]","(]":"[]","][":"][",")(":"][","](":"][",")[":"]["}}},{}],634:[function(t,e,r){"use strict";e.exports={solid:[[],0],dot:[[.5,1],200],dash:[[.5,1],50],longdash:[[.5,1],10],dashdot:[[.5,.625,.875,1],50],longdashdot:[[.5,.7,.8,1],10]}},{}],635:[function(t,e,r){"use strict";e.exports={circle:"\u25cf","circle-open":"\u25cb",square:"\u25a0","square-open":"\u25a1",diamond:"\u25c6","diamond-open":"\u25c7",cross:"+",x:"\u274c"}},{}],636:[function(t,e,r){"use strict";e.exports={SHOW_PLACEHOLDER:100,HIDE_PLACEHOLDER:1e3,DBLCLICKDELAY:300,DESELECTDIM:.2}},{}],637:[function(t,e,r){"use strict";e.exports={BADNUM:void 0,FP_SAFE:Number.MAX_VALUE/1e4,ONEAVGYEAR:315576e5,ONEAVGMONTH:26298e5,ONEDAY:864e5,ONEHOUR:36e5,ONEMIN:6e4,ONESEC:1e3,EPOCHJD:2440587.5,ALMOST_EQUAL:1-1e-6,MINUS_SIGN:"\u2212"}},{}],638:[function(t,e,r){"use strict";e.exports={entityToUnicode:{mu:"\u03bc","#956":"\u03bc",amp:"&","#28":"&",lt:"<","#60":"<",gt:">","#62":">",nbsp:"\xa0","#160":"\xa0",times:"\xd7","#215":"\xd7",plusmn:"\xb1","#177":"\xb1",deg:"\xb0","#176":"\xb0"}}},{}],639:[function(t,e,r){"use strict";r.xmlns="http://www.w3.org/2000/xmlns/",r.svg="http://www.w3.org/2000/svg",r.xlink="http://www.w3.org/1999/xlink",r.svgAttrs={xmlns:r.svg,"xmlns:xlink":r.xlink}},{}],640:[function(t,e,r){"use strict";r.version="1.38.3",t("es6-promise").polyfill(),t("../build/plotcss"),t("./fonts/mathjax_config");for(var n=t("./registry"),i=r.register=n.register,a=t("./plot_api"),o=Object.keys(a),s=0;s180&&(t-=360*Math.round(t/360)),t}},{}],643:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../constants/numerical").BADNUM,a=/^['"%,$#\s']+|[, ]|['"%,$#\s']+$/g;e.exports=function(t){return"string"==typeof t&&(t=t.replace(a,"")),n(t)?Number(t):i}},{"../constants/numerical":637,"fast-isnumeric":197}],644:[function(t,e,r){"use strict";e.exports=function(t){var e=t._fullLayout;e._glcanvas&&e._glcanvas.size()&&e._glcanvas.each(function(t){t.regl&&t.regl.clear({color:!0,depth:!0})})}},{}],645:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("tinycolor2"),a=t("../plots/attributes"),o=t("../components/colorscale/get_scale"),s=(Object.keys(t("../components/colorscale/scales")),t("./nested_property")),l=t("./regex").counter,c=t("../constants/interactions").DESELECTDIM,u=t("./angles").wrap180,f=t("./is_array").isArrayOrTypedArray;r.valObjectMeta={data_array:{coerceFunction:function(t,e,r){f(t)?e.set(t):void 0!==r&&e.set(r)}},enumerated:{coerceFunction:function(t,e,r,n){n.coerceNumber&&(t=+t),-1===n.values.indexOf(t)?e.set(r):e.set(t)},validateFunction:function(t,e){e.coerceNumber&&(t=+t);for(var r=e.values,n=0;ni.max?e.set(r):e.set(+t)}},integer:{coerceFunction:function(t,e,r,i){t%1||!n(t)||void 0!==i.min&&ti.max?e.set(r):e.set(+t)}},string:{coerceFunction:function(t,e,r,n){if("string"!=typeof t){var i="number"==typeof t;!0!==n.strict&&i?e.set(String(t)):e.set(r)}else n.noBlank&&!t?e.set(r):e.set(t)}},color:{coerceFunction:function(t,e,r){i(t).isValid()?e.set(t):e.set(r)}},colorlist:{coerceFunction:function(t,e,r){Array.isArray(t)&&t.length&&t.every(function(t){return i(t).isValid()})?e.set(t):e.set(r)}},colorscale:{coerceFunction:function(t,e,r){e.set(o(t,r))}},angle:{coerceFunction:function(t,e,r){"auto"===t?e.set("auto"):n(t)?e.set(u(+t)):e.set(r)}},subplotid:{coerceFunction:function(t,e,r,n){var i=n.regex||l(r);"string"==typeof t&&i.test(t)?e.set(t):e.set(r)},validateFunction:function(t,e){var r=e.dflt;return t===r||"string"==typeof t&&!!l(r).test(t)}},flaglist:{coerceFunction:function(t,e,r,n){if("string"==typeof t)if(-1===(n.extras||[]).indexOf(t)){for(var i=t.split("+"),a=0;a=n&&t<=i?t:u;if("string"!=typeof t&&"number"!=typeof t)return u;t=String(t);var a=_(e),o=t.charAt(0);!a||"G"!==o&&"g"!==o||(t=t.substr(1),e="");var s=a&&"chinese"===e.substr(0,7),l=t.match(s?x:y);if(!l)return u;var c=l[1],v=l[3]||"1",w=Number(l[5]||1),k=Number(l[7]||0),M=Number(l[9]||0),A=Number(l[11]||0);if(a){if(2===c.length)return u;var T;c=Number(c);try{var S=m.getComponentMethod("calendars","getCal")(e);if(s){var C="i"===v.charAt(v.length-1);v=parseInt(v,10),T=S.newDate(c,S.toMonthIndex(c,v,C),w)}else T=S.newDate(c,Number(v),w)}catch(t){return u}return T?(T.toJD()-g)*f+k*h+M*p+A*d:u}c=2===c.length?(Number(c)+2e3-b)%100+b:Number(c),v-=1;var E=new Date(Date.UTC(2e3,v,w,k,M));return E.setUTCFullYear(c),E.getUTCMonth()!==v?u:E.getUTCDate()!==w?u:E.getTime()+A*d},n=r.MIN_MS=r.dateTime2ms("-9999"),i=r.MAX_MS=r.dateTime2ms("9999-12-31 23:59:59.9999"),r.isDateTime=function(t,e){return r.dateTime2ms(t,e)!==u};var k=90*f,M=3*h,A=5*p;function T(t,e,r,n,i){if((e||r||n||i)&&(t+=" "+w(e,2)+":"+w(r,2),(n||i)&&(t+=":"+w(n,2),i))){for(var a=4;i%10==0;)a-=1,i/=10;t+="."+w(i,a)}return t}r.ms2DateTime=function(t,e,r){if("number"!=typeof t||!(t>=n&&t<=i))return u;e||(e=0);var a,o,s,c,y,x,b=Math.floor(10*l(t+.05,1)),w=Math.round(t-b/10);if(_(r)){var S=Math.floor(w/f)+g,C=Math.floor(l(t,f));try{a=m.getComponentMethod("calendars","getCal")(r).fromJD(S).formatDate("yyyy-mm-dd")}catch(t){a=v("G%Y-%m-%d")(new Date(w))}if("-"===a.charAt(0))for(;a.length<11;)a="-0"+a.substr(1);else for(;a.length<10;)a="0"+a;o=e=n+f&&t<=i-f))return u;var e=Math.floor(10*l(t+.05,1)),r=new Date(Math.round(t-e/10));return T(a.time.format("%Y-%m-%d")(r),r.getHours(),r.getMinutes(),r.getSeconds(),10*r.getUTCMilliseconds()+e)},r.cleanDate=function(t,e,n){if(r.isJSDate(t)||"number"==typeof t){if(_(n))return s.error("JS Dates and milliseconds are incompatible with world calendars",t),e;if(!(t=r.ms2DateTimeLocal(+t))&&void 0!==e)return e}else if(!r.isDateTime(t,n))return s.error("unrecognized date",t),e;return t};var S=/%\d?f/g;function C(t,e,r,n){t=t.replace(S,function(t){var r=Math.min(+t.charAt(1)||6,6);return(e/1e3%1+2).toFixed(r).substr(2).replace(/0+$/,"")||"0"});var i=new Date(Math.floor(e+.05));if(_(n))try{t=m.getComponentMethod("calendars","worldCalFmt")(t,e,n)}catch(t){return"Invalid"}return r(t)(i)}var E=[59,59.9,59.99,59.999,59.9999];r.formatDate=function(t,e,r,n,i,a){if(i=_(i)&&i,!e)if("y"===r)e=a.year;else if("m"===r)e=a.month;else{if("d"!==r)return function(t,e){var r=l(t+.05,f),n=w(Math.floor(r/h),2)+":"+w(l(Math.floor(r/p),60),2);if("M"!==e){o(e)||(e=0);var i=(100+Math.min(l(t/d,60),E[e])).toFixed(e).substr(1);e>0&&(i=i.replace(/0+$/,"").replace(/[\.]$/,"")),n+=":"+i}return n}(t,r)+"\n"+C(a.dayMonthYear,t,n,i);e=a.dayMonth+"\n"+a.year}return C(e,t,n,i)};var L=3*f;r.incrementMonth=function(t,e,r){r=_(r)&&r;var n=l(t,f);if(t=Math.round(t-n),r)try{var i=Math.round(t/f)+g,a=m.getComponentMethod("calendars","getCal")(r),o=a.fromJD(i);return e%12?a.add(o,e,"m"):a.add(o,e/12,"y"),(o.toJD()-g)*f+n}catch(e){s.error("invalid ms "+t+" in calendar "+r)}var c=new Date(t+L);return c.setUTCMonth(c.getUTCMonth()+e)+n-L},r.findExactDates=function(t,e){for(var r,n,i=0,a=0,s=0,l=0,c=_(e)&&m.getComponentMethod("calendars","getCal")(e),u=0;u0&&(r.push(i),i=[])}return i.length>0&&r.push(i),r},r.makeLine=function(t){return 1===t.length?{type:"LineString",coordinates:t[0]}:{type:"MultiLineString",coordinates:t}},r.makePolygon=function(t){if(1===t.length)return{type:"Polygon",coordinates:t};for(var e=new Array(t.length),r=0;r1||g<0||g>1?null:{x:t+l*g,y:e+f*g}}function l(t,e,r,n,i){var a=n*t+i*e;if(a<0)return n*n+i*i;if(a>r){var o=n-t,s=i-e;return o*o+s*s}var l=n*e-i*t;return l*l/r}r.segmentsIntersect=s,r.segmentDistance=function(t,e,r,n,i,a,o,c){if(s(t,e,r,n,i,a,o,c))return 0;var u=r-t,f=n-e,h=o-i,p=c-a,d=u*u+f*f,g=h*h+p*p,m=Math.min(l(u,f,d,i-t,a-e),l(u,f,d,o-t,c-e),l(h,p,g,t-i,e-a),l(h,p,g,r-i,n-a));return Math.sqrt(m)},r.getTextLocation=function(t,e,r,s){if(t===i&&s===a||(n={},i=t,a=s),n[r])return n[r];var l=t.getPointAtLength(o(r-s/2,e)),c=t.getPointAtLength(o(r+s/2,e)),u=Math.atan((c.y-l.y)/(c.x-l.x)),f=t.getPointAtLength(o(r,e)),h={x:(4*f.x+l.x+c.x)/6,y:(4*f.y+l.y+c.y)/6,theta:u};return n[r]=h,h},r.clearLocationCache=function(){i=null},r.getVisibleSegment=function(t,e,r){var n,i,a=e.left,o=e.right,s=e.top,l=e.bottom,c=0,u=t.getTotalLength(),f=u;function h(e){var r=t.getPointAtLength(e);0===e?n=r:e===u&&(i=r);var c=r.xo?r.x-o:0,f=r.yl?r.y-l:0;return Math.sqrt(c*c+f*f)}for(var p=h(c);p;){if((c+=p+r)>f)return;p=h(c)}for(p=h(f);p;){if(c>(f-=p+r))return;p=h(f)}return{min:c,max:f,len:f-c,total:u,isClosed:0===c&&f===u&&Math.abs(n.x-i.x)<.1&&Math.abs(n.y-i.y)<.1}},r.findPointOnPath=function(t,e,r,n){for(var i,a,o,s=(n=n||{}).pathLength||t.getTotalLength(),l=n.tolerance||.001,c=n.iterationLimit||30,u=t.getPointAtLength(0)[r]>t.getPointAtLength(s)[r]?-1:1,f=0,h=0,p=s;f0?p=i:h=i,f++}return a}},{"./mod":667}],655:[function(t,e,r){"use strict";e.exports=function(t){var e;if("string"==typeof t){if(null===(e=document.getElementById(t)))throw new Error("No DOM element with id '"+t+"' exists on the page.");return e}if(null==t)throw new Error("DOM element provided is null or undefined");return t}},{}],656:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("tinycolor2"),a=t("color-normalize"),o=t("../components/colorscale"),s=t("../components/color/attributes").defaultLine,l=t("./is_array").isArrayOrTypedArray,c=a(s),u=1;function f(t,e){var r=t;return r[3]*=e,r}function h(t){if(n(t))return c;var e=a(t);return e.length?e:c}function p(t){return n(t)?t:u}e.exports={formatColor:function(t,e,r){var n,i,s,d,g,m=t.color,v=l(m),y=l(e),x=[];if(n=void 0!==t.colorscale?o.makeColorScaleFunc(o.extractScale(t.colorscale,t.cmin,t.cmax)):h,i=v?function(t,e){return void 0===t[e]?c:a(n(t[e]))}:h,s=y?function(t,e){return void 0===t[e]?u:p(t[e])}:p,v||y)for(var b=0;b=0;){var n=t.indexOf(";",r);if(n/g,"")}(function(t){for(var e=0;(e=t.indexOf("",e))>=0;){var r=t.indexOf("",e);if(r/g,"\n"))))}},{"../constants/string_mappings":638,"superscript-text":466}],659:[function(t,e,r){"use strict";e.exports=function(t){return t}},{}],660:[function(t,e,r){"use strict";var n=t("d3"),i=t("fast-isnumeric"),a=t("../constants/numerical"),o=a.FP_SAFE,s=a.BADNUM,l=e.exports={};l.nestedProperty=t("./nested_property"),l.keyedContainer=t("./keyed_container"),l.relativeAttr=t("./relative_attr"),l.isPlainObject=t("./is_plain_object"),l.mod=t("./mod"),l.toLogRange=t("./to_log_range"),l.relinkPrivateKeys=t("./relink_private"),l.ensureArray=t("./ensure_array");var c=t("./is_array");l.isTypedArray=c.isTypedArray,l.isArrayOrTypedArray=c.isArrayOrTypedArray,l.isArray1D=c.isArray1D;var u=t("./coerce");l.valObjectMeta=u.valObjectMeta,l.coerce=u.coerce,l.coerce2=u.coerce2,l.coerceFont=u.coerceFont,l.coerceHoverinfo=u.coerceHoverinfo,l.coerceSelectionMarkerOpacity=u.coerceSelectionMarkerOpacity,l.validate=u.validate;var f=t("./dates");l.dateTime2ms=f.dateTime2ms,l.isDateTime=f.isDateTime,l.ms2DateTime=f.ms2DateTime,l.ms2DateTimeLocal=f.ms2DateTimeLocal,l.cleanDate=f.cleanDate,l.isJSDate=f.isJSDate,l.formatDate=f.formatDate,l.incrementMonth=f.incrementMonth,l.dateTick0=f.dateTick0,l.dfltRange=f.dfltRange,l.findExactDates=f.findExactDates,l.MIN_MS=f.MIN_MS,l.MAX_MS=f.MAX_MS;var h=t("./search");l.findBin=h.findBin,l.sorterAsc=h.sorterAsc,l.sorterDes=h.sorterDes,l.distinctVals=h.distinctVals,l.roundUp=h.roundUp;var p=t("./stats");l.aggNums=p.aggNums,l.len=p.len,l.mean=p.mean,l.midRange=p.midRange,l.variance=p.variance,l.stdev=p.stdev,l.interp=p.interp;var d=t("./matrix");l.init2dArray=d.init2dArray,l.transposeRagged=d.transposeRagged,l.dot=d.dot,l.translationMatrix=d.translationMatrix,l.rotationMatrix=d.rotationMatrix,l.rotationXYMatrix=d.rotationXYMatrix,l.apply2DTransform=d.apply2DTransform,l.apply2DTransform2=d.apply2DTransform2;var g=t("./angles");l.deg2rad=g.deg2rad,l.rad2deg=g.rad2deg,l.wrap360=g.wrap360,l.wrap180=g.wrap180;var m=t("./geometry2d");l.segmentsIntersect=m.segmentsIntersect,l.segmentDistance=m.segmentDistance,l.getTextLocation=m.getTextLocation,l.clearLocationCache=m.clearLocationCache,l.getVisibleSegment=m.getVisibleSegment,l.findPointOnPath=m.findPointOnPath;var v=t("./extend");l.extendFlat=v.extendFlat,l.extendDeep=v.extendDeep,l.extendDeepAll=v.extendDeepAll,l.extendDeepNoArrays=v.extendDeepNoArrays;var y=t("./loggers");l.log=y.log,l.warn=y.warn,l.error=y.error;var x=t("./regex");l.counterRegex=x.counter;var b=t("./throttle");function _(t){var e={};for(var r in t)for(var n=t[r],i=0;io?s:i(t)?Number(t):s:s},l.isIndex=function(t,e){return!(void 0!==e&&t>=e)&&(i(t)&&t>=0&&t%1==0)},l.noop=t("./noop"),l.identity=t("./identity"),l.swapAttrs=function(t,e,r,n){r||(r="x"),n||(n="y");for(var i=0;ir?Math.max(r,Math.min(e,t)):Math.max(e,Math.min(r,t))},l.bBoxIntersect=function(t,e,r){return r=r||0,t.left<=e.right+r&&e.left<=t.right+r&&t.top<=e.bottom+r&&e.top<=t.bottom+r},l.simpleMap=function(t,e,r,n){for(var i=t.length,a=new Array(i),o=0;o-1||c!==1/0&&c>=Math.pow(2,r)?t(e,r,n):s},l.OptionControl=function(t,e){t||(t={}),e||(e="opt");var r={optionList:[],_newoption:function(n){n[e]=t,r[n.name]=n,r.optionList.push(n)}};return r["_"+e]=t,r},l.smooth=function(t,e){if((e=Math.round(e)||0)<2)return t;var r,n,i,a,o=t.length,s=2*o,l=2*e-1,c=new Array(l),u=new Array(o);for(r=0;r=s&&(i-=s*Math.floor(i/s)),i<0?i=-1-i:i>=o&&(i=s-1-i),a+=t[i]*c[n];u[r]=a}return u},l.syncOrAsync=function(t,e,r){var n;function i(){return l.syncOrAsync(t,e,r)}for(;t.length;)if((n=(0,t.splice(0,1)[0])(e))&&n.then)return n.then(i).then(void 0,l.promiseError);return r&&r(e)},l.stripTrailingSlash=function(t){return"/"===t.substr(-1)?t.substr(0,t.length-1):t},l.noneOrAll=function(t,e,r){if(t){var n,i=!1,a=!0;for(n=0;n1?i+o[1]:"";if(a&&(o.length>1||s.length>4||r))for(;n.test(s);)s=s.replace(n,"$1"+a+"$2");return s+l};var M=/%{([^\s%{}]*)}/g,A=/^\w*$/;l.templateString=function(t,e){var r={};return t.replace(M,function(t,n){return A.test(n)?e[n]||"":(r[n]=r[n]||l.nestedProperty(e,n).get,r[n]()||"")})};l.subplotSort=function(t,e){for(var r=Math.min(t.length,e.length)+1,n=0,i=0,a=0;a=48&&o<=57,c=s>=48&&s<=57;if(l&&(n=10*n+o-48),c&&(i=10*i+s-48),!l||!c){if(n!==i)return n-i;if(o!==s)return o-s}}return i-n};var T=2e9;l.seedPseudoRandom=function(){T=2e9},l.pseudoRandom=function(){var t=T;return T=(69069*T+1)%4294967296,Math.abs(T-t)<429496729?l.pseudoRandom():T/4294967296}},{"../constants/numerical":637,"./angles":642,"./clean_number":643,"./coerce":645,"./dates":646,"./ensure_array":647,"./extend":649,"./filter_unique":650,"./filter_visible":651,"./geometry2d":654,"./get_graph_div":655,"./identity":659,"./is_array":661,"./is_plain_object":662,"./keyed_container":663,"./localize":664,"./loggers":665,"./matrix":666,"./mod":667,"./nested_property":668,"./noop":669,"./notifier":670,"./push_unique":674,"./regex":676,"./relative_attr":677,"./relink_private":678,"./search":679,"./stats":682,"./throttle":685,"./to_log_range":686,d3:131,"fast-isnumeric":197}],661:[function(t,e,r){"use strict";var n="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer:{isView:function(){return!1}},i="undefined"==typeof DataView?function(){}:DataView;function a(t){return n.isView(t)&&!(t instanceof i)}function o(t){return Array.isArray(t)||a(t)}e.exports={isTypedArray:a,isArrayOrTypedArray:o,isArray1D:function(t){return!o(t[0])}}},{}],662:[function(t,e,r){"use strict";e.exports=function(t){return window&&window.process&&window.process.versions?"[object Object]"===Object.prototype.toString.call(t):"[object Object]"===Object.prototype.toString.call(t)&&Object.getPrototypeOf(t)===Object.prototype}},{}],663:[function(t,e,r){"use strict";var n=t("./nested_property"),i=/^\w*$/;e.exports=function(t,e,r,a){var o,s,l;r=r||"name",a=a||"value";var c={};e&&e.length?(l=n(t,e),s=l.get()):s=t,e=e||"";var u={};if(s)for(o=0;o2)return c[e]=2|c[e],h.set(t,null);if(f){for(o=e;o1){for(var t=["LOG:"],e=0;e0){for(var t=["WARN:"],e=0;e0){for(var t=["ERROR:"],e=0;e/g),o=0;oo||a===i||al||e&&c(t))}:function(t,e){var a=t[0],c=t[1];if(a===i||ao||c===i||cl)return!1;var u,f,h,p,d,g=r.length,m=r[0][0],v=r[0][1],y=0;for(u=1;uMath.max(f,m)||c>Math.max(h,v)))if(cu||Math.abs(n(o,h))>i)return!0;return!1};a.filter=function(t,e){var r=[t[0]],n=0,i=0;function a(a){t.push(a);var s=r.length,l=n;r.splice(i+1);for(var c=l+1;c1&&a(t.pop());return{addPt:a,raw:t,filtered:r}}},{"../constants/numerical":637,"./matrix":666}],673:[function(t,e,r){(function(r){"use strict";var n=t("regl");e.exports=function(t,e){var i=t._fullLayout;i._glcanvas.each(function(a){a.regl||a.pick&&!i._has("parcoords")||(a.regl=n({canvas:this,attributes:{antialias:!a.pick,preserveDrawingBuffer:!0},pixelRatio:t._context.plotGlPixelRatio||r.devicePixelRatio,extensions:e||[]}))})}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{regl:438}],674:[function(t,e,r){"use strict";e.exports=function(t,e){if(e instanceof RegExp){var r,n=e.toString();for(r=0;ri.queueLength&&(t.undoQueue.queue.shift(),t.undoQueue.index--))},startSequence:function(t){t.undoQueue=t.undoQueue||{index:0,queue:[],sequence:!1},t.undoQueue.sequence=!0,t.undoQueue.beginSequence=!0},stopSequence:function(t){t.undoQueue=t.undoQueue||{index:0,queue:[],sequence:!1},t.undoQueue.sequence=!1,t.undoQueue.beginSequence=!1},undo:function(t){var e,r;if(t.framework&&t.framework.isPolar)t.framework.undo();else if(!(void 0===t.undoQueue||isNaN(t.undoQueue.index)||t.undoQueue.index<=0)){for(t.undoQueue.index--,e=t.undoQueue.queue[t.undoQueue.index],t.undoQueue.inSequence=!0,r=0;r=t.undoQueue.queue.length)){for(e=t.undoQueue.queue[t.undoQueue.index],t.undoQueue.inSequence=!0,r=0;re}function l(t,e){return t>=e}r.findBin=function(t,e,r){if(n(e.start))return r?Math.ceil((t-e.start)/e.size-1e-9)-1:Math.floor((t-e.start)/e.size+1e-9);var c,u,f=0,h=e.length,p=0,d=h>1?(e[h-1]-e[0])/(h-1):1;for(u=d>=0?r?a:o:r?l:s,t+=1e-9*d*(r?-1:1)*(d>=0?1:-1);f90&&i.log("Long binary search..."),f-1},r.sorterAsc=function(t,e){return t-e},r.sorterDes=function(t,e){return e-t},r.distinctVals=function(t){var e=t.slice();e.sort(r.sorterAsc);for(var n=e.length-1,i=e[n]-e[0]||1,a=i/(n||1)/1e4,o=[e[0]],s=0;se[s]+a&&(i=Math.min(i,e[s+1]-e[s]),o.push(e[s+1]));return{vals:o,minDiff:i}},r.roundUp=function(t,e,r){for(var n,i=0,a=e.length-1,o=0,s=r?0:1,l=r?1:0,c=r?Math.ceil:Math.floor;ia.length)&&(o=a.length),n(e)||(e=!1),i(a[0])){for(l=new Array(o),s=0;st.length-1)return t[t.length-1];var r=e%1;return r*t[Math.ceil(e)]+(1-r)*t[Math.floor(e)]}},{"./is_array":661,"fast-isnumeric":197}],683:[function(t,e,r){"use strict";var n=t("color-normalize");e.exports=function(t){return t?n(t):[0,0,0,1]}},{"color-normalize":101}],684:[function(t,e,r){"use strict";var n=t("d3"),i=t("../lib"),a=t("../constants/xmlns_namespaces"),o=t("../constants/string_mappings"),s=t("../constants/alignment").LINE_SPACING;function l(t,e){return t.node().getBoundingClientRect()[e]}var c=/([^$]*)([$]+[^$]*[$]+)([^$]*)/;r.convertToTspans=function(t,e,o){var v=t.text(),E=!t.attr("data-notex")&&"undefined"!=typeof MathJax&&v.match(c),L=n.select(t.node().parentNode);if(!L.empty()){var z=t.attr("class")?t.attr("class").split(" ")[0]:"text";return z+="-math",L.selectAll("svg."+z).remove(),L.selectAll("g."+z+"-group").remove(),t.style("display",null).attr({"data-unformatted":v,"data-math":"N"}),E?(e&&e._promises||[]).push(new Promise(function(e){t.style("display","none");var r=parseInt(t.node().style.fontSize,10),a={fontSize:r};!function(t,e,r){var a="math-output-"+i.randstr([],64),o=n.select("body").append("div").attr({id:a}).style({visibility:"hidden",position:"absolute"}).style({"font-size":e.fontSize+"px"}).text((s=t,s.replace(u,"\\lt ").replace(f,"\\gt ")));var s;MathJax.Hub.Queue(["Typeset",MathJax.Hub,o.node()],function(){var e=n.select("body").select("#MathJax_SVG_glyphs");if(o.select(".MathJax_SVG").empty()||!o.select("svg").node())i.log("There was an error in the tex syntax.",t),r();else{var a=o.select("svg").node().getBoundingClientRect();r(o.select(".MathJax_SVG"),e,a)}o.remove()})}(E[2],a,function(n,i,a){L.selectAll("svg."+z).remove(),L.selectAll("g."+z+"-group").remove();var s=n&&n.select("svg");if(!s||!s.node())return P(),void e();var c=L.append("g").classed(z+"-group",!0).attr({"pointer-events":"none","data-unformatted":v,"data-math":"Y"});c.node().appendChild(s.node()),i&&i.node()&&s.node().insertBefore(i.node().cloneNode(!0),s.node().firstChild),s.attr({class:z,height:a.height,preserveAspectRatio:"xMinYMin meet"}).style({overflow:"visible","pointer-events":"none"});var u=t.node().style.fill||"black";s.select("g").attr({fill:u,stroke:u});var f=l(s,"width"),h=l(s,"height"),p=+t.attr("x")-f*{start:0,middle:.5,end:1}[t.attr("text-anchor")||"start"],d=-(r||l(t,"height"))/4;"y"===z[0]?(c.attr({transform:"rotate("+[-90,+t.attr("x"),+t.attr("y")]+") translate("+[-f/2,d-h/2]+")"}),s.attr({x:+t.attr("x"),y:+t.attr("y")})):"l"===z[0]?s.attr({x:t.attr("x"),y:d-h/2}):"a"===z[0]?s.attr({x:0,y:d}):s.attr({x:p,y:+t.attr("y")+d-h/2}),o&&o.call(t,c),e(c)})})):P(),t}function P(){L.empty()||(z=t.attr("class")+"-math",L.select("svg."+z).remove()),t.text("").style("white-space","pre"),function(t,e){e=(r=e,function(t,e){if(!t)return"";for(var r=0;r1)for(var i=1;i doesnt match end tag <"+t+">. Pretending it did match.",e),o=c[c.length-1].node}else i.log("Ignoring unexpected end tag .",e)}w.test(e)?f():(o=t,c=[{node:t}]);for(var z=e.split(b),P=0;P|>|>)/g;var h={sup:"font-size:70%",sub:"font-size:70%",b:"font-weight:bold",i:"font-style:italic",a:"cursor:pointer",span:"",em:"font-style:italic;font-weight:bold"},p={sub:"0.3em",sup:"-0.6em"},d={sub:"-0.21em",sup:"0.42em"},g="\u200b",m=["http:","https:","mailto:","",void 0,":"],v=new RegExp("]*)?/?>","g"),y=Object.keys(o.entityToUnicode).map(function(t){return{regExp:new RegExp("&"+t+";","g"),sub:o.entityToUnicode[t]}}),x=/(\r\n?|\n)/g,b=/(<[^<>]*>)/,_=/<(\/?)([^ >]*)(\s+(.*))?>/i,w=//i,k=/(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i,M=/(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i,A=/(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i,T=/(^|[\s"'])popup\s*=\s*("([\w=,]*)"|'([\w=,]*)')/i;function S(t,e){if(!t)return null;var r=t.match(e);return r&&(r[3]||r[4])}var C=/(^|;)\s*color:/;function E(t,e,r){var n,i,a,o=r.horizontalAlign,s=r.verticalAlign||"top",l=t.node().getBoundingClientRect(),c=e.node().getBoundingClientRect();return i="bottom"===s?function(){return l.bottom-n.height}:"middle"===s?function(){return l.top+(l.height-n.height)/2}:function(){return l.top},a="right"===o?function(){return l.right-n.width}:"center"===o?function(){return l.left+(l.width-n.width)/2}:function(){return l.left},function(){return n=this.node().getBoundingClientRect(),this.style({top:i()-c.top+"px",left:a()-c.left+"px","z-index":1e3}),this}}r.plainText=function(t){return(t||"").replace(v," ")},r.lineCount=function(t){return t.selectAll("tspan.line").size()||1},r.positionText=function(t,e,r){return t.each(function(){var t=n.select(this);function i(e,r){return void 0===r?null===(r=t.attr(e))&&(t.attr(e,0),r=0):t.attr(e,r),r}var a=i("x",e),o=i("y",r);"text"===this.nodeName&&t.selectAll("tspan.line").attr({x:a,y:o})})},r.makeEditable=function(t,e){var r=e.gd,i=e.delegate,a=n.dispatch("edit","input","cancel"),o=i||t;if(t.style({"pointer-events":i?"none":"all"}),1!==t.size())throw new Error("boo");function s(){!function(){var i=n.select(r).select(".svg-container"),o=i.append("div"),s=t.node().style,c=parseFloat(s.fontSize||12),u=e.text;void 0===u&&(u=t.attr("data-unformatted"));o.classed("plugin-editable editable",!0).style({position:"absolute","font-family":s.fontFamily||"Arial","font-size":c,color:e.fill||s.fill||"black",opacity:1,"background-color":e.background||"transparent",outline:"#ffffff33 1px solid",margin:[-c/8+1,0,0,-1].join("px ")+"px",padding:"0","box-sizing":"border-box"}).attr({contenteditable:!0}).text(u).call(E(t,i,e)).on("blur",function(){r._editing=!1,t.text(this.textContent).style({opacity:1});var e,i=n.select(this).attr("class");(e=i?"."+i.split(" ")[0]+"-math-group":"[class*=-math-group]")&&n.select(t.node().parentNode).select(e).style({opacity:0});var o=this.textContent;n.select(this).transition().duration(0).remove(),n.select(document).on("mouseup",null),a.edit.call(t,o)}).on("focus",function(){var t=this;r._editing=!0,n.select(document).on("mouseup",function(){if(n.event.target===t)return!1;document.activeElement===o.node()&&o.node().blur()})}).on("keyup",function(){27===n.event.which?(r._editing=!1,t.style({opacity:1}),n.select(this).style({opacity:0}).on("blur",function(){return!1}).transition().remove(),a.cancel.call(t,this.textContent)):(a.input.call(t,this.textContent),n.select(this).call(E(t,i,e)))}).on("keydown",function(){13===n.event.which&&this.blur()}).call(l)}(),t.style({opacity:0});var i,s=o.attr("class");(i=s?"."+s.split(" ")[0]+"-math-group":"[class*=-math-group]")&&n.select(t.node().parentNode).select(i).style({opacity:0})}function l(t){var e=t.node(),r=document.createRange();r.selectNodeContents(e);var n=window.getSelection();n.removeAllRanges(),n.addRange(r),e.focus()}return e.immediate?s():o.on("click",s),n.rebind(t,a,"on")}},{"../constants/alignment":632,"../constants/string_mappings":638,"../constants/xmlns_namespaces":639,"../lib":660,d3:131}],685:[function(t,e,r){"use strict";var n={};function i(t){t&&null!==t.timer&&(clearTimeout(t.timer),t.timer=null)}r.throttle=function(t,e,r){var a=n[t],o=Date.now();if(!a){for(var s in n)n[s].tsa.ts+e?l():a.timer=setTimeout(function(){l(),a.timer=null},e)},r.done=function(t){var e=n[t];return e&&e.timer?new Promise(function(t){var r=e.onDone;e.onDone=function(){r&&r(),t(),e.onDone=null}}):Promise.resolve()},r.clear=function(t){if(t)i(n[t]),delete n[t];else for(var e in n)r.clear(e)}},{}],686:[function(t,e,r){"use strict";var n=t("fast-isnumeric");e.exports=function(t,e){if(t>0)return Math.log(t)/Math.LN10;var r=Math.log(Math.min(e[0],e[1]))/Math.LN10;return n(r)||(r=Math.log(Math.max(e[0],e[1]))/Math.LN10-6),r}},{"fast-isnumeric":197}],687:[function(t,e,r){"use strict";var n=e.exports={},i=t("../plots/geo/constants").locationmodeToLayer,a=t("topojson-client").feature;n.getTopojsonName=function(t){return[t.scope.replace(/ /g,"-"),"_",t.resolution.toString(),"m"].join("")},n.getTopojsonPath=function(t,e){return t+e+".json"},n.getTopojsonFeatures=function(t,e){var r=i[t.locationmode],n=e.objects[r];return a(e,n).features}},{"../plots/geo/constants":734,"topojson-client":476}],688:[function(t,e,r){"use strict";e.exports={moduleType:"locale",name:"en-US",dictionary:{"Click to enter Colorscale title":"Click to enter Colorscale title"},format:{date:"%m/%d/%Y"}}},{}],689:[function(t,e,r){"use strict";e.exports={moduleType:"locale",name:"en",dictionary:{"Click to enter Colorscale title":"Click to enter Colourscale title"},format:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],periods:["AM","PM"],dateTime:"%a %b %e %X %Y",date:"%d/%m/%Y",time:"%H:%M:%S",decimal:".",thousands:",",grouping:[3],currency:["$",""],year:"%Y",month:"%b %Y",dayMonth:"%b %-d",dayMonthYear:"%b %-d, %Y"}}},{}],690:[function(t,e,r){"use strict";var n=t("../registry");e.exports=function(t){for(var e,r,i=n.layoutArrayContainers,a=n.layoutArrayRegexes,o=t.split("[")[0],s=0;s0&&o.log("Clearing previous rejected promises from queue."),t._promises=[]},r.cleanLayout=function(t){var e,r;t||(t={}),t.xaxis1&&(t.xaxis||(t.xaxis=t.xaxis1),delete t.xaxis1),t.yaxis1&&(t.yaxis||(t.yaxis=t.yaxis1),delete t.yaxis1),t.scene1&&(t.scene||(t.scene=t.scene1),delete t.scene1);var n=(s.subplotsRegistry.cartesian||{}).attrRegex,a=(s.subplotsRegistry.gl3d||{}).attrRegex,l=Object.keys(t);for(e=0;e3?(T.x=1.02,T.xanchor="left"):T.x<-2&&(T.x=-.02,T.xanchor="right"),T.y>3?(T.y=1.02,T.yanchor="bottom"):T.y<-2&&(T.y=-.02,T.yanchor="top")),"rotate"===t.dragmode&&(t.dragmode="orbit"),f.clean(t),t},r.cleanData=function(t,e){for(var n=[],i=t.concat(Array.isArray(e)?e:[]).filter(function(t){return"uid"in t}).map(function(t){return t.uid}),l=0;l0)return t.substr(0,e)}r.hasParent=function(t,e){for(var r=y(e);r;){if(r in t)return!0;r=y(r)}return!1};var x=["x","y","z"];r.clearAxisTypes=function(t,e,r){for(var n=0;n1&&o.warn("Full array edits are incompatible with other edits",f);var y=r[""][""];if(u(y))e.set(null);else{if(!Array.isArray(y))return o.warn("Unrecognized full array edit value",f,y),!0;e.set(y)}return!g&&(h(m,v),p(t),!0)}var x,b,_,w,k,M,A,T=Object.keys(r).map(Number).sort(s),S=e.get(),C=S||[],E=n(v,f).get(),L=[],z=-1,P=C.length;for(x=0;xC.length-(A?0:1))o.warn("index out of range",f,_);else if(void 0!==M)k.length>1&&o.warn("Insertion & removal are incompatible with edits to the same index.",f,_),u(M)?L.push(_):A?("add"===M&&(M={}),C.splice(_,0,M),E&&E.splice(_,0,{})):o.warn("Unrecognized full object edit value",f,_,M),-1===z&&(z=_);else for(b=0;b=0;x--)C.splice(L[x],1),E&&E.splice(L[x],1);if(C.length?S||e.set(C):e.set(null),g)return!1;if(h(m,v),d!==a){var D;if(-1===z)D=T;else{for(P=Math.max(C.length,P),D=[],x=0;x=z);x++)D.push(_);for(x=z;x=t.data.length||i<-t.data.length)throw new Error(r+" must be valid indices for gd.data.");if(e.indexOf(i,n+1)>-1||i>=0&&e.indexOf(-t.data.length+i)>-1||i<0&&e.indexOf(t.data.length+i)>-1)throw new Error("each index in "+r+" must be unique.")}}function z(t,e,r){if(!Array.isArray(t.data))throw new Error("gd.data must be an array.");if(void 0===e)throw new Error("currentIndices is a required argument.");if(Array.isArray(e)||(e=[e]),L(t,e,"currentIndices"),void 0===r||Array.isArray(r)||(r=[r]),void 0!==r&&L(t,r,"newIndices"),void 0!==r&&e.length!==r.length)throw new Error("current and new indices must be of equal length.")}function P(t,e,r,n,a){!function(t,e,r,n){var i=o.isPlainObject(n);if(!Array.isArray(t.data))throw new Error("gd.data must be an array");if(!o.isPlainObject(e))throw new Error("update must be a key:value object");if(void 0===r)throw new Error("indices must be an integer or array of integers");for(var a in L(t,r,"indices"),e){if(!Array.isArray(e[a])||e[a].length!==r.length)throw new Error("attribute "+a+" must be an array of length equal to indices array length");if(i&&(!(a in n)||!Array.isArray(n[a])||n[a].length!==e[a].length))throw new Error("when maxPoints is set as a key:value object it must contain a 1:1 corrispondence with the keys and number of traces in the update object")}}(t,e,r,n);for(var s=function(t,e,r,n){var a,s,l,c,u,f=o.isPlainObject(n),h=[];for(var p in Array.isArray(r)||(r=[r]),r=E(r,t.data.length-1),e)for(var d=0;d=0&&r=0&&r0&&"string"!=typeof E.parts[z];)z--;var P=E.parts[z],D=E.parts[z-1]+"."+P,I=E.parts.slice(0,z).join("."),N=o.nestedProperty(t.layout,I).get(),U=o.nestedProperty(s,I).get(),q=E.get();if(void 0!==L){y[C]=L,x[C]="reverse"===P?L:O(q);var H=u.getLayoutValObject(s,E.parts);if(H&&H.impliedEdits&&null!==L)for(var G in H.impliedEdits)w(o.relativeAttr(C,G),H.impliedEdits[G]);if(-1!==["width","height"].indexOf(C)&&null===L)s[C]=t._initialAutoSize[C];else if(D.match(R))S(D),o.nestedProperty(s,I+"._inputRange").set(null);else if(D.match(B)){S(D),o.nestedProperty(s,I+"._inputRange").set(null);var W=o.nestedProperty(s,I).get();W._inputDomain&&(W._input.domain=W._inputDomain.slice())}else D.match(F)&&o.nestedProperty(s,I+"._inputDomain").set(null);if("type"===P){var Y=N,X="linear"===U.type&&"log"===L,Z="log"===U.type&&"linear"===L;if(X||Z){if(Y&&Y.range)if(U.autorange)X&&(Y.range=Y.range[1]>Y.range[0]?[1,2]:[2,1]);else{var J=Y.range[0],K=Y.range[1];X?(J<=0&&K<=0&&w(I+".autorange",!0),J<=0?J=K/1e6:K<=0&&(K=J/1e6),w(I+".range[0]",Math.log(J)/Math.LN10),w(I+".range[1]",Math.log(K)/Math.LN10)):(w(I+".range[0]",Math.pow(10,J)),w(I+".range[1]",Math.pow(10,K)))}else w(I+".autorange",!0);Array.isArray(s._subplots.polar)&&s._subplots.polar.length&&s[E.parts[0]]&&"radialaxis"===E.parts[1]&&delete s[E.parts[0]]._subplot.viewInitial["radialaxis.range"],c.getComponentMethod("annotations","convertCoords")(t,U,L,w),c.getComponentMethod("images","convertCoords")(t,U,L,w)}else w(I+".autorange",!0),w(I+".range",null);o.nestedProperty(s,I+"._inputRange").set(null)}else if(P.match(M)){var Q=o.nestedProperty(s,C).get(),$=(L||{}).type;$&&"-"!==$||($="linear"),c.getComponentMethod("annotations","convertCoords")(t,Q,$,w),c.getComponentMethod("images","convertCoords")(t,Q,$,w)}var tt=b.containerArrayMatch(C);if(tt){r=tt.array,n=tt.index;var et=tt.property,rt=(o.nestedProperty(a,r)||[])[n]||{},nt=rt,it=H||{editType:"calc"},at=-1!==it.editType.indexOf("calcIfAutorange");""===n?(at?v.calc=!0:k.update(v,it),at=!1):""===et&&(nt=L,b.isAddVal(L)?x[C]=null:b.isRemoveVal(L)?(x[C]=rt,nt=rt):o.warn("unrecognized full object value",e)),at&&(V(t,nt,"x")||V(t,nt,"y"))?v.calc=!0:k.update(v,it),h[r]||(h[r]={});var ot=h[r][n];ot||(ot=h[r][n]={}),ot[et]=L,delete e[C]}else"reverse"===P?(N.range?N.range.reverse():(w(I+".autorange",!0),N.range=[1,0]),U.autorange?v.calc=!0:v.plot=!0):(s._has("scatter-like")&&s._has("regl")&&"dragmode"===C&&("lasso"===L||"select"===L)&&"lasso"!==q&&"select"!==q?v.plot=!0:H?k.update(v,H):v.calc=!0,E.set(L))}}for(r in h){b.applyContainerArrayChanges(t,o.nestedProperty(a,r),h[r],v)||(v.plot=!0)}var st=s._axisConstraintGroups||[];for(A in T)for(n=0;n=i.length?i[0]:i[t]:i}function l(t){return Array.isArray(a)?t>=a.length?a[0]:a[t]:a}function c(t,e){var r=0;return function(){if(t&&++r===e)return t()}}return void 0===n._frameWaitingCnt&&(n._frameWaitingCnt=0),new Promise(function(a,u){function h(){n._currentFrame&&n._currentFrame.onComplete&&n._currentFrame.onComplete();var e=n._currentFrame=n._frameQueue.shift();if(e){var r=e.name?e.name.toString():null;t._fullLayout._currentFrame=r,n._lastFrameAt=Date.now(),n._timeToNext=e.frameOpts.duration,f.transition(t,e.frame.data,e.frame.layout,_.coerceTraceIndices(t,e.frame.traces),e.frameOpts,e.transitionOpts).then(function(){e.onComplete&&e.onComplete()}),t.emit("plotly_animatingframe",{name:r,frame:e.frame,animation:{frame:e.frameOpts,transition:e.transitionOpts}})}else t.emit("plotly_animated"),window.cancelAnimationFrame(n._animationRaf),n._animationRaf=null}function p(){t.emit("plotly_animating"),n._lastFrameAt=-1/0,n._timeToNext=0,n._runningTransitions=0,n._currentFrame=null;var e=function(){n._animationRaf=window.requestAnimationFrame(e),Date.now()-n._lastFrameAt>n._timeToNext&&h()};e()}var d,g,m=0;function v(t){return Array.isArray(i)?m>=i.length?t.transitionOpts=i[m]:t.transitionOpts=i[0]:t.transitionOpts=i,m++,t}var y=[],x=null==e,b=Array.isArray(e);if(!x&&!b&&o.isPlainObject(e))y.push({type:"object",data:v(o.extendFlat({},e))});else if(x||-1!==["string","number"].indexOf(typeof e))for(d=0;d0&&MM)&&A.push(g);y=A}}y.length>0?function(e){if(0!==e.length){for(var i=0;i=0;n--)if(o.isPlainObject(e[n])){var g=e[n].name,m=(u[g]||d[g]||{}).name,v=e[n].name,y=u[m]||d[m];m&&v&&"number"==typeof v&&y&&A<5&&(A++,o.warn('addFrames: overwriting frame "'+(u[m]||d[m]).name+'" with a frame whose name of type "number" also equates to "'+m+'". This is valid but may potentially lead to unexpected behavior since all plotly.js frame names are stored internally as strings.'),5===A&&o.warn("addFrames: This API call has yielded too many of these warnings. For the rest of this call, further warnings about numeric frame names will be suppressed.")),d[g]={name:g},p.push({frame:f.supplyFrameDefaults(e[n]),index:r&&void 0!==r[n]&&null!==r[n]?r[n]:h+n})}p.sort(function(t,e){return t.index>e.index?-1:t.index=0;n--){if("number"==typeof(i=p[n].frame).name&&o.warn("Warning: addFrames accepts frames with numeric names, but the numbers areimplicitly cast to strings"),!i.name)for(;u[i.name="frame "+t._transitionData._counter++];);if(u[i.name]){for(a=0;a=0;r--)n=e[r],a.push({type:"delete",index:n}),s.unshift({type:"insert",index:n,value:i[n]});var c=f.modifyFrames,u=f.modifyFrames,h=[t,s],p=[t,a];return l&&l.add(t,c,h,u,p),f.modifyFrames(t,a)},r.purge=function(t){var e=(t=o.getGraphDiv(t))._fullLayout||{},r=t._fullData||[],n=t.calcdata||[];return f.cleanPlot([],{},r,e,n),f.purge(t),s.purge(t),e._container&&e._container.remove(),delete t._context,t}},{"../components/color":532,"../components/drawing":557,"../constants/xmlns_namespaces":639,"../lib":660,"../lib/events":648,"../lib/queue":675,"../lib/svg_text_utils":684,"../plots/cartesian/axes":706,"../plots/cartesian/constants":711,"../plots/cartesian/graph_interact":715,"../plots/plots":768,"../plots/polar/legacy":776,"../registry":790,"./edit_types":691,"./helpers":692,"./manage_arrays":694,"./plot_config":696,"./plot_schema":697,"./subroutines":698,d3:131,"fast-isnumeric":197,"has-hover":354}],696:[function(t,e,r){"use strict";e.exports={staticPlot:!1,editable:!1,edits:{annotationPosition:!1,annotationTail:!1,annotationText:!1,axisTitleText:!1,colorbarPosition:!1,colorbarTitleText:!1,legendPosition:!1,legendText:!1,shapePosition:!1,titleText:!1},autosizable:!1,queueLength:0,fillFrame:!1,frameMargins:0,scrollZoom:!1,doubleClick:"reset+autosize",showTips:!0,showAxisDragHandles:!0,showAxisRangeEntryBoxes:!0,showLink:!1,sendData:!0,linkText:"Edit chart",showSources:!1,displayModeBar:"hover",modeBarButtonsToRemove:[],modeBarButtonsToAdd:[],modeBarButtons:!1,toImageButtonOptions:{},displaylogo:!0,plotGlPixelRatio:2,setBackground:"transparent",topojsonURL:"https://cdn.plot.ly/",mapboxAccessToken:null,logging:1,globalTransforms:[],locale:"en-US",locales:{}}},{}],697:[function(t,e,r){"use strict";var n=t("../registry"),i=t("../lib"),a=t("../plots/attributes"),o=t("../plots/layout_attributes"),s=t("../plots/frame_attributes"),l=t("../plots/animation_attributes"),c=t("../plots/polar/legacy/area_attributes"),u=t("../plots/polar/legacy/axis_attributes"),f=t("./edit_types"),h=i.extendFlat,p=i.extendDeepAll,d=i.isPlainObject,g="_isSubplotObj",m="_isLinkedToArray",v=[g,m,"_arrayAttrRegexps","_deprecated"];function y(t,e,r){if(!t)return!1;if(t._isLinkedToArray)if(x(e[r]))r++;else if(r=a.length)return!1;if(2===t.dimensions){if(r++,e.length===r)return t;var o=e[r];if(!x(o))return!1;t=a[i][o]}else t=a[i]}else t=a}}return t}function x(t){return t===Math.round(t)&&t>=0}function b(t){return function(t){r.crawl(t,function(t,e,n){r.isValObject(t)?"data_array"===t.valType?(t.role="data",n[e+"src"]={valType:"string",editType:"none"}):!0===t.arrayOk&&(n[e+"src"]={valType:"string",editType:"none"}):d(t)&&(t.role="object")})}(t),function(t){r.crawl(t,function(t,e,r){if(!t)return;var n=t[m];if(!n)return;delete t[m],r[e]={items:{}},r[e].items[n]=t,r[e].role="object"})}(t),function(t){!function t(e){for(var r in e)if(d(e[r]))t(e[r]);else if(Array.isArray(e[r]))for(var n=0;n=l.length)return!1;i=(r=(n.transformsRegistry[l[u].type]||{}).attributes)&&r[e[2]],s=3}else if("area"===t.type)i=c[o];else{var f=t._module;if(f||(f=(n.modules[t.type||a.type.dflt]||{})._module),!f)return!1;if(!(i=(r=f.attributes)&&r[o])){var h=f.basePlotModule;h&&h.attributes&&(i=h.attributes[o])}i||(i=a[o])}return y(i,e,s)},r.getLayoutValObject=function(t,e){return y(function(t,e){var r,i,a,s,l=t._basePlotModules;if(l){var c;for(r=0;r=t[1]||i[1]<=t[0])&&a[0]e[0])return!0}return!1}(r,n,k)){var s=a.node(),l=e.bg=o.ensureSingle(a,"rect","bg");s.insertBefore(l.node(),s.childNodes[0])}else a.select("rect.bg").remove(),w.push(t),k.push([r,n])});var M=i._bgLayer.selectAll(".bg").data(w);return M.enter().append("rect").classed("bg",!0),M.exit().remove(),M.each(function(t){i._plots[t].bg=n.select(this)}),b.each(function(t){var e=i._plots[t],r=e.xaxis,n=e.yaxis;e.bg&&d&&e.bg.call(c.setRect,r._offset-s,n._offset-s,r._length+2*s,n._length+2*s).call(l.fill,i.plot_bgcolor).style("stroke-width",0);var a,f,h=e.clipId="clip"+i._uid+t+"plot",p=o.ensureSingleById(i._clips,"clipPath",h,function(t){t.classed("plotclip",!0).append("rect")});if(e.clipRect=p.select("rect").attr({width:r._length,height:n._length}),c.setTranslate(e.plot,r._offset,n._offset),e._hasClipOnAxisFalse?(a=null,f=h):(a=h,f=null),c.setClipUrl(e.plot,a),e.layerClipId=f,d){var m,v,y,b,w,k,M,A,T,S,C,E,L,z="M0,0";x(r,t)&&(w=_(r,"left",n,u),m=r._offset-(w?s+w:0),k=_(r,"right",n,u),v=r._offset+r._length+(k?s+k:0),y=g(r,n,"bottom"),b=g(r,n,"top"),(L=!r._anchorAxis||t!==r._mainSubplot)&&r.ticks&&"allticks"===r.mirror&&(r._linepositions[t]=[y,b]),z=I(r,D,function(t){return"M"+r._offset+","+t+"h"+r._length}),L&&r.showline&&("all"===r.mirror||"allticks"===r.mirror)&&(z+=D(y)+D(b)),e.xlines.style("stroke-width",r._lw+"px").call(l.stroke,r.showline?r.linecolor:"rgba(0,0,0,0)")),e.xlines.attr("d",z);var P="M0,0";x(n,t)&&(C=_(n,"bottom",r,u),M=n._offset+n._length+(C?s:0),E=_(n,"top",r,u),A=n._offset-(E?s:0),T=g(n,r,"left"),S=g(n,r,"right"),(L=!n._anchorAxis||t!==r._mainSubplot)&&n.ticks&&"allticks"===n.mirror&&(n._linepositions[t]=[T,S]),P=I(n,O,function(t){return"M"+t+","+n._offset+"v"+n._length}),L&&n.showline&&("all"===n.mirror||"allticks"===n.mirror)&&(P+=O(T)+O(S)),e.ylines.style("stroke-width",n._lw+"px").call(l.stroke,n.showline?n.linecolor:"rgba(0,0,0,0)")),e.ylines.attr("d",P)}function D(t){return"M"+m+","+t+"H"+v}function O(t){return"M"+t+","+A+"V"+M}function I(e,r,n){if(!e.showline||t!==e._mainSubplot)return"";if(!e._anchorAxis)return n(e._mainLinePosition);var i=r(e._mainLinePosition);return e.mirror&&(i+=r(e._mainMirrorPosition)),i}}),h.makeClipPaths(t),r.drawMainTitle(t),f.manage(t),t._promises.length&&Promise.all(t._promises)},r.drawMainTitle=function(t){var e=t._fullLayout;u.draw(t,"gtitle",{propContainer:e,propName:"title",placeholder:e._dfltTitle.plot,attributes:{x:e.width/2,y:e._size.t/2,"text-anchor":"middle"}})},r.doTraceStyle=function(t){for(var e=0;ex.length&&i.push(p("unused",a,v.concat(x.length)));var M,A,T,S,C,E=x.length,L=Array.isArray(k);if(L&&(E=Math.min(E,k.length)),2===b.dimensions)for(A=0;Ax[A].length&&i.push(p("unused",a,v.concat(A,x[A].length)));var z=x[A].length;for(M=0;M<(L?Math.min(z,k[A].length):z);M++)T=L?k[A][M]:k,S=y[A][M],C=x[A][M],n.validate(S,T)?C!==S&&C!==+S&&i.push(p("dynamic",a,v.concat(A,M),S,C)):i.push(p("value",a,v.concat(A,M),S))}else i.push(p("array",a,v.concat(A),y[A]));else for(A=0;A1&&h.push(p("object","layout"))),i.supplyDefaults(d);for(var g=d._fullData,m=r.length,v=0;v0&&c>0&&u/c>d&&(o=n,l=a,d=u/c);if(h===p){var y=h-1,x=h+1;f="tozero"===t.rangemode?h<0?[y,0]:[0,x]:"nonnegative"===t.rangemode?[Math.max(0,y),Math.max(0,x)]:[y,x]}else d&&("linear"!==t.type&&"-"!==t.type||("tozero"===t.rangemode?(o.val>=0&&(o={val:0,pad:0}),l.val<=0&&(l={val:0,pad:0})):"nonnegative"===t.rangemode&&(o.val-d*m(o)<0&&(o={val:0,pad:0}),l.val<0&&(l={val:1,pad:0})),d=(l.val-o.val)/(t._length-m(o)-m(l))),f=[o.val-d*m(o),l.val+d*m(l)]);return f[0]===f[1]&&("tozero"===t.rangemode?f=f[0]<0?[f[0],0]:f[0]>0?[0,f[0]]:[0,1]:(f=[f[0]-1,f[0]+1],"nonnegative"===t.rangemode&&(f[0]=Math.max(0,f[0])))),g&&f.reverse(),i.simpleMap(f,t.l2r||Number)}function s(t){var e=t._length/20;return"domain"===t.constrain&&t._inputDomain&&(e*=(t._inputDomain[1]-t._inputDomain[0])/(t.domain[1]-t.domain[0])),function(t){return t.pad+(t.extrapad?e:0)}}function l(t){return n(t)&&Math.abs(t)=e}e.exports={getAutoRange:o,makePadFn:s,doAutoRange:function(t){t._length||t.setScale();var e,r=t._min&&t._max&&t._min.length&&t._max.length;t.autorange&&r&&(t.range=o(t),t._r=t.range.slice(),t._rl=i.simpleMap(t._r,t.r2l),(e=t._input).range=t.range.slice(),e.autorange=t.autorange);if(t._anchorAxis&&t._anchorAxis.rangeslider){var n=t._anchorAxis.rangeslider[t._name];n&&"auto"===n.rangemode&&(n.range=r?o(t):t._rangeInitial?t._rangeInitial.slice():t.range.slice()),(e=t._anchorAxis._input).rangeslider[t._name]=i.extendFlat({},n)}},expand:function(t,e,r){if(!function(t){return t.autorange||t._rangesliderAutorange}(t)||!e)return;t._min||(t._min=[]);t._max||(t._max=[]);r||(r={});t._m||t.setScale();var i,o,s,f,h,p,d,g,m,v,y,x,b=e.length,_=r.padded||!1,w=r.tozero&&("linear"===t.type||"-"===t.type),k="log"===t.type,M=!1;function A(t){if(Array.isArray(t))return M=!0,function(e){return Math.max(Number(t[e]||0),0)};var e=Math.max(Number(t||0),0);return function(){return e}}var T=A((t._m>0?r.ppadplus:r.ppadminus)||r.ppad||0),S=A((t._m>0?r.ppadminus:r.ppadplus)||r.ppad||0),C=A(r.vpadplus||r.vpad),E=A(r.vpadminus||r.vpad);if(!M){if(y=1/0,x=-1/0,k)for(i=0;i0&&(y=f),f>x&&f-a&&(y=f),f>x&&f=b&&(f.extrapad||!_)){v=!1;break}M(i,f.val)&&f.pad<=b&&(_||!f.extrapad)&&(a.splice(o,1),o--)}if(v){var A=w&&0===i;a.push({val:i,pad:A?0:b,extrapad:!A&&_})}}}}var z=Math.min(6,b);for(i=0;i=z;i--)L(i)}}},{"../../constants/numerical":637,"../../lib":660,"fast-isnumeric":197}],706:[function(t,e,r){"use strict";var n=t("d3"),i=t("fast-isnumeric"),a=t("../../plots/plots"),o=t("../../registry"),s=t("../../lib"),l=t("../../lib/svg_text_utils"),c=t("../../components/titles"),u=t("../../components/color"),f=t("../../components/drawing"),h=t("../../constants/numerical"),p=h.ONEAVGYEAR,d=h.ONEAVGMONTH,g=h.ONEDAY,m=h.ONEHOUR,v=h.ONEMIN,y=h.ONESEC,x=h.MINUS_SIGN,b=h.BADNUM,_=t("../../constants/alignment").MID_SHIFT,w=t("../../constants/alignment").LINE_SPACING,k=e.exports={};k.setConvert=t("./set_convert");var M=t("./axis_autotype"),A=t("./axis_ids");k.id2name=A.id2name,k.name2id=A.name2id,k.cleanId=A.cleanId,k.list=A.list,k.listIds=A.listIds,k.getFromId=A.getFromId,k.getFromTrace=A.getFromTrace;var T=t("./autorange");k.expand=T.expand,k.getAutoRange=T.getAutoRange,k.coerceRef=function(t,e,r,n,i,a){var o=n.charAt(n.length-1),l=r._fullLayout._subplots[o+"axis"],c=n+"ref",u={};return i||(i=l[0]||a),a||(a=i),u[c]={valType:"enumerated",values:l.concat(a?[a]:[]),dflt:i},s.coerce(t,e,u,c)},k.coercePosition=function(t,e,r,n,i,a){var o,l;if("paper"===n||"pixel"===n)o=s.ensureNumber,l=r(i,a);else{var c=k.getFromId(e,n);l=r(i,a=c.fraction2r(a)),o=c.cleanPos}t[i]=o(l)},k.cleanPosition=function(t,e,r){return("paper"===r||"pixel"===r?s.ensureNumber:k.getFromId(e,r).cleanPos)(t)};var S=k.getDataConversions=function(t,e,r,n){var i,a="x"===r||"y"===r||"z"===r?r:n;if(Array.isArray(a)){if(i={type:M(n),_categories:[]},k.setConvert(i),"category"===i.type)for(var o=0;o2e-6||((r-t._forceTick0)/t._minDtick%1+1.000001)%1>2e-6)&&(t._minDtick=0)):t._minDtick=0},k.saveRangeInitial=function(t,e){for(var r=k.list(t,"",!0),n=!1,i=0;i.3*h||u(n)||u(a))){var p=r.dtick/2;t+=t+p.8){var o=Number(r.substr(1));a.exactYears>.8&&o%12==0?t=k.tickIncrement(t,"M6","reverse")+1.5*g:a.exactMonths>.8?t=k.tickIncrement(t,"M1","reverse")+15.5*g:t-=g/2;var l=k.tickIncrement(t,r);if(l<=n)return l}return t}(m,t,l.dtick,c,a)),d=m,0;d<=u;)d=k.tickIncrement(d,l.dtick,!1,a),0;return{start:e.c2r(m,0,a),end:e.c2r(d,0,a),size:l.dtick,_dataSpan:u-c}},k.prepTicks=function(t){var e=s.simpleMap(t.range,t.r2l);if("auto"===t.tickmode||!t.dtick){var r,n=t.nticks;n||("category"===t.type?(r=t.tickfont?1.2*(t.tickfont.size||12):15,n=t._length/r):(r="y"===t._id.charAt(0)?40:80,n=s.constrain(t._length/r,4,9)+1),"radialaxis"===t._name&&(n*=2)),"array"===t.tickmode&&(n*=100),k.autoTicks(t,Math.abs(e[1]-e[0])/n),t._minDtick>0&&t.dtick<2*t._minDtick&&(t.dtick=t._minDtick,t.tick0=t.l2r(t._forceTick0))}t.tick0||(t.tick0="date"===t.type?"2000-01-01":0),F(t)},k.calcTicks=function(t){k.prepTicks(t);var e=s.simpleMap(t.range,t.r2l);if("array"===t.tickmode)return function(t){var e,r,n=t.tickvals,i=t.ticktext,a=new Array(n.length),o=s.simpleMap(t.range,t.r2l),l=1.0001*o[0]-1e-4*o[1],c=1.0001*o[1]-1e-4*o[0],u=Math.min(l,c),f=Math.max(l,c),h=0;Array.isArray(i)||(i=[]);var p="category"===t.type?t.d2l_noadd:t.d2l;"log"===t.type&&"L"!==String(t.dtick).charAt(0)&&(t.dtick="L"+Math.pow(10,Math.floor(Math.min(t.range[0],t.range[1]))-1));for(r=0;ru&&e=n:c<=n)&&!(a.length>l||c===o);c=k.tickIncrement(c,t.dtick,i,t.calendar))o=c,a.push(c);"angular"===t._id&&360===Math.abs(e[1]-e[0])&&a.pop(),t._tmax=a[a.length-1],t._prevDateHead="",t._inCalcTicks=!0;for(var u=new Array(a.length),f=0;f10||"01-01"!==n.substr(5)?t._tickround="d":t._tickround=+e.substr(1)%12==0?"y":"m";else if(e>=g&&a<=10||e>=15*g)t._tickround="d";else if(e>=v&&a<=16||e>=m)t._tickround="M";else if(e>=y&&a<=19||e>=v)t._tickround="S";else{var o=t.l2r(r+e).replace(/^-/,"").length;t._tickround=Math.max(a,o)-20}}else if(i(e)||"L"===e.charAt(0)){var s=t.range.map(t.r2d||Number);i(e)||(e=Number(e.substr(1))),t._tickround=2-Math.floor(Math.log(e)/Math.LN10+.01);var l=Math.max(Math.abs(s[0]),Math.abs(s[1])),c=Math.floor(Math.log(l)/Math.LN10+.01);Math.abs(c)>3&&(V(t.exponentformat)&&!U(c)?t._tickexponent=3*Math.round((c-1)/3):t._tickexponent=c)}else t._tickround=null}function N(t,e,r){var n=t.tickfont||{};return{x:e,dx:0,dy:0,text:r||"",fontSize:n.size,font:n.family,fontColor:n.color}}k.autoTicks=function(t,e){var r;function n(t){return Math.pow(t,Math.floor(Math.log(e)/Math.LN10))}if("date"===t.type){t.tick0=s.dateTick0(t.calendar);var a=2*e;a>p?(e/=p,r=n(10),t.dtick="M"+12*B(e,r,L)):a>d?(e/=d,t.dtick="M"+B(e,1,z)):a>g?(t.dtick=B(e,g,D),t.tick0=s.dateTick0(t.calendar,!0)):a>m?t.dtick=B(e,m,z):a>v?t.dtick=B(e,v,P):a>y?t.dtick=B(e,y,P):(r=n(10),t.dtick=B(e,r,L))}else if("log"===t.type){t.tick0=0;var o=s.simpleMap(t.range,t.r2l);if(e>.7)t.dtick=Math.ceil(e);else if(Math.abs(o[1]-o[0])<1){var l=1.5*Math.abs((o[1]-o[0])/e);e=Math.abs(Math.pow(10,o[1])-Math.pow(10,o[0]))/l,r=n(10),t.dtick="L"+B(e,r,L)}else t.dtick=e>.3?"D2":"D1"}else"category"===t.type?(t.tick0=0,t.dtick=Math.ceil(Math.max(e,1))):"angular"===t._id?(t.tick0=0,r=1,t.dtick=B(e,r,R)):(t.tick0=0,r=n(10),t.dtick=B(e,r,L));if(0===t.dtick&&(t.dtick=1),!i(t.dtick)&&"string"!=typeof t.dtick){var c=t.dtick;throw t.dtick=1,"ax.dtick error: "+String(c)}},k.tickIncrement=function(t,e,r,a){var o=r?-1:1;if(i(e))return t+o*e;var l=e.charAt(0),c=o*Number(e.substr(1));if("M"===l)return s.incrementMonth(t,c,a);if("L"===l)return Math.log(Math.pow(10,t)+c)/Math.LN10;if("D"===l){var u="D2"===e?I:O,f=t+.01*o,h=s.roundUp(s.mod(f,1),u,r);return Math.floor(f)+Math.log(n.round(Math.pow(10,h),1))/Math.LN10}throw"unrecognized dtick "+String(e)},k.tickFirst=function(t){var e=t.r2l||Number,r=s.simpleMap(t.range,e),a=r[1]"+l,t._prevDateHead=l));e.text=c}(t,o,r,c):"log"===t.type?function(t,e,r,n,a){var o=t.dtick,l=e.x,c=t.tickformat;"never"===a&&(a="");!n||"string"==typeof o&&"L"===o.charAt(0)||(o="L3");if(c||"string"==typeof o&&"L"===o.charAt(0))e.text=q(Math.pow(10,l),t,a,n);else if(i(o)||"D"===o.charAt(0)&&s.mod(l+.01,1)<.1){var u=Math.round(l);-1!==["e","E","power"].indexOf(t.exponentformat)||V(t.exponentformat)&&U(u)?(e.text=0===u?1:1===u?"10":u>1?"10"+u+"":"10"+x+-u+"",e.fontSize*=1.25):(e.text=q(Math.pow(10,l),t,"","fakehover"),"D1"===o&&"y"===t._id.charAt(0)&&(e.dy-=e.fontSize/6))}else{if("D"!==o.charAt(0))throw"unrecognized dtick "+String(o);e.text=String(Math.round(Math.pow(10,s.mod(l,1)))),e.fontSize*=.75}if("D1"===t.dtick){var f=String(e.text).charAt(0);"0"!==f&&"1"!==f||("y"===t._id.charAt(0)?e.dx-=e.fontSize/4:(e.dy+=e.fontSize/2,e.dx+=(t.range[1]>t.range[0]?1:-1)*e.fontSize*(l<0?.5:.25)))}}(t,o,0,c,n):"category"===t.type?function(t,e){var r=t._categories[Math.round(e.x)];void 0===r&&(r="");e.text=String(r)}(t,o):"angular"===t._id?function(t,e,r,n,i){if("radians"!==t.thetaunit||r)e.text=q(e.x,t,i,n);else{var a=e.x/180;if(0===a)e.text="0";else{var o=function(t){function e(t,e){return Math.abs(t-e)<=1e-6}var r=function(t){var r=1;for(;!e(Math.round(t*r)/r,t);)r*=10;return r}(t),n=t*r,i=Math.abs(function t(r,n){return e(n,0)?r:t(n,r%n)}(n,r));return[Math.round(n/i),Math.round(r/i)]}(a);if(o[1]>=100)e.text=q(s.deg2rad(e.x),t,i,n);else{var l=e.x<0;1===o[1]?1===o[0]?e.text="\u03c0":e.text=o[0]+"\u03c0":e.text=["",o[0],"","\u2044","",o[1],"","\u03c0"].join(""),l&&(e.text=x+e.text)}}}}(t,o,r,c,n):function(t,e,r,n,i){"never"===i?i="":"all"===t.showexponent&&Math.abs(e.x/t.dtick)<1e-6&&(i="hide");e.text=q(e.x,t,i,n)}(t,o,0,c,n),t.tickprefix&&!p(t.showtickprefix)&&(o.text=t.tickprefix+o.text),t.ticksuffix&&!p(t.showticksuffix)&&(o.text+=t.ticksuffix),o},k.hoverLabelText=function(t,e,r){if(r!==b&&r!==e)return k.hoverLabelText(t,e)+" - "+k.hoverLabelText(t,r);var n="log"===t.type&&e<=0,i=k.tickText(t,t.c2l(n?-e:e),"hover").text;return n?0===e?"0":x+i:i};var j=["f","p","n","\u03bc","m","","k","M","G","T"];function V(t){return"SI"===t||"B"===t}function U(t){return t>14||t<-15}function q(t,e,r,n){var a=t<0,o=e._tickround,l=r||e.exponentformat||"B",c=e._tickexponent,u=k.getTickFormat(e),f=e.separatethousands;if(n){var h={exponentformat:l,dtick:"none"===e.showexponent?e.dtick:i(t)&&Math.abs(t)||1,range:"none"===e.showexponent?e.range.map(e.r2d):[0,t||1]};F(h),o=(Number(h._tickround)||0)+4,c=h._tickexponent,e.hoverformat&&(u=e.hoverformat)}if(u)return e._numFormat(u)(t).replace(/-/g,x);var p,d=Math.pow(10,-o)/2;if("none"===l&&(c=0),(t=Math.abs(t))"+p+"":"B"===l&&9===c?t+="B":V(l)&&(t+=j[c/3+5]));return a?x+t:t}function H(t,e){for(var r=0;r=0,a=c(t,e[1])<=0;return(r||i)&&(n||a)}if(t.tickformatstops&&t.tickformatstops.length>0)switch(t.type){case"date":case"linear":for(e=0;e=a(n))){r=t.tickformatstops[e];break}break;case"log":for(e=0;e1&&e1)for(n=1;n2*o}(t,e)?"date":function(t){for(var e,r=Math.max(1,(t.length-1)/1e3),n=0,o=0,s=0;s2*n}(t)?"category":function(t){if(!t)return!1;for(var e=0;en?1:-1:+(t.substr(1)||1)-+(e.substr(1)||1)}},{"../../registry":790,"./constants":711}],710:[function(t,e,r){"use strict";e.exports=function(t,e,r,n){if("category"===e.type){var i,a=t.categoryarray,o=Array.isArray(a)&&a.length>0;o&&(i="array");var s,l=r("categoryorder",i);"array"===l&&(s=r("categoryarray")),o||"array"!==l||(l=e.categoryorder="trace"),"trace"===l?e._initialCategories=[]:"array"===l?e._initialCategories=s.slice():(s=function(t,e){var r,n,i,a=e.dataAttr||t._id.charAt(0),o={};if(e.axData)r=e.axData;else for(r=[],n=0;no*y)||w)for(r=0;rP&&IL&&(L=I);h/=(L-E)/(2*z),E=c.l2r(E),L=c.l2r(L),c.range=c._input.range=T=0?Math.min(t,.9):1/(1/Math.max(t,-.3)+3.222))}function P(t,e,r,n,i){return t.append("path").attr("class","zoombox").style({fill:e>.2?"rgba(0,0,0,0)":"rgba(255,255,255,0)","stroke-width":0}).attr("transform","translate("+r+", "+n+")").attr("d",i+"Z")}function D(t,e,r){return t.append("path").attr("class","zoombox-corners").style({fill:u.background,stroke:u.defaultLine,"stroke-width":1,opacity:0}).attr("transform","translate("+e+", "+r+")").attr("d","M0,0Z")}function O(t,e,r,n,i,a){t.attr("d",n+"M"+r.l+","+r.t+"v"+r.h+"h"+r.w+"v-"+r.h+"h-"+r.w+"Z"),I(t,e,i,a)}function I(t,e,r,n){r||(t.transition().style("fill",n>.2?"rgba(0,0,0,0.4)":"rgba(255,255,255,0.3)").duration(200),e.transition().style("opacity",1).duration(200))}function R(t){n.select(t).selectAll(".zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners").remove()}function B(t){A&&t.data&&t._context.showTips&&(s.notifier(s._(t,"Double-click to zoom back out"),"long"),A=!1)}function F(t){return"lasso"===t||"select"===t}function N(t){var e=Math.floor(Math.min(t.b-t.t,t.r-t.l,M)/2);return"M"+(t.l-3.5)+","+(t.t-.5+e)+"h3v"+-e+"h"+e+"v-3h-"+(e+3)+"ZM"+(t.r+3.5)+","+(t.t-.5+e)+"h-3v"+-e+"h"+-e+"v-3h"+(e+3)+"ZM"+(t.r+3.5)+","+(t.b+.5-e)+"h-3v"+e+"h"+-e+"v3h"+(e+3)+"ZM"+(t.l-3.5)+","+(t.b+.5-e)+"h3v"+e+"h"+e+"v3h-"+(e+3)+"Z"}function j(t,e){if(a){var r=void 0!==t.onwheel?"wheel":"mousewheel";t._onwheel&&t.removeEventListener(r,t._onwheel),t._onwheel=e,t.addEventListener(r,e,{passive:!1})}else void 0!==t.onwheel?t.onwheel=e:void 0!==t.onmousewheel&&(t.onmousewheel=e)}function V(t){var e=[];for(var r in t)e.push(t[r]);return e}e.exports={makeDragBox:function(t,e,r,a,u,p,A,T){var I,U,q,H,G,W,Y,X,Z,J,K,Q,$,tt,et,rt,nt,it,at,ot,st,lt=t._fullLayout._zoomlayer,ct=A+T==="nsew",ut=1===(A+T).length;function ft(){if(I=e.xaxis,U=e.yaxis,Z=I._length,J=U._length,Y=I._offset,X=U._offset,(q={})[I._id]=I,(H={})[U._id]=U,A&&T)for(var r=e.overlays,n=0;nM||o>M?(bt="xy",a/Z>o/J?(o=a*J/Z,gt>i?mt.t=gt-o:mt.b=gt+o):(a=o*Z/J,dt>n?mt.l=dt-a:mt.r=dt+a),wt.attr("d",N(mt))):s():!$||o10||r.scrollWidth-r.clientWidth>10)){clearTimeout(Dt);var n=-e.deltaY;if(isFinite(n)||(n=e.wheelDelta/10),isFinite(n)){var i,a=Math.exp(-Math.min(Math.max(n,-20),20)/200),o=It.draglayer.select(".nsewdrag").node().getBoundingClientRect(),l=(e.clientX-o.left)/o.width,c=(o.bottom-e.clientY)/o.height;if(rt){for(T||(l=.5),i=0;ig[1]-.01&&(e.domain=s),i.noneOrAll(t.domain,e.domain,s)}return r("layer"),e}},{"../../lib":660,"fast-isnumeric":197}],722:[function(t,e,r){"use strict";var n=t("../../constants/alignment").FROM_BL;e.exports=function(t,e,r){void 0===r&&(r=n[t.constraintoward||"center"]);var i=[t.r2l(t.range[0]),t.r2l(t.range[1])],a=i[0]+(i[1]-i[0])*r;t.range=t._input.range=[t.l2r(a+(i[0]-a)*e),t.l2r(a+(i[1]-a)*e)]}},{"../../constants/alignment":632}],723:[function(t,e,r){"use strict";var n=t("polybooljs"),i=t("../../registry"),a=t("../../components/color"),o=t("../../components/fx"),s=t("../../lib/polygon"),l=t("../../lib/throttle"),c=t("../../components/fx/helpers").makeEventData,u=t("./axis_ids").getFromId,f=t("../sort_modules").sortModules,h=t("./constants"),p=h.MINSELECT,d=s.filter,g=s.tester,m=s.multitester;function v(t){return t._id}function y(t,e,r){var n,a,o,s;if(r){var l=r.points||[];for(n=0;n0)return Math.log(e)/Math.LN10;if(e<=0&&r&&t.range&&2===t.range.length){var n=t.range[0],i=t.range[1];return.5*(n+i-3*u*Math.abs(n-i))}return h}function v(e,r,n){var a=l(e,n||t.calendar);if(a===h){if(!i(e))return h;a=l(new Date(+e))}return a}function y(e,r,n){return s(e,r,n||t.calendar)}function x(e){return t._categories[Math.round(e)]}function b(e){if(t._categoriesMap){var r=t._categoriesMap[e];if(void 0!==r)return r}if(i(e))return+e}function _(e){return i(e)?n.round(t._b+t._m*e,2):h}function w(e){return(e-t._b)/t._m}t.c2l="log"===t.type?m:c,t.l2c="log"===t.type?g:c,t.l2p=_,t.p2l=w,t.c2p="log"===t.type?function(t,e){return _(m(t,e))}:_,t.p2c="log"===t.type?function(t){return g(w(t))}:w,-1!==["linear","-"].indexOf(t.type)?(t.d2r=t.r2d=t.d2c=t.r2c=t.d2l=t.r2l=o,t.c2d=t.c2r=t.l2d=t.l2r=c,t.d2p=t.r2p=function(e){return t.l2p(o(e))},t.p2d=t.p2r=w,t.cleanPos=c):"log"===t.type?(t.d2r=t.d2l=function(t,e){return m(o(t),e)},t.r2d=t.r2c=function(t){return g(o(t))},t.d2c=t.r2l=o,t.c2d=t.l2r=c,t.c2r=m,t.l2d=g,t.d2p=function(e,r){return t.l2p(t.d2r(e,r))},t.p2d=function(t){return g(w(t))},t.r2p=function(e){return t.l2p(o(e))},t.p2r=w,t.cleanPos=c):"date"===t.type?(t.d2r=t.r2d=a.identity,t.d2c=t.r2c=t.d2l=t.r2l=v,t.c2d=t.c2r=t.l2d=t.l2r=y,t.d2p=t.r2p=function(e,r,n){return t.l2p(v(e,0,n))},t.p2d=t.p2r=function(t,e,r){return y(w(t),e,r)},t.cleanPos=function(e){return a.cleanDate(e,h,t.calendar)}):"category"===t.type&&(t.d2c=t.d2l=function(e){if(null!=e){if(void 0===t._categoriesMap&&(t._categoriesMap={}),void 0!==t._categoriesMap[e])return t._categoriesMap[e];t._categories.push(e);var r=t._categories.length-1;return t._categoriesMap[e]=r,r}return h},t.r2d=t.c2d=t.l2d=x,t.d2r=t.d2l_noadd=b,t.r2c=function(e){var r=b(e);return void 0!==r?r:t.fraction2r(.5)},t.l2r=t.c2r=c,t.r2l=b,t.d2p=function(e){return t.l2p(t.r2c(e))},t.p2d=function(t){return x(w(t))},t.r2p=t.d2p,t.p2r=w,t.cleanPos=function(t){return"string"==typeof t&&""!==t?t:c(t)}),t.fraction2r=function(e){var r=t.r2l(t.range[0]),n=t.r2l(t.range[1]);return t.l2r(r+e*(n-r))},t.r2fraction=function(e){var r=t.r2l(t.range[0]),n=t.r2l(t.range[1]);return(t.r2l(e)-r)/(n-r)},t.cleanRange=function(e,n){n||(n={}),e||(e="range");var o,s,l=a.nestedProperty(t,e).get();if(s=(s="date"===t.type?a.dfltRange(t.calendar):"y"===r?p.DFLTRANGEY:n.dfltRange||p.DFLTRANGEX).slice(),l&&2===l.length)for("date"===t.type&&(l[0]=a.cleanDate(l[0],h,t.calendar),l[1]=a.cleanDate(l[1],h,t.calendar)),o=0;o<2;o++)if("date"===t.type){if(!a.isDateTime(l[o],t.calendar)){t[e]=s;break}if(t.r2l(l[0])===t.r2l(l[1])){var c=a.constrain(t.r2l(l[0]),a.MIN_MS+1e3,a.MAX_MS-1e3);l[0]=t.l2r(c-1e3),l[1]=t.l2r(c+1e3);break}}else{if(!i(l[o])){if(!i(l[1-o])){t[e]=s;break}l[o]=l[1-o]*(o?10:.1)}if(l[o]<-f?l[o]=-f:l[o]>f&&(l[o]=f),l[0]===l[1]){var u=Math.max(1,Math.abs(1e-6*l[0]));l[0]-=u,l[1]+=u}}else a.nestedProperty(t,e).set(s)},t.setScale=function(n){var i=e._size;if(t._categories||(t._categories=[]),t._categoriesMap||(t._categoriesMap={}),t.overlaying){var a=d.getFromId({_fullLayout:e},t.overlaying);t.domain=a.domain}var o=n&&t._r?"_r":"range",s=t.calendar;t.cleanRange(o);var l=t.r2l(t[o][0],s),c=t.r2l(t[o][1],s);if("y"===r?(t._offset=i.t+(1-t.domain[1])*i.h,t._length=i.h*(t.domain[1]-t.domain[0]),t._m=t._length/(l-c),t._b=-t._m*c):(t._offset=i.l+t.domain[0]*i.w,t._length=i.w*(t.domain[1]-t.domain[0]),t._m=t._length/(c-l),t._b=-t._m*l),!isFinite(t._m)||!isFinite(t._b))throw e._replotting=!1,new Error("Something went wrong with axis scaling")},t.makeCalcdata=function(e,r){var n,i,o,s,l=t.type,c="date"===l&&e[r+"calendar"];if(r in e){if(n=e[r],s=e._length||n.length,a.isTypedArray(n)&&("linear"===l||"log"===l)){if(s===n.length)return n;if(n.subarray)return n.subarray(0,s)}for(i=new Array(s),o=0;o0?Number(u):c;else if("string"!=typeof u)e.dtick=c;else{var f=u.charAt(0),h=u.substr(1);((h=n(h)?Number(h):0)<=0||!("date"===o&&"M"===f&&h===Math.round(h)||"log"===o&&"L"===f||"log"===o&&"D"===f&&(1===h||2===h)))&&(e.dtick=c)}var p="date"===o?i.dateTick0(e.calendar):0,d=r("tick0",p);"date"===o?e.tick0=i.cleanDate(d,p):n(d)&&"D1"!==u&&"D2"!==u?e.tick0=Number(d):e.tick0=p}else{void 0===r("tickvals")?e.tickmode="auto":r("ticktext")}}},{"../../constants/numerical":637,"../../lib":660,"fast-isnumeric":197}],728:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../registry"),a=t("../../components/drawing"),o=t("./axes"),s=t("./constants").attrRegex;e.exports=function(t,e,r,l){var c=t._fullLayout,u=[];var f,h,p,d,g=function(t){var e,r,n,i,a={};for(e in t)if((r=e.split("."))[0].match(s)){var o=e.charAt(0),l=r[0];if(n=c[l],i={},Array.isArray(t[e])?i.to=t[e].slice(0):Array.isArray(t[e].range)&&(i.to=t[e].range.slice(0)),!i.to)continue;i.axisName=l,i.length=n._length,u.push(o),a[o]=i}return a}(e),m=Object.keys(g),v=function(t,e,r){var n,i,a,o=t._plots,s=[];for(n in o){var l=o[n];if(-1===s.indexOf(l)){var c=l.xaxis._id,u=l.yaxis._id,f=l.xaxis.range,h=l.yaxis.range;l.xaxis._r=l.xaxis.range.slice(),l.yaxis._r=l.yaxis.range.slice(),i=r[c]?r[c].to:f,a=r[u]?r[u].to:h,f[0]===i[0]&&f[1]===i[1]&&h[0]===a[0]&&h[1]===a[1]||-1===e.indexOf(c)&&-1===e.indexOf(u)||s.push(l)}}return s}(c,m,g);if(!v.length)return function(){function e(e,r,n){for(var i=0;i rect").call(a.setTranslate,0,0).call(a.setScale,1,1),t.plot.call(a.setTranslate,e._offset,r._offset).call(a.setScale,1,1);var n=t.plot.selectAll(".scatterlayer .trace");n.selectAll(".point").call(a.setPointGroupScale,1,1),n.selectAll(".textpoint").call(a.setTextPointsScale,1,1),n.call(a.hideOutsideRangePoints,t)}function x(e,r){var n,s,l,u=g[e.xaxis._id],f=g[e.yaxis._id],h=[];if(u){s=(n=t._fullLayout[u.axisName])._r,l=u.to,h[0]=(s[0]*(1-r)+r*l[0]-s[0])/(s[1]-s[0])*e.xaxis._length;var p=s[1]-s[0],d=l[1]-l[0];n.range[0]=s[0]*(1-r)+r*l[0],n.range[1]=s[1]*(1-r)+r*l[1],h[2]=e.xaxis._length*(1-r+r*d/p)}else h[0]=0,h[2]=e.xaxis._length;if(f){s=(n=t._fullLayout[f.axisName])._r,l=f.to,h[1]=(s[1]*(1-r)+r*l[1]-s[1])/(s[0]-s[1])*e.yaxis._length;var m=s[1]-s[0],v=l[1]-l[0];n.range[0]=s[0]*(1-r)+r*l[0],n.range[1]=s[1]*(1-r)+r*l[1],h[3]=e.yaxis._length*(1-r+r*v/m)}else h[1]=0,h[3]=e.yaxis._length;!function(e,r){var n,a=[];for(a=[e._id,r._id],n=0;nr.duration?(function(){for(var e={},r=0;r0&&i["_"+r+"axes"][e])return i;if((i[r+"axis"]||r)===e){if(s(i,r))return i;if((i[r]||[]).length||i[r+"0"])return i}}}(e,r,a);if(!l)return;if("histogram"===l.type&&a==={v:"y",h:"x"}[l.orientation||"v"])return void(t.type="linear");var c,u=a+"calendar",f=l[u];if(s(l,a)){var h=o(l),p=[];for(c=0;c0?".":"")+a;i.isPlainObject(o)?l(o,e,s,n+1):e(s,a,o)}})}r.manageCommandObserver=function(t,e,n,o){var s={},l=!0;e&&e._commandObserver&&(s=e._commandObserver),s.cache||(s.cache={}),s.lookupTable={};var c=r.hasSimpleAPICommandBindings(t,n,s.lookupTable);if(e&&e._commandObserver){if(c)return s;if(e._commandObserver.remove)return e._commandObserver.remove(),e._commandObserver=null,s}if(c){a(t,c,s.cache),s.check=function(){if(l){var e=a(t,c,s.cache);return e.changed&&o&&void 0!==s.lookupTable[e.value]&&(s.disable(),Promise.resolve(o({value:e.value,type:c.type,prop:c.prop,traces:c.traces,index:s.lookupTable[e.value]})).then(s.enable,s.enable)),e.changed}};for(var u=["plotly_relayout","plotly_redraw","plotly_restyle","plotly_update","plotly_animatingframe","plotly_afterplot"],f=0;fi*Math.PI/180}return!1},r.getPath=function(){return n.geo.path().projection(r)},r.getBounds=function(t){return r.getPath().bounds(t)},r.fitExtent=function(t,e){var n=t[1][0]-t[0][0],i=t[1][1]-t[0][1],a=r.clipExtent&&r.clipExtent();r.scale(150).translate([0,0]),a&&r.clipExtent(null);var o=r.getBounds(e),s=Math.min(n/(o[1][0]-o[0][0]),i/(o[1][1]-o[0][1])),l=+t[0][0]+(n-s*(o[1][0]+o[0][0]))/2,c=+t[0][1]+(i-s*(o[1][1]+o[0][1]))/2;return a&&r.clipExtent(a),r.scale(150*s).translate([l,c])},r.precision(d.precision),i&&r.clipAngle(i-d.clipPad);return r}(e);u.center([c.lon-l.lon,c.lat-l.lat]).rotate([-l.lon,-l.lat,l.roll]).parallels(s.parallels);var f=[[r.l+r.w*o.x[0],r.t+r.h*(1-o.y[1])],[r.l+r.w*o.x[1],r.t+r.h*(1-o.y[0])]],h=e.lonaxis,p=e.lataxis,g=function(t,e){var r=d.clipPad,n=t[0]+r,i=t[1]-r,a=e[0]+r,o=e[1]-r;n>0&&i<0&&(i+=360);var s=(i-n)/4;return{type:"Polygon",coordinates:[[[n,a],[n,o],[n+s,o],[n+2*s,o],[n+3*s,o],[i,o],[i,a],[i-s,a],[i-2*s,a],[i-3*s,a],[n,a]]]}}(h.range,p.range);u.fitExtent(f,g);var m=this.bounds=u.getBounds(g),v=this.fitScale=u.scale(),y=u.translate();if(!isFinite(m[0][0])||!isFinite(m[0][1])||!isFinite(m[1][0])||!isFinite(m[1][1])||isNaN(y[0])||isNaN(y[0])){for(var x=this.graphDiv,b=["projection.rotation","center","lonaxis.range","lataxis.range"],_="Invalid geo settings, relayout'ing to default view.",w={},k=0;k0&&k<0&&(k+=360);var M,A,T,S=(w+k)/2;if(!c){var C=u?s.projRotate:[S,0,0];M=r("projection.rotation.lon",C[0]),r("projection.rotation.lat",C[1]),r("projection.rotation.roll",C[2]),r("showcoastlines",!u)&&(r("coastlinecolor"),r("coastlinewidth")),r("showocean")&&r("oceancolor")}(c?(A=-96.6,T=38.7):(A=u?S:M,T=(_[0]+_[1])/2),r("center.lon",A),r("center.lat",T),f)&&r("projection.parallels",s.projParallels||[0,60]);r("projection.scale"),r("showland")&&r("landcolor"),r("showlakes")&&r("lakecolor"),r("showrivers")&&(r("rivercolor"),r("riverwidth")),r("showcountries",u&&"usa"!==a)&&(r("countrycolor"),r("countrywidth")),("usa"===a||"north america"===a&&50===n)&&(r("showsubunits",!0),r("subunitcolor"),r("subunitwidth")),u||r("showframe",!0)&&(r("framecolor"),r("framewidth")),r("bgcolor")}e.exports=function(t,e,r){n(t,e,r,{type:"geo",attributes:a,handleDefaults:s,partition:"y"})}},{"../../subplot_defaults":782,"../constants":734,"./layout_attributes":739}],739:[function(t,e,r){"use strict";var n=t("../../../components/color/attributes"),i=t("../../domain").attributes,a=t("../constants"),o=t("../../../plot_api/edit_types").overrideAll,s={range:{valType:"info_array",items:[{valType:"number"},{valType:"number"}]},showgrid:{valType:"boolean",dflt:!1},tick0:{valType:"number"},dtick:{valType:"number"},gridcolor:{valType:"color",dflt:n.lightLine},gridwidth:{valType:"number",min:0,dflt:1}};e.exports=o({domain:i({name:"geo"},{}),resolution:{valType:"enumerated",values:[110,50],dflt:110,coerceNumber:!0},scope:{valType:"enumerated",values:Object.keys(a.scopeDefaults),dflt:"world"},projection:{type:{valType:"enumerated",values:Object.keys(a.projNames)},rotation:{lon:{valType:"number"},lat:{valType:"number"},roll:{valType:"number"}},parallels:{valType:"info_array",items:[{valType:"number"},{valType:"number"}]},scale:{valType:"number",min:0,dflt:1}},center:{lon:{valType:"number"},lat:{valType:"number"}},showcoastlines:{valType:"boolean"},coastlinecolor:{valType:"color",dflt:n.defaultLine},coastlinewidth:{valType:"number",min:0,dflt:1},showland:{valType:"boolean",dflt:!1},landcolor:{valType:"color",dflt:a.landColor},showocean:{valType:"boolean",dflt:!1},oceancolor:{valType:"color",dflt:a.waterColor},showlakes:{valType:"boolean",dflt:!1},lakecolor:{valType:"color",dflt:a.waterColor},showrivers:{valType:"boolean",dflt:!1},rivercolor:{valType:"color",dflt:a.waterColor},riverwidth:{valType:"number",min:0,dflt:1},showcountries:{valType:"boolean"},countrycolor:{valType:"color",dflt:n.defaultLine},countrywidth:{valType:"number",min:0,dflt:1},showsubunits:{valType:"boolean"},subunitcolor:{valType:"color",dflt:n.defaultLine},subunitwidth:{valType:"number",min:0,dflt:1},showframe:{valType:"boolean"},framecolor:{valType:"color",dflt:n.defaultLine},framewidth:{valType:"number",min:0,dflt:1},bgcolor:{valType:"color",dflt:n.background},lonaxis:s,lataxis:s},"plot","from-root")},{"../../../components/color/attributes":531,"../../../plot_api/edit_types":691,"../../domain":731,"../constants":734}],740:[function(t,e,r){"use strict";e.exports=function(t){function e(t,e){return{type:"Feature",id:t.id,properties:t.properties,geometry:r(t.geometry,e)}}function r(e,n){if(!e)return null;if("GeometryCollection"===e.type)return{type:"GeometryCollection",geometries:object.geometries.map(function(t){return r(t,n)})};if(!c.hasOwnProperty(e.type))return null;var i=c[e.type];return t.geo.stream(e,n(i)),i.result()}t.geo.project=function(t,e){var i=e.stream;if(!i)throw new Error("not yet supported");return(t&&n.hasOwnProperty(t.type)?n[t.type]:r)(t,i)};var n={Feature:e,FeatureCollection:function(t,r){return{type:"FeatureCollection",features:t.features.map(function(t){return e(t,r)})}}},i=[],a=[],o={point:function(t,e){i.push([t,e])},result:function(){var t=i.length?i.length<2?{type:"Point",coordinates:i[0]}:{type:"MultiPoint",coordinates:i}:null;return i=[],t}},s={lineStart:u,point:function(t,e){i.push([t,e])},lineEnd:function(){i.length&&(a.push(i),i=[])},result:function(){var t=a.length?a.length<2?{type:"LineString",coordinates:a[0]}:{type:"MultiLineString",coordinates:a}:null;return a=[],t}},l={polygonStart:u,lineStart:u,point:function(t,e){i.push([t,e])},lineEnd:function(){var t=i.length;if(t){do{i.push(i[0].slice())}while(++t<4);a.push(i),i=[]}},polygonEnd:u,result:function(){if(!a.length)return null;var t=[],e=[];return a.forEach(function(r){!function(t){if((e=t.length)<4)return!1;for(var e,r=0,n=t[e-1][1]*t[0][0]-t[e-1][0]*t[0][1];++rn^p>n&&r<(h-c)*(n-u)/(p-u)+c&&(i=!i)}return i}(t[0],r))return t.push(e),!0})||t.push([e])}),a=[],t.length?t.length>1?{type:"MultiPolygon",coordinates:t}:{type:"Polygon",coordinates:t[0]}:null}},c={Point:o,MultiPoint:o,LineString:s,MultiLineString:s,Polygon:l,MultiPolygon:l,Sphere:l};function u(){}var f=1e-6,h=f*f,p=Math.PI,d=p/2,g=(Math.sqrt(p),p/180),m=180/p;function v(t){return t>1?d:t<-1?-d:Math.asin(t)}function y(t){return t>1?0:t<-1?p:Math.acos(t)}var x=t.geo.projection,b=t.geo.projectionMutator;function _(t,e){var r=(2+d)*Math.sin(e);e/=2;for(var n=0,i=1/0;n<10&&Math.abs(i)>f;n++){var a=Math.cos(e);e-=i=(e+Math.sin(e)*(a+2)-r)/(2*a*(1+a))}return[2/Math.sqrt(p*(4+p))*t*(1+Math.cos(e)),2*Math.sqrt(p/(4+p))*Math.sin(e)]}t.geo.interrupt=function(e){var r,n=[[[[-p,0],[0,d],[p,0]]],[[[-p,0],[0,-d],[p,0]]]];function i(t,r){for(var i=r<0?-1:1,a=n[+(r<0)],o=0,s=a.length-1;oa[o][2][0];++o);var l=e(t-a[o][1][0],r);return l[0]+=e(a[o][1][0],i*r>i*a[o][0][1]?a[o][0][1]:r)[0],l}e.invert&&(i.invert=function(t,a){for(var o=r[+(a<0)],s=n[+(a<0)],c=0,u=o.length;c=0;--i){var o=n[1][i],l=180*o[0][0]/p,c=180*o[0][1]/p,u=180*o[1][1]/p,f=180*o[2][0]/p,h=180*o[2][1]/p;r.push(s([[f-e,h-e],[f-e,u+e],[l+e,u+e],[l+e,c-e]],30))}return{type:"Polygon",coordinates:[t.merge(r)]}}(),l)},i},a.lobes=function(t){return arguments.length?(n=t.map(function(t){return t.map(function(t){return[[t[0][0]*p/180,t[0][1]*p/180],[t[1][0]*p/180,t[1][1]*p/180],[t[2][0]*p/180,t[2][1]*p/180]]})}),r=n.map(function(t){return t.map(function(t){var r,n=e(t[0][0],t[0][1])[0],i=e(t[2][0],t[2][1])[0],a=e(t[1][0],t[0][1])[1],o=e(t[1][0],t[1][1])[1];return a>o&&(r=a,a=o,o=r),[[n,a],[i,o]]})}),a):n.map(function(t){return t.map(function(t){return[[180*t[0][0]/p,180*t[0][1]/p],[180*t[1][0]/p,180*t[1][1]/p],[180*t[2][0]/p,180*t[2][1]/p]]})})},a},_.invert=function(t,e){var r=.5*e*Math.sqrt((4+p)/p),n=v(r),i=Math.cos(n);return[t/(2/Math.sqrt(p*(4+p))*(1+i)),v((n+r*(i+2))/(2+d))]},(t.geo.eckert4=function(){return x(_)}).raw=_;var w=t.geo.azimuthalEqualArea.raw;function k(t,e){if(arguments.length<2&&(e=t),1===e)return w;if(e===1/0)return M;function r(r,n){var i=w(r/e,n);return i[0]*=t,i}return r.invert=function(r,n){var i=w.invert(r/t,n);return i[0]*=e,i},r}function M(t,e){return[t*Math.cos(e)/Math.cos(e/=2),2*Math.sin(e)]}function A(t,e){return[3*t/(2*p)*Math.sqrt(p*p/3-e*e),e]}function T(t,e){return[t,1.25*Math.log(Math.tan(p/4+.4*e))]}function S(t){return function(e){var r,n=t*Math.sin(e),i=30;do{e-=r=(e+Math.sin(e)-n)/(1+Math.cos(e))}while(Math.abs(r)>f&&--i>0);return e/2}}M.invert=function(t,e){var r=2*v(e/2);return[t*Math.cos(r/2)/Math.cos(r),r]},(t.geo.hammer=function(){var t=2,e=b(k),r=e(t);return r.coefficient=function(r){return arguments.length?e(t=+r):t},r}).raw=k,A.invert=function(t,e){return[2/3*p*t/Math.sqrt(p*p/3-e*e),e]},(t.geo.kavrayskiy7=function(){return x(A)}).raw=A,T.invert=function(t,e){return[t,2.5*Math.atan(Math.exp(.8*e))-.625*p]},(t.geo.miller=function(){return x(T)}).raw=T,S(p);var C=function(t,e,r){var n=S(r);function i(r,i){return[t*r*Math.cos(i=n(i)),e*Math.sin(i)]}return i.invert=function(n,i){var a=v(i/e);return[n/(t*Math.cos(a)),v((2*a+Math.sin(2*a))/r)]},i}(Math.SQRT2/d,Math.SQRT2,p);function E(t,e){var r=e*e,n=r*r;return[t*(.8707-.131979*r+n*(n*(.003971*r-.001529*n)-.013791)),e*(1.007226+r*(.015085+n*(.028874*r-.044475-.005916*n)))]}(t.geo.mollweide=function(){return x(C)}).raw=C,E.invert=function(t,e){var r,n=e,i=25;do{var a=n*n,o=a*a;n-=r=(n*(1.007226+a*(.015085+o*(.028874*a-.044475-.005916*o)))-e)/(1.007226+a*(.045255+o*(.259866*a-.311325-.005916*11*o)))}while(Math.abs(r)>f&&--i>0);return[t/(.8707+(a=n*n)*(a*(a*a*a*(.003971-.001529*a)-.013791)-.131979)),n]},(t.geo.naturalEarth=function(){return x(E)}).raw=E;var L=[[.9986,-.062],[1,0],[.9986,.062],[.9954,.124],[.99,.186],[.9822,.248],[.973,.31],[.96,.372],[.9427,.434],[.9216,.4958],[.8962,.5571],[.8679,.6176],[.835,.6769],[.7986,.7346],[.7597,.7903],[.7186,.8435],[.6732,.8936],[.6213,.9394],[.5722,.9761],[.5322,1]];function z(t,e){var r,n=Math.min(18,36*Math.abs(e)/p),i=Math.floor(n),a=n-i,o=(r=L[i])[0],s=r[1],l=(r=L[++i])[0],c=r[1],u=(r=L[Math.min(19,++i)])[0],f=r[1];return[t*(l+a*(u-o)/2+a*a*(u-2*l+o)/2),(e>0?d:-d)*(c+a*(f-s)/2+a*a*(f-2*c+s)/2)]}function P(t,e){return[t*Math.cos(e),e]}function D(t,e){var r,n=Math.cos(e),i=(r=y(n*Math.cos(t/=2)))?r/Math.sin(r):1;return[2*n*Math.sin(t)*i,Math.sin(e)*i]}function O(t,e){var r=D(t,e);return[(r[0]+t/d)/2,(r[1]+e)/2]}L.forEach(function(t){t[1]*=1.0144}),z.invert=function(t,e){var r=e/d,n=90*r,i=Math.min(18,Math.abs(n/5)),a=Math.max(0,Math.floor(i));do{var o=L[a][1],s=L[a+1][1],l=L[Math.min(19,a+2)][1],c=l-o,u=l-2*s+o,f=2*(Math.abs(r)-s)/c,p=u/c,v=f*(1-p*f*(1-2*p*f));if(v>=0||1===a){n=(e>=0?5:-5)*(v+i);var y,x=50;do{v=(i=Math.min(18,Math.abs(n)/5))-(a=Math.floor(i)),o=L[a][1],s=L[a+1][1],l=L[Math.min(19,a+2)][1],n-=(y=(e>=0?d:-d)*(s+v*(l-o)/2+v*v*(l-2*s+o)/2)-e)*m}while(Math.abs(y)>h&&--x>0);break}}while(--a>=0);var b=L[a][0],_=L[a+1][0],w=L[Math.min(19,a+2)][0];return[t/(_+v*(w-b)/2+v*v*(w-2*_+b)/2),n*g]},(t.geo.robinson=function(){return x(z)}).raw=z,P.invert=function(t,e){return[t/Math.cos(e),e]},(t.geo.sinusoidal=function(){return x(P)}).raw=P,D.invert=function(t,e){if(!(t*t+4*e*e>p*p+f)){var r=t,n=e,i=25;do{var a,o=Math.sin(r),s=Math.sin(r/2),l=Math.cos(r/2),c=Math.sin(n),u=Math.cos(n),h=Math.sin(2*n),d=c*c,g=u*u,m=s*s,v=1-g*l*l,x=v?y(u*l)*Math.sqrt(a=1/v):a=0,b=2*x*u*s-t,_=x*c-e,w=a*(g*m+x*u*l*d),k=a*(.5*o*h-2*x*c*s),M=.25*a*(h*s-x*c*g*o),A=a*(d*l+x*m*u),T=k*M-A*w;if(!T)break;var S=(_*k-b*A)/T,C=(b*M-_*w)/T;r-=S,n-=C}while((Math.abs(S)>f||Math.abs(C)>f)&&--i>0);return[r,n]}},(t.geo.aitoff=function(){return x(D)}).raw=D,O.invert=function(t,e){var r=t,n=e,i=25;do{var a,o=Math.cos(n),s=Math.sin(n),l=Math.sin(2*n),c=s*s,u=o*o,h=Math.sin(r),p=Math.cos(r/2),g=Math.sin(r/2),m=g*g,v=1-u*p*p,x=v?y(o*p)*Math.sqrt(a=1/v):a=0,b=.5*(2*x*o*g+r/d)-t,_=.5*(x*s+n)-e,w=.5*a*(u*m+x*o*p*c)+.5/d,k=a*(h*l/4-x*s*g),M=.125*a*(l*g-x*s*u*h),A=.5*a*(c*p+x*m*o)+.5,T=k*M-A*w,S=(_*k-b*A)/T,C=(b*M-_*w)/T;r-=S,n-=C}while((Math.abs(S)>f||Math.abs(C)>f)&&--i>0);return[r,n]},(t.geo.winkel3=function(){return x(O)}).raw=O}},{}],741:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../lib"),a=Math.PI/180,o=180/Math.PI,s={cursor:"pointer"},l={cursor:"auto"};function c(t,e){return n.behavior.zoom().translate(e.translate()).scale(e.scale())}function u(t,e,r){var n=t.id,a=t.graphDiv,o=a.layout[n],s=a._fullLayout[n],l={};function c(t,e){var r=i.nestedProperty(s,t);r.get()!==e&&(r.set(e),i.nestedProperty(o,t).set(e),l[n+"."+t]=e)}r(c),c("projection.scale",e.scale()/t.fitScale),a.emit("plotly_relayout",l)}function f(t,e){var r=c(0,e);function i(r){var n=e.invert(t.midPt);r("center.lon",n[0]),r("center.lat",n[1])}return r.on("zoomstart",function(){n.select(this).style(s)}).on("zoom",function(){e.scale(n.event.scale).translate(n.event.translate),t.render()}).on("zoomend",function(){n.select(this).style(l),u(t,e,i)}),r}function h(t,e){var r,i,a,o,f,h,p,d,g=c(0,e),m=2;function v(t){return e.invert(t)}function y(r){var n=e.rotate(),i=e.invert(t.midPt);r("projection.rotation.lon",-n[0]),r("center.lon",i[0]),r("center.lat",i[1])}return g.on("zoomstart",function(){n.select(this).style(s),r=n.mouse(this),i=e.rotate(),a=e.translate(),o=i,f=v(r)}).on("zoom",function(){if(h=n.mouse(this),l=e(v(s=r)),Math.abs(l[0]-s[0])>m||Math.abs(l[1]-s[1])>m)return g.scale(e.scale()),void g.translate(e.translate());var s,l;e.scale(n.event.scale),e.translate([a[0],n.event.translate[1]]),f?v(h)&&(d=v(h),p=[o[0]+(d[0]-f[0]),i[1],i[2]],e.rotate(p),o=p):f=v(r=h),t.render()}).on("zoomend",function(){n.select(this).style(l),u(t,e,y)}),g}function p(t,e){var r,i={r:e.rotate(),k:e.scale()},f=c(0,e),h=function(t){var e=0,r=arguments.length,i=[];for(;++ed?(a=(f>0?90:-90)-p,i=0):(a=Math.asin(f/d)*o-p,i=Math.sqrt(d*d-f*f));var m=180-a-2*p,y=(Math.atan2(h,u)-Math.atan2(c,i))*o,x=(Math.atan2(h,u)-Math.atan2(c,-i))*o,b=g(r[0],r[1],a,y),_=g(r[0],r[1],m,x);return b<=_?[a,y,r[2]]:[m,x,r[2]]}(k,r,C);isFinite(M[0])&&isFinite(M[1])&&isFinite(M[2])||(M=C),e.rotate(M),C=M}}else r=d(e,T=b);h.of(this,arguments)({type:"zoom"})}),A=h.of(this,arguments),p++||A({type:"zoomstart"})}).on("zoomend",function(){var r;n.select(this).style(l),m.call(f,"zoom",null),r=h.of(this,arguments),--p||r({type:"zoomend"}),u(t,e,x)}).on("zoom.redraw",function(){t.render()}),n.rebind(f,h,"on")}function d(t,e){var r=t.invert(e);return r&&isFinite(r[0])&&isFinite(r[1])&&function(t){var e=t[0]*a,r=t[1]*a,n=Math.cos(r);return[n*Math.cos(e),n*Math.sin(e),Math.sin(r)]}(r)}function g(t,e,r,n){var i=m(r-t),a=m(n-e);return Math.sqrt(i*i+a*a)}function m(t){return(t%360+540)%360-180}function v(t,e,r){var n=r*a,i=t.slice(),o=0===e?1:0,s=2===e?1:2,l=Math.cos(n),c=Math.sin(n);return i[o]=t[o]*l-t[s]*c,i[s]=t[s]*l+t[o]*c,i}function y(t,e){for(var r=0,n=0,i=t.length;nMath.abs(s)?(c.boxEnd[1]=c.boxStart[1]+Math.abs(a)*_*(s>=0?1:-1),c.boxEnd[1]l[3]&&(c.boxEnd[1]=l[3],c.boxEnd[0]=c.boxStart[0]+(l[3]-c.boxStart[1])/Math.abs(_))):(c.boxEnd[0]=c.boxStart[0]+Math.abs(s)/_*(a>=0?1:-1),c.boxEnd[0]l[2]&&(c.boxEnd[0]=l[2],c.boxEnd[1]=c.boxStart[1]+(l[2]-c.boxStart[0])*Math.abs(_)))}}else c.boxEnabled?(a=c.boxStart[0]!==c.boxEnd[0],s=c.boxStart[1]!==c.boxEnd[1],a||s?(a&&(m(0,c.boxStart[0],c.boxEnd[0]),t.xaxis.autorange=!1),s&&(m(1,c.boxStart[1],c.boxEnd[1]),t.yaxis.autorange=!1),t.relayoutCallback()):t.glplot.setDirty(),c.boxEnabled=!1,c.boxInited=!1):c.boxInited&&(c.boxInited=!1);break;case"pan":c.boxEnabled=!1,c.boxInited=!1,e?(c.panning||(c.dragStart[0]=n,c.dragStart[1]=i),Math.abs(c.dragStart[0]-n)Math.abs(e))c.rotate(a,0,0,-t*r*Math.PI*d.rotateSpeed/window.innerWidth);else{var o=-d.zoomSpeed*i*e/window.innerHeight*(a-c.lastT())/20;c.pan(a,0,0,f*(Math.exp(o)-1))}}},!0),d};var n=t("right-now"),i=t("3d-view"),a=t("mouse-change"),o=t("mouse-wheel"),s=t("mouse-event-offset"),l=t("has-passive-events")},{"3d-view":42,"has-passive-events":355,"mouse-change":378,"mouse-event-offset":379,"mouse-wheel":381,"right-now":440}],748:[function(t,e,r){"use strict";var n=t("../../plot_api/edit_types").overrideAll,i=t("../../components/fx/layout_attributes"),a=t("./scene"),o=t("../get_data").getSubplotData,s=t("../../lib"),l=t("../../constants/xmlns_namespaces");r.name="gl3d",r.attr="scene",r.idRoot="scene",r.idRegex=r.attrRegex=s.counterRegex("scene"),r.attributes=t("./layout/attributes"),r.layoutAttributes=t("./layout/layout_attributes"),r.baseLayoutAttrOverrides=n({hoverlabel:i.hoverlabel},"plot","nested"),r.supplyLayoutDefaults=t("./layout/defaults"),r.plot=function(t){for(var e=t._fullLayout,r=t._fullData,n=e._subplots.gl3d,i=0;i1;o(t,e,r,{type:"gl3d",attributes:l,handleDefaults:c,fullLayout:e,font:e.font,fullData:r,getDfltFromLayout:function(e){if(!i)return n.validate(t[e],l[e])?t[e]:void 0},paper_bgcolor:e.paper_bgcolor,calendar:e.calendar})}},{"../../../components/color":532,"../../../lib":660,"../../../registry":790,"../../subplot_defaults":782,"./axis_defaults":751,"./layout_attributes":754}],754:[function(t,e,r){"use strict";var n=t("./axis_attributes"),i=t("../../domain").attributes,a=t("../../../lib/extend").extendFlat,o=t("../../../lib").counterRegex;function s(t,e,r){return{x:{valType:"number",dflt:t,editType:"camera"},y:{valType:"number",dflt:e,editType:"camera"},z:{valType:"number",dflt:r,editType:"camera"},editType:"camera"}}e.exports={_arrayAttrRegexps:[o("scene",".annotations",!0)],bgcolor:{valType:"color",dflt:"rgba(0,0,0,0)",editType:"plot"},camera:{up:a(s(0,0,1),{}),center:a(s(0,0,0),{}),eye:a(s(1.25,1.25,1.25),{}),editType:"camera"},domain:i({name:"scene",editType:"plot"}),aspectmode:{valType:"enumerated",values:["auto","cube","data","manual"],dflt:"auto",editType:"plot",impliedEdits:{"aspectratio.x":void 0,"aspectratio.y":void 0,"aspectratio.z":void 0}},aspectratio:{x:{valType:"number",min:0,editType:"plot",impliedEdits:{"^aspectmode":"manual"}},y:{valType:"number",min:0,editType:"plot",impliedEdits:{"^aspectmode":"manual"}},z:{valType:"number",min:0,editType:"plot",impliedEdits:{"^aspectmode":"manual"}},editType:"plot",impliedEdits:{aspectmode:"manual"}},xaxis:n,yaxis:n,zaxis:n,dragmode:{valType:"enumerated",values:["orbit","turntable","zoom","pan",!1],dflt:"turntable",editType:"plot"},hovermode:{valType:"enumerated",values:["closest",!1],dflt:"closest",editType:"modebar"},editType:"plot",_deprecated:{cameraposition:{valType:"info_array",editType:"camera"}}}},{"../../../lib":660,"../../../lib/extend":649,"../../domain":731,"./axis_attributes":750}],755:[function(t,e,r){"use strict";var n=t("../../../lib/str2rgbarray"),i=["xaxis","yaxis","zaxis"];function a(){this.enabled=[!0,!0,!0],this.colors=[[0,0,0,1],[0,0,0,1],[0,0,0,1]],this.drawSides=[!0,!0,!0],this.lineWidth=[1,1,1]}a.prototype.merge=function(t){for(var e=0;e<3;++e){var r=t[i[e]];r.visible?(this.enabled[e]=r.showspikes,this.colors[e]=n(r.spikecolor),this.drawSides[e]=r.spikesides,this.lineWidth[e]=r.spikethickness):(this.enabled[e]=!1,this.drawSides[e]=!1)}},e.exports=function(t){var e=new a;return e.merge(t),e}},{"../../../lib/str2rgbarray":683}],756:[function(t,e,r){"use strict";e.exports=function(t){for(var e=t.axesOptions,r=t.glplot.axesPixels,l=t.fullSceneLayout,c=[[],[],[]],u=0;u<3;++u){var f=l[o[u]];if(f._length=(r[u].hi-r[u].lo)*r[u].pixelsPerDataUnit/t.dataScale[u],Math.abs(f._length)===1/0)c[u]=[];else{f._input_range=f.range.slice(),f.range[0]=r[u].lo/t.dataScale[u],f.range[1]=r[u].hi/t.dataScale[u],f._m=1/(t.dataScale[u]*r[u].pixelsPerDataUnit),f.range[0]===f.range[1]&&(f.range[0]-=1,f.range[1]+=1);var h=f.tickmode;if("auto"===f.tickmode){f.tickmode="linear";var p=f.nticks||i.constrain(f._length/40,4,9);n.autoTicks(f,Math.abs(f.range[1]-f.range[0])/p)}for(var d=n.calcTicks(f),g=0;g")}else m=c.textLabel;t.fullSceneLayout.hovermode&&f.loneHover({x:(.5+.5*d[0]/d[3])*i,y:(.5-.5*d[1]/d[3])*a,xLabel:w,yLabel:k,zLabel:M,text:m,name:l.name,color:f.castHoverOption(e,v,"bgcolor")||l.color,borderColor:f.castHoverOption(e,v,"bordercolor"),fontFamily:f.castHoverOption(e,v,"font.family"),fontSize:f.castHoverOption(e,v,"font.size"),fontColor:f.castHoverOption(e,v,"font.color")},{container:r,gd:t.graphDiv});var T={x:c.traceCoordinate[0],y:c.traceCoordinate[1],z:c.traceCoordinate[2],data:e._input,fullData:e,curveNumber:e.index,pointNumber:v};f.appendArrayPointValue(T,e,v);var S={points:[T]};c.buttons&&c.distance<5?t.graphDiv.emit("plotly_click",S):t.graphDiv.emit("plotly_hover",S),o=S}else f.loneUnhover(r),t.graphDiv.emit("plotly_unhover",o);t.drawAnnotations(t)}.bind(null,t),t.traces={},!0}function b(t,e){var r=document.createElement("div"),n=t.container;this.graphDiv=t.graphDiv;var i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.style.position="absolute",i.style.top=i.style.left="0px",i.style.width=i.style.height="100%",i.style["z-index"]=20,i.style["pointer-events"]="none",r.appendChild(i),this.svgContainer=i,r.id=t.id,r.style.position="absolute",r.style.top=r.style.left="0px",r.style.width=r.style.height="100%",n.appendChild(r),this.fullLayout=e,this.id=t.id||"scene",this.fullSceneLayout=e[this.id],this.plotArgs=[[],{},{}],this.axesOptions=m(e[this.id]),this.spikeOptions=v(e[this.id]),this.container=r,this.staticMode=!!t.staticPlot,this.pixelRatio=t.plotGlPixelRatio||2,this.dataScale=[1,1,1],this.contourLevels=[[],[],[]],this.convertAnnotations=l.getComponentMethod("annotations3d","convert"),this.drawAnnotations=l.getComponentMethod("annotations3d","draw"),x(this)}var _=b.prototype;_.recoverContext=function(){var t=this,e=this.glplot.gl,r=this.glplot.canvas;this.glplot.dispose(),requestAnimationFrame(function n(){e.isContextLost()?requestAnimationFrame(n):x(t,t.fullLayout,r,e)?t.plot.apply(t,t.plotArgs):c.error("Catastrophic and unrecoverable WebGL error. Context lost.")})};var w=["xaxis","yaxis","zaxis"];function k(t,e,r){for(var n=t.fullSceneLayout,i=0;i<3;i++){var a=w[i],o=a.charAt(0),s=n[a],l=e[o],u=e[o+"calendar"],f=e["_"+o+"length"];if(c.isArrayOrTypedArray(l))for(var h,p=0;p<(f||l.length);p++)if(c.isArrayOrTypedArray(l[p]))for(var d=0;df[1][o]?p[o]=1:f[1][o]===f[0][o]?p[o]=1:p[o]=1/(f[1][o]-f[0][o]);for(this.dataScale=p,this.convertAnnotations(this),a=0;ag[1][a])g[0][a]=-1,g[1][a]=1;else{var C=g[1][a]-g[0][a];g[0][a]-=C/32,g[1][a]+=C/32}}else{var E=s.range;g[0][a]=s.r2l(E[0]),g[1][a]=s.r2l(E[1])}g[0][a]===g[1][a]&&(g[0][a]-=1,g[1][a]+=1),m[a]=g[1][a]-g[0][a],this.glplot.bounds[0][a]=g[0][a]*p[a],this.glplot.bounds[1][a]=g[1][a]*p[a]}var L=[1,1,1];for(a=0;a<3;++a){var z=v[l=(s=c[w[a]]).type];L[a]=Math.pow(z.acc,1/z.count)/p[a]}var P;if("auto"===c.aspectmode)P=Math.max.apply(null,L)/Math.min.apply(null,L)<=4?L:[1,1,1];else if("cube"===c.aspectmode)P=[1,1,1];else if("data"===c.aspectmode)P=L;else{if("manual"!==c.aspectmode)throw new Error("scene.js aspectRatio was not one of the enumerated types");var D=c.aspectratio;P=[D.x,D.y,D.z]}c.aspectratio.x=u.aspectratio.x=P[0],c.aspectratio.y=u.aspectratio.y=P[1],c.aspectratio.z=u.aspectratio.z=P[2],this.glplot.aspect=P;var O=c.domain||null,I=e._size||null;if(O&&I){var R=this.container.style;R.position="absolute",R.left=I.l+O.x[0]*I.w+"px",R.top=I.t+(1-O.y[1])*I.h+"px",R.width=I.w*(O.x[1]-O.x[0])+"px",R.height=I.h*(O.y[1]-O.y[0])+"px"}this.glplot.redraw()}},_.destroy=function(){this.glplot&&(this.camera.mouseListener.enabled=!1,this.container.removeEventListener("wheel",this.camera.wheelListener),this.camera=this.glplot.camera=null,this.glplot.dispose(),this.container.parentNode.removeChild(this.container),this.glplot=null)},_.getCamera=function(){return this.glplot.camera.view.recalcMatrix(this.camera.view.lastT()),M(this.glplot.camera)},_.setCamera=function(t){var e;this.glplot.camera.lookAt.apply(this,[[(e=t).eye.x,e.eye.y,e.eye.z],[e.center.x,e.center.y,e.center.z],[e.up.x,e.up.y,e.up.z]])},_.saveCamera=function(t){var e=this.getCamera(),r=c.nestedProperty(t,this.id+".camera"),n=r.get(),i=!1;function a(t,e,r,n){var i=["up","center","eye"],a=["x","y","z"];return e[i[r]]&&t[i[r]][a[n]]===e[i[r]][a[n]]}if(void 0===n)i=!0;else for(var o=0;o<3;o++)for(var s=0;s<3;s++)if(!a(e,n,o,s)){i=!0;break}return i&&r.set(e),i},_.updateFx=function(t,e){var r=this.camera;r&&("orbit"===t?(r.mode="orbit",r.keyBindingMode="rotate"):"turntable"===t?(r.up=[0,0,1],r.mode="turntable",r.keyBindingMode="rotate"):r.keyBindingMode=t),this.fullSceneLayout.hovermode=e},_.toImage=function(t){t||(t="png"),this.staticMode&&this.container.appendChild(n),this.glplot.redraw();var e=this.glplot.gl,r=e.drawingBufferWidth,i=e.drawingBufferHeight;e.bindFramebuffer(e.FRAMEBUFFER,null);var a=new Uint8Array(r*i*4);e.readPixels(0,0,r,i,e.RGBA,e.UNSIGNED_BYTE,a);for(var o=0,s=i-1;o0}function l(t){var e={},r={};switch(t.type){case"circle":n.extendFlat(r,{"circle-radius":t.circle.radius,"circle-color":t.color,"circle-opacity":t.opacity});break;case"line":n.extendFlat(r,{"line-width":t.line.width,"line-color":t.color,"line-opacity":t.opacity});break;case"fill":n.extendFlat(r,{"fill-color":t.color,"fill-outline-color":t.fill.outlinecolor,"fill-opacity":t.opacity});break;case"symbol":var a=t.symbol,o=i(a.textposition,a.iconsize);n.extendFlat(e,{"icon-image":a.icon+"-15","icon-size":a.iconsize/10,"text-field":a.text,"text-size":a.textfont.size,"text-anchor":o.anchor,"text-offset":o.offset}),n.extendFlat(r,{"icon-color":t.color,"text-color":a.textfont.color,"text-opacity":t.opacity})}return{layout:e,paint:r}}o.update=function(t){this.visible?this.needsNewSource(t)?(this.updateLayer(t),this.updateSource(t)):this.needsNewLayer(t)?this.updateLayer(t):this.updateStyle(t):(this.updateSource(t),this.updateLayer(t)),this.visible=s(t)},o.needsNewSource=function(t){return this.sourceType!==t.sourcetype||this.source!==t.source||this.layerType!==t.type},o.needsNewLayer=function(t){return this.layerType!==t.type||this.below!==t.below},o.updateSource=function(t){var e=this.map;if(e.getSource(this.idSource)&&e.removeSource(this.idSource),this.sourceType=t.sourcetype,this.source=t.source,s(t)){var r=function(t){var e,r=t.sourcetype,n=t.source,i={type:r};"geojson"===r?e="data":"vector"===r&&(e="string"==typeof n?"url":"tiles");return i[e]=n,i}(t);e.addSource(this.idSource,r)}},o.updateLayer=function(t){var e=this.map,r=l(t);e.getLayer(this.idLayer)&&e.removeLayer(this.idLayer),this.layerType=t.type,s(t)&&e.addLayer({id:this.idLayer,source:this.idSource,"source-layer":t.sourcelayer||"",type:t.type,layout:r.layout,paint:r.paint},t.below)},o.updateStyle=function(t){if(s(t)){var e=l(t);this.mapbox.setOptions(this.idLayer,"setLayoutProperty",e.layout),this.mapbox.setOptions(this.idLayer,"setPaintProperty",e.paint)}},o.dispose=function(){var t=this.map;t.removeLayer(this.idLayer),t.removeSource(this.idSource)},e.exports=function(t,e,r){var n=new a(t,e);return n.update(r),n}},{"../../lib":660,"./convert_text_opts":761}],764:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../../components/color").defaultLine,a=t("../domain").attributes,o=t("../font_attributes"),s=t("../../traces/scatter/attributes").textposition,l=t("../../plot_api/edit_types").overrideAll,c=o({});c.family.dflt="Open Sans Regular, Arial Unicode MS Regular",e.exports=l({_arrayAttrRegexps:[n.counterRegex("mapbox",".layers",!0)],domain:a({name:"mapbox"}),accesstoken:{valType:"string",noBlank:!0,strict:!0},style:{valType:"any",values:["basic","streets","outdoors","light","dark","satellite","satellite-streets"],dflt:"basic"},center:{lon:{valType:"number",dflt:0},lat:{valType:"number",dflt:0}},zoom:{valType:"number",dflt:1},bearing:{valType:"number",dflt:0},pitch:{valType:"number",dflt:0},layers:{_isLinkedToArray:"layer",sourcetype:{valType:"enumerated",values:["geojson","vector"],dflt:"geojson"},source:{valType:"any"},sourcelayer:{valType:"string",dflt:""},type:{valType:"enumerated",values:["circle","line","fill","symbol"],dflt:"circle"},below:{valType:"string",dflt:""},color:{valType:"color",dflt:i},opacity:{valType:"number",min:0,max:1,dflt:1},circle:{radius:{valType:"number",dflt:15}},line:{width:{valType:"number",dflt:2}},fill:{outlinecolor:{valType:"color",dflt:i}},symbol:{icon:{valType:"string",dflt:"marker"},iconsize:{valType:"number",dflt:10},text:{valType:"string",dflt:""},textfont:c,textposition:n.extendFlat({},s,{arrayOk:!1})}}},"plot","from-root")},{"../../components/color":532,"../../lib":660,"../../plot_api/edit_types":691,"../../traces/scatter/attributes":990,"../domain":731,"../font_attributes":732}],765:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../subplot_defaults"),a=t("./layout_attributes");function o(t,e,r,i){r("accesstoken",i.accessToken),r("style"),r("center.lon"),r("center.lat"),r("zoom"),r("bearing"),r("pitch"),function(t,e){var r,i,o=t.layers||[],s=e.layers=[];function l(t,e){return n.coerce(r,i,a.layers,t,e)}for(var c=0;c=e.width-20?(a["text-anchor"]="start",a.x=5):(a["text-anchor"]="end",a.x=e._paper.attr("width")-7),r.attr(a);var o=r.select(".js-link-to-tool"),c=r.select(".js-link-spacer"),u=r.select(".js-sourcelinks");t._context.showSources&&t._context.showSources(t),t._context.showLink&&function(t,e){e.text("");var r=e.append("a").attr({"xlink:xlink:href":"#",class:"link--impt link--embedview","font-weight":"bold"}).text(t._context.linkText+" "+String.fromCharCode(187));if(t._context.sendData)r.on("click",function(){m.sendDataToCloud(t)});else{var n=window.location.pathname.split("/"),i=window.location.search;r.attr({"xlink:xlink:show":"new","xlink:xlink:href":"/"+n[2].split(".")[0]+"/"+n[1]+i})}}(t,o),c.text(o.text()&&u.text()?" - ":"")}},m.sendDataToCloud=function(t){t.emit("plotly_beforeexport");var e=window.PLOTLYENV&&window.PLOTLYENV.BASE_URL||"https://plot.ly",r=n.select(t).append("div").attr("id","hiddenform").style("display","none"),i=r.append("form").attr({action:e+"/external",method:"post",target:"_blank"});return i.append("input").attr({type:"text",name:"data"}).node().value=m.graphJson(t,!1,"keepdata"),i.node().submit(),r.remove(),t.emit("plotly_afterexport"),!1};var x,b=["days","shortDays","months","shortMonths","periods","dateTime","date","time","decimal","thousands","grouping","currency"],_=["year","month","dayMonth","dayMonthYear"];function w(t,e){var r=t._context.locale,n=!1,i={};function o(t){for(var r=!0,a=0;a1&&O.length>1){for(a.getComponentMethod("grid","sizeDefaults")(c,l),o=0;o15&&O.length>15&&0===l.shapes.length&&0===l.images.length,l._hasCartesian=l._has("cartesian"),l._hasGeo=l._has("geo"),l._hasGL3D=l._has("gl3d"),l._hasGL2D=l._has("gl2d"),l._hasTernary=l._has("ternary"),l._hasPie=l._has("pie"),m.linkSubplots(p,l,h,i),m.cleanPlot(p,l,h,i,y),d(l,i),m.doAutoMargin(t);var F=u.list(t);for(o=0;o0){var u=function(t){var e,r={left:0,right:0,bottom:0,top:0};if(t)for(e in t)t.hasOwnProperty(e)&&(r.left+=t[e].left||0,r.right+=t[e].right||0,r.bottom+=t[e].bottom||0,r.top+=t[e].top||0);return r}(t._boundingBoxMargins),f=u.left+u.right,h=u.bottom+u.top,p=1-2*l,d=r._container&&r._container.node?r._container.node().getBoundingClientRect():{width:r.width,height:r.height};n=Math.round(p*(d.width-f)),a=Math.round(p*(d.height-h))}else{var g=c?window.getComputedStyle(t):{};n=parseFloat(g.width)||r.width,a=parseFloat(g.height)||r.height}var v=m.layoutAttributes.width.min,y=m.layoutAttributes.height.min;n1,b=!e.height&&Math.abs(r.height-a)>1;(b||x)&&(x&&(r.width=n),b&&(r.height=a)),t._initialAutoSize||(t._initialAutoSize={width:n,height:a}),m.sanitizeMargins(r)},m.supplyLayoutModuleDefaults=function(t,e,r,n){var i,o,l,c=a.componentsRegistry,u=e._basePlotModules,f=a.subplotsRegistry.cartesian;for(i in c)(l=c[i]).includeBasePlot&&l.includeBasePlot(t,e);for(var h in u.length||u.push(f),e._has("cartesian")&&(a.getComponentMethod("grid","contentDefaults")(t,e),f.finalizeSubplots(t,e)),e._subplots)e._subplots[h].sort(s.subplotSort);for(o=0;o.5*n.width&&(r.l=r.r=0),r.b+r.t>.5*n.height&&(r.b=r.t=0),n._pushmargin[e]={l:{val:r.x,size:r.l+i},r:{val:r.x,size:r.r+i},b:{val:r.y,size:r.b+i},t:{val:r.y,size:r.t+i}}}else delete n._pushmargin[e];n._replotting||m.doAutoMargin(t)}},m.doAutoMargin=function(t){var e=t._fullLayout;e._size||(e._size={}),e._pushmargin||(e._pushmargin={});var r=e._size,n=JSON.stringify(r),o=Math.max(e.margin.l||0,0),s=Math.max(e.margin.r||0,0),l=Math.max(e.margin.t||0,0),c=Math.max(e.margin.b||0,0),u=e._pushmargin;if(!1!==e.margin.autoexpand)for(var f in u.base={l:{val:0,size:o},r:{val:1,size:s},t:{val:1,size:l},b:{val:0,size:c}},u){var h=u[f].l||{},p=u[f].b||{},d=h.val,g=h.size,m=p.val,v=p.size;for(var y in u){if(i(g)&&u[y].r){var x=u[y].r.val,b=u[y].r.size;if(x>d){var _=(g*x+(b-e.width)*d)/(x-d),w=(b*(1-d)+(g-e.width)*(1-x))/(x-d);_>=0&&w>=0&&_+w>o+s&&(o=_,s=w)}}if(i(v)&&u[y].t){var k=u[y].t.val,M=u[y].t.size;if(k>m){var A=(v*k+(M-e.height)*m)/(k-m),T=(M*(1-m)+(v-e.height)*(1-k))/(k-m);A>=0&&T>=0&&A+T>c+l&&(c=A,l=T)}}}}if(r.l=Math.round(o),r.r=Math.round(s),r.t=Math.round(l),r.b=Math.round(c),r.p=Math.round(e.margin.pad),r.w=Math.round(e.width)-r.l-r.r,r.h=Math.round(e.height)-r.t-r.b,!e._replotting&&"{}"!==n&&n!==JSON.stringify(e._size))return a.call("plot",t)},m.graphJson=function(t,e,r,n,i){(i&&e&&!t._fullData||i&&!e&&!t._fullLayout)&&m.supplyDefaults(t);var a=i?t._fullData:t.data,o=i?t._fullLayout:t.layout,l=(t._transitionData||{})._frames;function c(t){if("function"==typeof t)return null;if(s.isPlainObject(t)){var e,n,i={};for(e in t)if("function"!=typeof t[e]&&-1===["_","["].indexOf(e.charAt(0))){if("keepdata"===r){if("src"===e.substr(e.length-3))continue}else if("keepstream"===r){if("string"==typeof(n=t[e+"src"])&&n.indexOf(":")>0&&!s.isPlainObject(t.stream))continue}else if("keepall"!==r&&"string"==typeof(n=t[e+"src"])&&n.indexOf(":")>0)continue;i[e]=c(t[e])}return i}return Array.isArray(t)?t.map(c):s.isJSDate(t)?s.ms2DateTimeLocal(+t):t}var u={data:(a||[]).map(function(t){var r=c(t);return e&&delete r.fit,r})};return e||(u.layout=c(o)),t.framework&&t.framework.isPolar&&(u=t.framework.getConfig()),l&&(u.frames=c(l)),"object"===n?u:JSON.stringify(u)},m.modifyFrames=function(t,e){var r,n,i,a=t._transitionData._frames,o=t._transitionData._frameHash;for(r=0;r0&&(t._transitioningWithDuration=!0),t._transitionData._interruptCallbacks.push(function(){p=!0}),i.redraw&&t._transitionData._interruptCallbacks.push(function(){return a.call("redraw",t)}),t._transitionData._interruptCallbacks.push(function(){t.emit("plotly_transitioninterrupted",[])});var n,l,c=0,u=0;function f(){return c++,function(){var r;u++,p||u!==c||(r=e,t._transitionData&&(function(t){if(t)for(;t.length;)t.shift()}(t._transitionData._interruptCallbacks),Promise.resolve().then(function(){if(i.redraw)return a.call("redraw",t)}).then(function(){t._transitioning=!1,t._transitioningWithDuration=!1,t.emit("plotly_transitioned",[])}).then(r)))}}var d=t._fullLayout._basePlotModules,g=!1;if(r)for(l=0;l=0;s--)if(o[s].enabled){r._indexToPoints=o[s]._indexToPoints;break}n&&n.calc&&(a=n.calc(t,r))}Array.isArray(a)&&a[0]||(a=[{x:c,y:c}]),a[0].t||(a[0].t={}),a[0].trace=r,p[e]=a}}for(m&&M(l),i=0;i=0?h.angularAxis.domain:n.extent(k),C=Math.abs(k[1]-k[0]);A&&!M&&(C=0);var E=S.slice();T&&M&&(E[1]+=C);var L=h.angularAxis.ticksCount||4;L>8&&(L=L/(L/8)+L%8),h.angularAxis.ticksStep&&(L=(E[1]-E[0])/L);var z=h.angularAxis.ticksStep||(E[1]-E[0])/(L*(h.minorTicks+1));w&&(z=Math.max(Math.round(z),1)),E[2]||(E[2]=z);var P=n.range.apply(this,E);if(P=P.map(function(t,e){return parseFloat(t.toPrecision(12))}),s=n.scale.linear().domain(E.slice(0,2)).range("clockwise"===h.direction?[0,360]:[360,0]),u.layout.angularAxis.domain=s.domain(),u.layout.angularAxis.endPadding=T?C:0,void 0===(t=n.select(this).select("svg.chart-root"))||t.empty()){var D=(new DOMParser).parseFromString("' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '","application/xml"),O=this.appendChild(this.ownerDocument.importNode(D.documentElement,!0));t=n.select(O)}t.select(".guides-group").style({"pointer-events":"none"}),t.select(".angular.axis-group").style({"pointer-events":"none"}),t.select(".radial.axis-group").style({"pointer-events":"none"});var I,R=t.select(".chart-group"),B={fill:"none",stroke:h.tickColor},F={"font-size":h.font.size,"font-family":h.font.family,fill:h.font.color,"text-shadow":["-1px 0px","1px -1px","-1px 1px","1px 1px"].map(function(t,e){return" "+t+" 0 "+h.font.outlineColor}).join(",")};if(h.showLegend){I=t.select(".legend-group").attr({transform:"translate("+[x,h.margin.top]+")"}).style({display:"block"});var N=p.map(function(t,e){var r=o.util.cloneJson(t);return r.symbol="DotPlot"===t.geometry?t.dotType||"circle":"LinePlot"!=t.geometry?"square":"line",r.visibleInLegend=void 0===t.visibleInLegend||t.visibleInLegend,r.color="LinePlot"===t.geometry?t.strokeColor:t.color,r});o.Legend().config({data:p.map(function(t,e){return t.name||"Element"+e}),legendConfig:i({},o.Legend.defaultConfig().legendConfig,{container:I,elements:N,reverseOrder:h.legend.reverseOrder})})();var j=I.node().getBBox();x=Math.min(h.width-j.width-h.margin.left-h.margin.right,h.height-h.margin.top-h.margin.bottom)/2,x=Math.max(10,x),_=[h.margin.left+x,h.margin.top+x],r.range([0,x]),u.layout.radialAxis.domain=r.domain(),I.attr("transform","translate("+[_[0]+x,_[1]-x]+")")}else I=t.select(".legend-group").style({display:"none"});t.attr({width:h.width,height:h.height}).style({opacity:h.opacity}),R.attr("transform","translate("+_+")").style({cursor:"crosshair"});var V=[(h.width-(h.margin.left+h.margin.right+2*x+(j?j.width:0)))/2,(h.height-(h.margin.top+h.margin.bottom+2*x))/2];if(V[0]=Math.max(0,V[0]),V[1]=Math.max(0,V[1]),t.select(".outer-group").attr("transform","translate("+V+")"),h.title){var U=t.select("g.title-group text").style(F).text(h.title),q=U.node().getBBox();U.attr({x:_[0]-q.width/2,y:_[1]-x-20})}var H=t.select(".radial.axis-group");if(h.radialAxis.gridLinesVisible){var G=H.selectAll("circle.grid-circle").data(r.ticks(5));G.enter().append("circle").attr({class:"grid-circle"}).style(B),G.attr("r",r),G.exit().remove()}H.select("circle.outside-circle").attr({r:x}).style(B);var W=t.select("circle.background-circle").attr({r:x}).style({fill:h.backgroundColor,stroke:h.stroke});function Y(t,e){return s(t)%360+h.orientation}if(h.radialAxis.visible){var X=n.svg.axis().scale(r).ticks(5).tickSize(5);H.call(X).attr({transform:"rotate("+h.radialAxis.orientation+")"}),H.selectAll(".domain").style(B),H.selectAll("g>text").text(function(t,e){return this.textContent+h.radialAxis.ticksSuffix}).style(F).style({"text-anchor":"start"}).attr({x:0,y:0,dx:0,dy:0,transform:function(t,e){return"horizontal"===h.radialAxis.tickOrientation?"rotate("+-h.radialAxis.orientation+") translate("+[0,F["font-size"]]+")":"translate("+[0,F["font-size"]]+")"}}),H.selectAll("g>line").style({stroke:"black"})}var Z=t.select(".angular.axis-group").selectAll("g.angular-tick").data(P),J=Z.enter().append("g").classed("angular-tick",!0);Z.attr({transform:function(t,e){return"rotate("+Y(t)+")"}}).style({display:h.angularAxis.visible?"block":"none"}),Z.exit().remove(),J.append("line").classed("grid-line",!0).classed("major",function(t,e){return e%(h.minorTicks+1)==0}).classed("minor",function(t,e){return!(e%(h.minorTicks+1)==0)}).style(B),J.selectAll(".minor").style({stroke:h.minorTickColor}),Z.select("line.grid-line").attr({x1:h.tickLength?x-h.tickLength:0,x2:x}).style({display:h.angularAxis.gridLinesVisible?"block":"none"}),J.append("text").classed("axis-text",!0).style(F);var K=Z.select("text.axis-text").attr({x:x+h.labelOffset,dy:a+"em",transform:function(t,e){var r=Y(t),n=x+h.labelOffset,i=h.angularAxis.tickOrientation;return"horizontal"==i?"rotate("+-r+" "+n+" 0)":"radial"==i?r<270&&r>90?"rotate(180 "+n+" 0)":null:"rotate("+(r<=180&&r>0?-90:90)+" "+n+" 0)"}}).style({"text-anchor":"middle",display:h.angularAxis.labelsVisible?"block":"none"}).text(function(t,e){return e%(h.minorTicks+1)!=0?"":w?w[t]+h.angularAxis.ticksSuffix:t+h.angularAxis.ticksSuffix}).style(F);h.angularAxis.rewriteTicks&&K.text(function(t,e){return e%(h.minorTicks+1)!=0?"":h.angularAxis.rewriteTicks(this.textContent,e)});var Q=n.max(R.selectAll(".angular-tick text")[0].map(function(t,e){return t.getCTM().e+t.getBBox().width}));I.attr({transform:"translate("+[x+Q,h.margin.top]+")"});var $=t.select("g.geometry-group").selectAll("g").size()>0,tt=t.select("g.geometry-group").selectAll("g.geometry").data(p);if(tt.enter().append("g").attr({class:function(t,e){return"geometry geometry"+e}}),tt.exit().remove(),p[0]||$){var et=[];p.forEach(function(t,e){var n={};n.radialScale=r,n.angularScale=s,n.container=tt.filter(function(t,r){return r==e}),n.geometry=t.geometry,n.orientation=h.orientation,n.direction=h.direction,n.index=e,et.push({data:t,geometryConfig:n})});var rt=n.nest().key(function(t,e){return void 0!==t.data.groupId||"unstacked"}).entries(et),nt=[];rt.forEach(function(t,e){"unstacked"===t.key?nt=nt.concat(t.values.map(function(t,e){return[t]})):nt.push(t.values)}),nt.forEach(function(t,e){var r;r=Array.isArray(t)?t[0].geometryConfig.geometry:t.geometryConfig.geometry;var n=t.map(function(t,e){return i(o[r].defaultConfig(),t)});o[r]().config(n)()})}var it,at,ot=t.select(".guides-group"),st=t.select(".tooltips-group"),lt=o.tooltipPanel().config({container:st,fontSize:8})(),ct=o.tooltipPanel().config({container:st,fontSize:8})(),ut=o.tooltipPanel().config({container:st,hasTick:!0})();if(!M){var ft=ot.select("line").attr({x1:0,y1:0,y2:0}).style({stroke:"grey","pointer-events":"none"});R.on("mousemove.angular-guide",function(t,e){var r=o.util.getMousePos(W).angle;ft.attr({x2:-x,transform:"rotate("+r+")"}).style({opacity:.5});var n=(r+180+360-h.orientation)%360;it=s.invert(n);var i=o.util.convertToCartesian(x+12,r+180);lt.text(o.util.round(it)).move([i[0]+_[0],i[1]+_[1]])}).on("mouseout.angular-guide",function(t,e){ot.select("line").style({opacity:0})})}var ht=ot.select("circle").style({stroke:"grey",fill:"none"});R.on("mousemove.radial-guide",function(t,e){var n=o.util.getMousePos(W).radius;ht.attr({r:n}).style({opacity:.5}),at=r.invert(o.util.getMousePos(W).radius);var i=o.util.convertToCartesian(n,h.radialAxis.orientation);ct.text(o.util.round(at)).move([i[0]+_[0],i[1]+_[1]])}).on("mouseout.radial-guide",function(t,e){ht.style({opacity:0}),ut.hide(),lt.hide(),ct.hide()}),t.selectAll(".geometry-group .mark").on("mouseover.tooltip",function(e,r){var i=n.select(this),a=this.style.fill,s="black",l=this.style.opacity||1;if(i.attr({"data-opacity":l}),a&&"none"!==a){i.attr({"data-fill":a}),s=n.hsl(a).darker().toString(),i.style({fill:s,opacity:1});var c={t:o.util.round(e[0]),r:o.util.round(e[1])};M&&(c.t=w[e[0]]);var u="t: "+c.t+", r: "+c.r,f=this.getBoundingClientRect(),h=t.node().getBoundingClientRect(),p=[f.left+f.width/2-V[0]-h.left,f.top+f.height/2-V[1]-h.top];ut.config({color:s}).text(u),ut.move(p)}else a=this.style.stroke||"black",i.attr({"data-stroke":a}),s=n.hsl(a).darker().toString(),i.style({stroke:s,opacity:1})}).on("mousemove.tooltip",function(t,e){if(0!=n.event.which)return!1;n.select(this).attr("data-fill")&&ut.show()}).on("mouseout.tooltip",function(t,e){ut.hide();var r=n.select(this),i=r.attr("data-fill");i?r.style({fill:i,opacity:r.attr("data-opacity")}):r.style({stroke:r.attr("data-stroke"),opacity:r.attr("data-opacity")})})})}(c),this},h.config=function(t){if(!arguments.length)return l;var e=o.util.cloneJson(t);return e.data.forEach(function(t,e){l.data[e]||(l.data[e]={}),i(l.data[e],o.Axis.defaultConfig().data[0]),i(l.data[e],t)}),i(l.layout,o.Axis.defaultConfig().layout),i(l.layout,e.layout),this},h.getLiveConfig=function(){return u},h.getinputConfig=function(){return c},h.radialScale=function(t){return r},h.angularScale=function(t){return s},h.svg=function(){return t},n.rebind(h,f,"on"),h},o.Axis.defaultConfig=function(t,e){return{data:[{t:[1,2,3,4],r:[10,11,12,13],name:"Line1",geometry:"LinePlot",color:null,strokeDash:"solid",strokeColor:null,strokeSize:"1",visibleInLegend:!0,opacity:1}],layout:{defaultColorRange:n.scale.category10().range(),title:null,height:450,width:500,margin:{top:40,right:40,bottom:40,left:40},font:{size:12,color:"gray",outlineColor:"white",family:"Tahoma, sans-serif"},direction:"clockwise",orientation:0,labelOffset:10,radialAxis:{domain:null,orientation:-45,ticksSuffix:"",visible:!0,gridLinesVisible:!0,tickOrientation:"horizontal",rewriteTicks:null},angularAxis:{domain:[0,360],ticksSuffix:"",visible:!0,gridLinesVisible:!0,labelsVisible:!0,tickOrientation:"horizontal",rewriteTicks:null,ticksCount:null,ticksStep:null},minorTicks:0,tickLength:null,tickColor:"silver",minorTickColor:"#eee",backgroundColor:"none",needsEndSpacing:null,showLegend:!0,legend:{reverseOrder:!1},opacity:1}}},o.util={},o.DATAEXTENT="dataExtent",o.AREA="AreaChart",o.LINE="LinePlot",o.DOT="DotPlot",o.BAR="BarChart",o.util._override=function(t,e){for(var r in t)r in e&&(e[r]=t[r])},o.util._extend=function(t,e){for(var r in t)e[r]=t[r]},o.util._rndSnd=function(){return 2*Math.random()-1+(2*Math.random()-1)+(2*Math.random()-1)},o.util.dataFromEquation2=function(t,e){var r=e||6;return n.range(0,360+r,r).map(function(e,r){var n=e*Math.PI/180;return[e,t(n)]})},o.util.dataFromEquation=function(t,e,r){var i=e||6,a=[],o=[];n.range(0,360+i,i).forEach(function(e,r){var n=e*Math.PI/180,i=t(n);a.push(e),o.push(i)});var s={t:a,r:o};return r&&(s.name=r),s},o.util.ensureArray=function(t,e){if(void 0===t)return null;var r=[].concat(t);return n.range(e).map(function(t,e){return r[e]||r[0]})},o.util.fillArrays=function(t,e,r){return e.forEach(function(e,n){t[e]=o.util.ensureArray(t[e],r)}),t},o.util.cloneJson=function(t){return JSON.parse(JSON.stringify(t))},o.util.validateKeys=function(t,e){"string"==typeof e&&(e=e.split("."));var r=e.shift();return t[r]&&(!e.length||objHasKeys(t[r],e))},o.util.sumArrays=function(t,e){return n.zip(t,e).map(function(t,e){return n.sum(t)})},o.util.arrayLast=function(t){return t[t.length-1]},o.util.arrayEqual=function(t,e){for(var r=Math.max(t.length,e.length,1);r-- >=0&&t[r]===e[r];);return-2===r},o.util.flattenArray=function(t){for(var e=[];!o.util.arrayEqual(e,t);)e=t,t=[].concat.apply([],t);return t},o.util.deduplicate=function(t){return t.filter(function(t,e,r){return r.indexOf(t)==e})},o.util.convertToCartesian=function(t,e){var r=e*Math.PI/180;return[t*Math.cos(r),t*Math.sin(r)]},o.util.round=function(t,e){var r=e||2,n=Math.pow(10,r);return Math.round(t*n)/n},o.util.getMousePos=function(t){var e=n.mouse(t.node()),r=e[0],i=e[1],a={};return a.x=r,a.y=i,a.pos=e,a.angle=180*(Math.atan2(i,r)+Math.PI)/Math.PI,a.radius=Math.sqrt(r*r+i*i),a},o.util.duplicatesCount=function(t){for(var e,r={},n={},i=0,a=t.length;i0)){var l=n.select(this.parentNode).selectAll("path.line").data([0]);l.enter().insert("path"),l.attr({class:"line",d:u(s),transform:function(t,r){return"rotate("+(e.orientation+90)+")"},"pointer-events":"none"}).style({fill:function(t,e){return d.fill(r,i,a)},"fill-opacity":0,stroke:function(t,e){return d.stroke(r,i,a)},"stroke-width":function(t,e){return d["stroke-width"](r,i,a)},"stroke-dasharray":function(t,e){return d["stroke-dasharray"](r,i,a)},opacity:function(t,e){return d.opacity(r,i,a)},display:function(t,e){return d.display(r,i,a)}})}};var f=e.angularScale.range(),h=Math.abs(f[1]-f[0])/o[0].length*Math.PI/180,p=n.svg.arc().startAngle(function(t){return-h/2}).endAngle(function(t){return h/2}).innerRadius(function(t){return e.radialScale(l+(t[2]||0))}).outerRadius(function(t){return e.radialScale(l+(t[2]||0))+e.radialScale(t[1])});c.arc=function(t,r,i){n.select(this).attr({class:"mark arc",d:p,transform:function(t,r){return"rotate("+(e.orientation+s(t[0])+90)+")"}})};var d={fill:function(e,r,n){return t[n].data.color},stroke:function(e,r,n){return t[n].data.strokeColor},"stroke-width":function(e,r,n){return t[n].data.strokeSize+"px"},"stroke-dasharray":function(e,n,i){return r[t[i].data.strokeDash]},opacity:function(e,r,n){return t[n].data.opacity},display:function(e,r,n){return void 0===t[n].data.visible||t[n].data.visible?"block":"none"}},g=n.select(this).selectAll("g.layer").data(o);g.enter().append("g").attr({class:"layer"});var m=g.selectAll("path.mark").data(function(t,e){return t});m.enter().append("path").attr({class:"mark"}),m.style(d).each(c[e.geometryType]),m.exit().remove(),g.exit().remove()})}return a.config=function(e){return arguments.length?(e.forEach(function(e,r){t[r]||(t[r]={}),i(t[r],o.PolyChart.defaultConfig()),i(t[r],e)}),this):t},a.getColorScale=function(){},n.rebind(a,e,"on"),a},o.PolyChart.defaultConfig=function(){return{data:{name:"geom1",t:[[1,2,3,4]],r:[[1,2,3,4]],dotType:"circle",dotSize:64,dotVisible:!1,barWidth:20,color:"#ffa500",strokeSize:1,strokeColor:"silver",strokeDash:"solid",opacity:1,index:0,visible:!0,visibleInLegend:!0},geometryConfig:{geometry:"LinePlot",geometryType:"arc",direction:"clockwise",orientation:0,container:"body",radialScale:null,angularScale:null,colorScale:n.scale.category20()}}},o.BarChart=function(){return o.PolyChart()},o.BarChart.defaultConfig=function(){return{geometryConfig:{geometryType:"bar"}}},o.AreaChart=function(){return o.PolyChart()},o.AreaChart.defaultConfig=function(){return{geometryConfig:{geometryType:"arc"}}},o.DotPlot=function(){return o.PolyChart()},o.DotPlot.defaultConfig=function(){return{geometryConfig:{geometryType:"dot",dotType:"circle"}}},o.LinePlot=function(){return o.PolyChart()},o.LinePlot.defaultConfig=function(){return{geometryConfig:{geometryType:"line"}}},o.Legend=function(){var t=o.Legend.defaultConfig(),e=n.dispatch("hover");function r(){var e=t.legendConfig,a=t.data.map(function(t,r){return[].concat(t).map(function(t,n){var a=i({},e.elements[r]);return a.name=t,a.color=[].concat(e.elements[r].color)[n],a})}),o=n.merge(a);o=o.filter(function(t,r){return e.elements[r]&&(e.elements[r].visibleInLegend||void 0===e.elements[r].visibleInLegend)}),e.reverseOrder&&(o=o.reverse());var s=e.container;("string"==typeof s||s.nodeName)&&(s=n.select(s));var l=o.map(function(t,e){return t.color}),c=e.fontSize,u=null==e.isContinuous?"number"==typeof o[0]:e.isContinuous,f=u?e.height:c*o.length,h=s.classed("legend-group",!0).selectAll("svg").data([0]),p=h.enter().append("svg").attr({width:300,height:f+c,xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",version:"1.1"});p.append("g").classed("legend-axis",!0),p.append("g").classed("legend-marks",!0);var d=n.range(o.length),g=n.scale[u?"linear":"ordinal"]().domain(d).range(l),m=n.scale[u?"linear":"ordinal"]().domain(d)[u?"range":"rangePoints"]([0,f]);if(u){var v=h.select(".legend-marks").append("defs").append("linearGradient").attr({id:"grad1",x1:"0%",y1:"0%",x2:"0%",y2:"100%"}).selectAll("stop").data(l);v.enter().append("stop"),v.attr({offset:function(t,e){return e/(l.length-1)*100+"%"}}).style({"stop-color":function(t,e){return t}}),h.append("rect").classed("legend-mark",!0).attr({height:e.height,width:e.colorBandWidth,fill:"url(#grad1)"})}else{var y=h.select(".legend-marks").selectAll("path.legend-mark").data(o);y.enter().append("path").classed("legend-mark",!0),y.attr({transform:function(t,e){return"translate("+[c/2,m(e)+c/2]+")"},d:function(t,e){var r,i,a,o=t.symbol;return a=3*(i=c),"line"===(r=o)?"M"+[[-i/2,-i/12],[i/2,-i/12],[i/2,i/12],[-i/2,i/12]]+"Z":-1!=n.svg.symbolTypes.indexOf(r)?n.svg.symbol().type(r).size(a)():n.svg.symbol().type("square").size(a)()},fill:function(t,e){return g(e)}}),y.exit().remove()}var x=n.svg.axis().scale(m).orient("right"),b=h.select("g.legend-axis").attr({transform:"translate("+[u?e.colorBandWidth:c,c/2]+")"}).call(x);return b.selectAll(".domain").style({fill:"none",stroke:"none"}),b.selectAll("line").style({fill:"none",stroke:u?e.textColor:"none"}),b.selectAll("text").style({fill:e.textColor,"font-size":e.fontSize}).text(function(t,e){return o[e].name}),r}return r.config=function(e){return arguments.length?(i(t,e),this):t},n.rebind(r,e,"on"),r},o.Legend.defaultConfig=function(t,e){return{data:["a","b","c"],legendConfig:{elements:[{symbol:"line",color:"red"},{symbol:"square",color:"yellow"},{symbol:"diamond",color:"limegreen"}],height:150,colorBandWidth:30,fontSize:12,container:"body",isContinuous:null,textColor:"grey",reverseOrder:!1}}},o.tooltipPanel=function(){var t,e,r,a={container:null,hasTick:!1,fontSize:12,color:"white",padding:5},s="tooltip-"+o.tooltipPanel.uid++,l=10,c=function(){var n=(t=a.container.selectAll("g."+s).data([0])).enter().append("g").classed(s,!0).style({"pointer-events":"none",display:"none"});return r=n.append("path").style({fill:"white","fill-opacity":.9}).attr({d:"M0 0"}),e=n.append("text").attr({dx:a.padding+l,dy:.3*+a.fontSize}),c};return c.text=function(i){var o=n.hsl(a.color).l,s=o>=.5?"#aaa":"white",u=o>=.5?"black":"white",f=i||"";e.style({fill:u,"font-size":a.fontSize+"px"}).text(f);var h=a.padding,p=e.node().getBBox(),d={fill:a.color,stroke:s,"stroke-width":"2px"},g=p.width+2*h+l,m=p.height+2*h;return r.attr({d:"M"+[[l,-m/2],[l,-m/4],[a.hasTick?0:l,0],[l,m/4],[l,m/2],[g,m/2],[g,-m/2]].join("L")+"Z"}).style(d),t.attr({transform:"translate("+[l,-m/2+2*h]+")"}),t.style({display:"block"}),c},c.move=function(e){if(t)return t.attr({transform:"translate("+[e[0],e[1]]+")"}).style({display:"block"}),c},c.hide=function(){if(t)return t.style({display:"none"}),c},c.show=function(){if(t)return t.style({display:"block"}),c},c.config=function(t){return i(a,t),c},c},o.tooltipPanel.uid=1,o.adapter={},o.adapter.plotly=function(){var t={convert:function(t,e){var r={};if(t.data&&(r.data=t.data.map(function(t,r){var n=i({},t);return[[n,["marker","color"],["color"]],[n,["marker","opacity"],["opacity"]],[n,["marker","line","color"],["strokeColor"]],[n,["marker","line","dash"],["strokeDash"]],[n,["marker","line","width"],["strokeSize"]],[n,["marker","symbol"],["dotType"]],[n,["marker","size"],["dotSize"]],[n,["marker","barWidth"],["barWidth"]],[n,["line","interpolation"],["lineInterpolation"]],[n,["showlegend"],["visibleInLegend"]]].forEach(function(t,r){o.util.translator.apply(null,t.concat(e))}),e||delete n.marker,e&&delete n.groupId,e?("LinePlot"===n.geometry?(n.type="scatter",!0===n.dotVisible?(delete n.dotVisible,n.mode="lines+markers"):n.mode="lines"):"DotPlot"===n.geometry?(n.type="scatter",n.mode="markers"):"AreaChart"===n.geometry?n.type="area":"BarChart"===n.geometry&&(n.type="bar"),delete n.geometry):("scatter"===n.type?"lines"===n.mode?n.geometry="LinePlot":"markers"===n.mode?n.geometry="DotPlot":"lines+markers"===n.mode&&(n.geometry="LinePlot",n.dotVisible=!0):"area"===n.type?n.geometry="AreaChart":"bar"===n.type&&(n.geometry="BarChart"),delete n.mode,delete n.type),n}),!e&&t.layout&&"stack"===t.layout.barmode)){var a=o.util.duplicates(r.data.map(function(t,e){return t.geometry}));r.data.forEach(function(t,e){var n=a.indexOf(t.geometry);-1!=n&&(r.data[e].groupId=n)})}if(t.layout){var s=i({},t.layout);if([[s,["plot_bgcolor"],["backgroundColor"]],[s,["showlegend"],["showLegend"]],[s,["radialaxis"],["radialAxis"]],[s,["angularaxis"],["angularAxis"]],[s.angularaxis,["showline"],["gridLinesVisible"]],[s.angularaxis,["showticklabels"],["labelsVisible"]],[s.angularaxis,["nticks"],["ticksCount"]],[s.angularaxis,["tickorientation"],["tickOrientation"]],[s.angularaxis,["ticksuffix"],["ticksSuffix"]],[s.angularaxis,["range"],["domain"]],[s.angularaxis,["endpadding"],["endPadding"]],[s.radialaxis,["showline"],["gridLinesVisible"]],[s.radialaxis,["tickorientation"],["tickOrientation"]],[s.radialaxis,["ticksuffix"],["ticksSuffix"]],[s.radialaxis,["range"],["domain"]],[s.angularAxis,["showline"],["gridLinesVisible"]],[s.angularAxis,["showticklabels"],["labelsVisible"]],[s.angularAxis,["nticks"],["ticksCount"]],[s.angularAxis,["tickorientation"],["tickOrientation"]],[s.angularAxis,["ticksuffix"],["ticksSuffix"]],[s.angularAxis,["range"],["domain"]],[s.angularAxis,["endpadding"],["endPadding"]],[s.radialAxis,["showline"],["gridLinesVisible"]],[s.radialAxis,["tickorientation"],["tickOrientation"]],[s.radialAxis,["ticksuffix"],["ticksSuffix"]],[s.radialAxis,["range"],["domain"]],[s.font,["outlinecolor"],["outlineColor"]],[s.legend,["traceorder"],["reverseOrder"]],[s,["labeloffset"],["labelOffset"]],[s,["defaultcolorrange"],["defaultColorRange"]]].forEach(function(t,r){o.util.translator.apply(null,t.concat(e))}),e?(void 0!==s.tickLength&&(s.angularaxis.ticklen=s.tickLength,delete s.tickLength),s.tickColor&&(s.angularaxis.tickcolor=s.tickColor,delete s.tickColor)):(s.angularAxis&&void 0!==s.angularAxis.ticklen&&(s.tickLength=s.angularAxis.ticklen),s.angularAxis&&void 0!==s.angularAxis.tickcolor&&(s.tickColor=s.angularAxis.tickcolor)),s.legend&&"boolean"!=typeof s.legend.reverseOrder&&(s.legend.reverseOrder="normal"!=s.legend.reverseOrder),s.legend&&"boolean"==typeof s.legend.traceorder&&(s.legend.traceorder=s.legend.traceorder?"reversed":"normal",delete s.legend.reverseOrder),s.margin&&void 0!==s.margin.t){var l=["t","r","b","l","pad"],c=["top","right","bottom","left","pad"],u={};n.entries(s.margin).forEach(function(t,e){u[c[l.indexOf(t.key)]]=t.value}),s.margin=u}e&&(delete s.needsEndSpacing,delete s.minorTickColor,delete s.minorTicks,delete s.angularaxis.ticksCount,delete s.angularaxis.ticksCount,delete s.angularaxis.ticksStep,delete s.angularaxis.rewriteTicks,delete s.angularaxis.nticks,delete s.radialaxis.ticksCount,delete s.radialaxis.ticksCount,delete s.radialaxis.ticksStep,delete s.radialaxis.rewriteTicks,delete s.radialaxis.nticks),r.layout=s}return r}};return t}},{"../../../constants/alignment":632,"../../../lib":660,d3:131}],778:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../../lib"),a=t("../../../components/color"),o=t("./micropolar"),s=t("./undo_manager"),l=i.extendDeepAll,c=e.exports={};c.framework=function(t){var e,r,i,a,u,f=new s;function h(r,s){return s&&(u=s),n.select(n.select(u).node().parentNode).selectAll(".svg-container>*:not(.chart-root)").remove(),e=e?l(e,r):r,i||(i=o.Axis()),a=o.adapter.plotly().convert(e),i.config(a).render(u),t.data=e.data,t.layout=e.layout,c.fillLayout(t),e}return h.isPolar=!0,h.svg=function(){return i.svg()},h.getConfig=function(){return e},h.getLiveConfig=function(){return o.adapter.plotly().convert(i.getLiveConfig(),!0)},h.getLiveScales=function(){return{t:i.angularScale(),r:i.radialScale()}},h.setUndoPoint=function(){var t,n,i=this,a=o.util.cloneJson(e);t=a,n=r,f.add({undo:function(){n&&i(n)},redo:function(){i(t)}}),r=o.util.cloneJson(a)},h.undo=function(){f.undo()},h.redo=function(){f.redo()},h},c.fillLayout=function(t){var e=n.select(t).selectAll(".plot-container"),r=e.selectAll(".svg-container"),i=t.framework&&t.framework.svg&&t.framework.svg(),o={width:800,height:600,paper_bgcolor:a.background,_container:e,_paperdiv:r,_paper:i};t._fullLayout=l(o,t.layout)}},{"../../../components/color":532,"../../../lib":660,"./micropolar":777,"./undo_manager":779,d3:131}],779:[function(t,e,r){"use strict";e.exports=function(){var t,e=[],r=-1,n=!1;function i(t,e){return t?(n=!0,t[e](),n=!1,this):this}return{add:function(t){return n?this:(e.splice(r+1,e.length-r),e.push(t),r=e.length-1,this)},setCallback:function(e){t=e},undo:function(){var n=e[r];return n?(i(n,"undo"),r-=1,t&&t(n.undo),this):this},redo:function(){var n=e[r+1];return n?(i(n,"redo"),r+=1,t&&t(n.redo),this):this},clear:function(){e=[],r=-1},hasUndo:function(){return-1!==r},hasRedo:function(){return r0?1:-1}function F(t){return B(Math.cos(t))}function N(t){return B(Math.sin(t))}e.exports=function(t,e){return new S(t,e)},C.plot=function(t,e){var r=e[this.id];this._hasClipOnAxisFalse=!1;for(var n=0;n=90||s>90&&l>=450?1:u<=0&&h<=0?0:Math.max(u,h);e=s<=180&&l>=180||s>180&&l>=540?-1:c>=0&&f>=0?0:Math.min(c,f);r=s<=270&&l>=270||s>270&&l>=630?-1:u>=0&&h>=0?0:Math.min(u,h);n=l>=360?1:c<=0&&f<=0?0:Math.max(c,f);return[e,r,n,i]}(v),x=y[2]-y[0],b=y[3]-y[1],w=m/g,M=Math.abs(b/x);w>M?(c=g,d=(m-(f=g*M))/i.h/2,h=[a[0],a[1]],p=[o[0]+d,o[1]-d]):(f=m,d=(g-(c=m/M))/i.w/2,h=[a[0]+d,a[1]-d],p=[o[0],o[1]]),r.xLength2=c,r.yLength2=f,r.xDomain2=h,r.yDomain2=p;var A=r.xOffset2=i.l+i.w*h[0],T=r.yOffset2=i.t+i.h*(1-p[1]),S=r.radius=c/x,C=r.cx=A-S*y[0],E=r.cy=T+S*y[3],L=r.cxx=C-A,z=r.cyy=E-T;r.updateRadialAxis(t,e),r.updateRadialAxisTitle(t,e),r.updateAngularAxis(t,e);var D=r.radialAxis.range,O=D[1]-D[0],R=r.xaxis={type:"linear",_id:"x",range:[y[0]*O,y[2]*O],domain:h};u.setConvert(R,t),R.setScale();var B=r.yaxis={type:"linear",_id:"y",range:[y[1]*O,y[3]*O],domain:p};u.setConvert(B,t),B.setScale(),R.isPtWithinRange=function(t){return r.isPtWithinSector(t)},B.isPtWithinRange=function(){return!0},n.frontplot.attr("transform",I(A,T)).call(l.setClipUrl,r._hasClipOnAxisFalse?null:r.clipIds.circle),n.bgcircle.attr({d:P(S,v),transform:I(C,E)}).call(s.fill,e.bgcolor),r.clipPaths.circle.select("path").attr("d",P(S,v)).attr("transform",I(L,z)),r.framework.selectAll(".crisp").classed("crisp",0)},C.updateRadialAxis=function(t,e){var r=this.gd,n=this.layers,i=this.radius,a=this.cx,l=this.cy,c=t._size,h=e.radialaxis,p=e.sector,d=k(p[0]);this.fillViewInitialKey("radialaxis.angle",h.angle);var g=this.radialAxis=o.extendFlat({},h,{_axislayer:n["radial-axis"],_gridlayer:n["radial-grid"],_id:"x",_pos:0,side:{counterclockwise:"top",clockwise:"bottom"}[h.side],domain:[0,i/c.w],anchor:"free",position:0,_counteraxis:!0,automargin:!1});E(g,h,t),f(g),h.range=g.range.slice(),h._input.range=g.range.slice(),this.fillViewInitialKey("radialaxis.range",g.range.slice()),"auto"===g.tickangle&&d>90&&d<=270&&(g.tickangle=180),g._transfn=function(t){return"translate("+g.l2p(t.x)+",0)"},g._gridpath=function(t){return z(g.r2p(t.x),p)};var m=L(h);this.radialTickLayout!==m&&(n["radial-axis"].selectAll(".xtick").remove(),this.radialTickLayout=m),u.doTicksSingle(r,g,!0),O(n["radial-axis"],h.showticklabels||h.ticks,{transform:I(a,l)+R(-h.angle)}),O(n["radial-grid"],h.showgrid,{transform:I(a,l)}).selectAll("path").attr("transform",null),O(n["radial-line"].select("line"),h.showline,{x1:0,y1:0,x2:i,y2:0,transform:I(a,l)+R(-h.angle)}).attr("stroke-width",h.linewidth).call(s.stroke,h.linecolor)},C.updateRadialAxisTitle=function(t,e,r){var n=this.gd,i=this.radius,a=this.cx,o=this.cy,s=e.radialaxis,c=this.id+"title",u=void 0!==r?r:s.angle,f=_(u),h=Math.cos(f),p=Math.sin(f),d=0;if(s.title){var m=l.bBox(this.layers["radial-axis"].node()).height,v=s.titlefont.size;d="counterclockwise"===s.side?-m-.4*v:m+.8*v}this.layers["radial-axis-title"]=g.draw(n,c,{propContainer:s,propName:this.id+".radialaxis.title",placeholder:b(n,"Click to enter radial axis title"),attributes:{x:a+i/2*h+d*p,y:o-i/2*p+d*h,"text-anchor":"middle"},transform:{rotate:-u}})},C.updateAngularAxis=function(t,e){var r=this,i=r.gd,a=r.layers,l=r.radius,c=r.cx,f=r.cy,h=e.angularaxis,p=e.sector,d=p.map(_);r.fillViewInitialKey("angularaxis.rotation",h.rotation);var g=r.angularAxis=o.extendFlat({},h,{_axislayer:a["angular-axis"],_gridlayer:a["angular-grid"],_id:"angular",_pos:0,side:"right",domain:[0,Math.PI],anchor:"free",position:0,_counteraxis:!0,automargin:!1,autorange:!1});if("linear"===g.type)D(p)?g.range=p.slice():g.range=d.map(g.unTransformRad).map(w),"radians"===g.thetaunit&&(g.tick0=w(g.tick0),g.dtick=w(g.dtick));else if("category"===g.type){var m=h.period?Math.max(h.period,h._categories.length):h._categories.length;g.range=[0,m],g._tickFilter=function(t){return r.isPtWithinSector({r:r.radialAxis.range[1],rad:g.c2rad(t.x)})}}function v(t){return g.c2rad(t.x,"degrees")}function y(t){return[l*Math.cos(t),l*Math.sin(t)]}E(g,h,t),g._transfn=function(t){var e=v(t),r=y(e),i=I(c+r[0],f-r[1]),a=n.select(this);return a&&a.node()&&a.classed("ticks")&&(i+=R(-w(e))),i},g._gridpath=function(t){var e=y(v(t));return"M0,0L"+-e[0]+","+e[1]};var b="outside"!==h.ticks?.7:.5;g._labelx=function(t){var e=v(t),r=g._labelStandoff,n=g._pad;return(0===N(e)?0:Math.cos(e)*(r+n+b*t.fontSize))+F(e)*(t.dx+r+n)},g._labely=function(t){var e=v(t),r=g._labelStandoff,n=g._labelShift,i=g._pad;return t.dy+t.fontSize*x-n+-Math.sin(e)*(r+i+b*t.fontSize)},g._labelanchor=function(t,e){var r=v(e);return 0===N(r)?F(r)>0?"start":"end":"middle"};var k=L(h);r.angularTickLayout!==k&&(a["angular-axis"].selectAll(".angulartick").remove(),r.angularTickLayout=k),u.doTicksSingle(i,g,!0),O(a["angular-line"].select("path"),h.showline,{d:P(l,p),transform:I(c,f)}).attr("stroke-width",h.linewidth).call(s.stroke,h.linecolor)},C.updateFx=function(t,e){this.gd._context.staticPlot||(this.updateAngularDrag(t,e),this.updateRadialDrag(t,e),this.updateMainDrag(t,e))},C.updateMainDrag=function(t,e){var r=this,o=r.gd,s=r.layers,l=t._zoomlayer,c=T.MINZOOM,u=T.OFFEDGE,f=r.radius,g=r.cx,y=r.cy,x=r.cxx,b=r.cyy,_=e.sector,w=p.makeDragger(s,"path","maindrag","crosshair");n.select(w).attr("d",P(f,_)).attr("transform",I(g,y));var k,M,A,S,C,E,L,z,D,O={element:w,gd:o,subplot:r.id,plotinfo:{xaxis:r.xaxis,yaxis:r.yaxis},xaxes:[r.xaxis],yaxes:[r.yaxis]};function R(t,e){var r=t-x,n=e-b;return Math.sqrt(r*r+n*n)}function B(t,e){return Math.atan2(b-e,t-x)}function F(t,e){return[t*Math.cos(e),t*Math.sin(-e)]}function N(t,e){var r=T.cornerLen,n=T.cornerHalfWidth;if(0===t)return P(2*n,_);var i=r/t/2,a=e-i,o=e+i,s=Math.max(0,Math.min(t,f)),l=s-n,c=s+n;return"M"+F(l,a)+"A"+[l,l]+" 0,0,0 "+F(l,o)+"L"+F(c,o)+"A"+[c,c]+" 0,0,1 "+F(c,a)+"Z"}function j(t,e){var r,n,i=k+t,a=M+e,o=R(k,M),s=Math.min(R(i,a),f),l=B(k,M),h=B(i,a);oc?(o0==h>y[0]){S=d.range[1]=h,u.doTicksSingle(i,r.radialAxis,!0),s["radial-grid"].attr("transform",I(c,f)).selectAll("path").attr("transform",null);var p=S-y[0],g=r.sectorBBox;for(var v in r.xaxis.range=[g[0]*p,g[2]*p],r.yaxis.range=[g[1]*p,g[3]*p],r.xaxis.setScale(),r.yaxis.setScale(),r.traceHash){var b=r.traceHash[v],_=o.filterVisible(b),w=b[0][0].trace._module,k=i._fullLayout[r.id];if(w.plot(i,r,_,k),!a.traceIs(v,"gl"))for(var M=0;M<_.length;M++)w.style(i,_[M])}}}},C.updateAngularDrag=function(t,e){var r=this,i=r.gd,s=r.layers,c=r.radius,f=r.cx,d=r.cy,g=r.cxx,m=r.cyy,x=e.sector,b=T.angularDragBoxSize,k=p.makeDragger(s,"path","angulardrag","move"),S={element:k,gd:i};function C(t,e){return Math.atan2(m+b-e,t-g-b)}n.select(k).attr("d",function(t,e,r){var n,i,a,o=Math.abs(r[1]-r[0])<=180?0:1;function s(t,e){return[t*Math.cos(e),-t*Math.sin(e)]}function l(t,e,r){return"A"+[t,t]+" "+[0,o,r]+" "+s(t,e)}return D(r)?(n=0,a=2*Math.PI,i=Math.PI,"M"+s(t,n)+l(t,i,0)+l(t,a,0)+"ZM"+s(e,n)+l(e,i,1)+l(e,a,1)+"Z"):(n=_(r[0]),a=_(r[1]),"M"+s(t,n)+"L"+s(e,n)+l(e,a,0)+"L"+s(t,a)+l(t,n,1)+"Z")}(c,c+b,x)).attr("transform",I(f,d)).call(y,"move");var E,L,z,P,O,B,F=s.frontplot.select(".scatterlayer").selectAll(".trace"),N=F.selectAll(".point"),j=F.selectAll(".textpoint");function V(t,e){var c=C(E+t,L+e),f=w(c-B);P=z+f,s.frontplot.attr("transform",I(r.xOffset2,r.yOffset2)+R([-f,g,m])),r.clipPaths.circle.select("path").attr("transform",I(g,m)+R(f)),N.each(function(){var t=n.select(this),e=l.getTranslate(t);t.attr("transform",I(e.x,e.y)+R([f]))}),j.each(function(){var t=n.select(this),e=t.select("text"),r=l.getTranslate(t);t.attr("transform",R([f,e.attr("x"),e.attr("y")])+I(r.x,r.y))});var h=r.angularAxis;for(var p in h.rotation=M(P),"linear"!==h.type||D(x)||(h.range=O.map(_).map(h.unTransformRad).map(w)),A(h),u.doTicksSingle(i,h,!0),r._hasClipOnAxisFalse&&!D(x)&&(r.sector=[O[0]-f,O[1]-f],F.call(l.hideOutsideRangePoints,r)),r.traceHash)if(a.traceIs(p,"gl")){var d=r.traceHash[p],v=o.filterVisible(d),y=d[0][0].trace._module,b=i._fullLayout[r.id];y.plot(i,r,v,b)}}function U(){j.select("text").attr("transform",null);var t={};t[r.id+".angularaxis.rotation"]=P,a.call("relayout",i,t)}S.prepFn=function(e,n,i){var a=t[r.id];O=a.sector.slice(),z=a.angularaxis.rotation;var o=k.getBoundingClientRect();E=n-o.left,L=i-o.top,B=C(E,L),S.moveFn=V,S.doneFn=U,v(t._zoomlayer)},h.init(S)},C.isPtWithinSector=function(t){var e=this.sector,r=this.radialAxis,n=r.range,i=r.c2r(t.r),a=k(e[0]),o=k(e[1]);a>o&&(o+=360);var s,l,c=k(w(t.rad)),u=c+360;return n[1]>=n[0]?(s=n[0],l=n[1]):(s=n[1],l=n[0]),i>=s&&i<=l&&(D(e)||c>=a&&c<=o||u>=a&&u<=o)},C.fillViewInitialKey=function(t,e){t in this.viewInitial||(this.viewInitial[t]=e)}},{"../../components/color":532,"../../components/dragelement":554,"../../components/drawing":557,"../../components/fx":574,"../../components/titles":625,"../../constants/alignment":632,"../../lib":660,"../../lib/setcursor":680,"../../registry":790,"../cartesian/autorange":705,"../cartesian/axes":706,"../cartesian/dragbox":714,"../cartesian/select":723,"../plots":768,"./constants":769,"./helpers":770,d3:131,tinycolor2:473}],781:[function(t,e,r){"use strict";function n(t,e){return"splom"===t?-1:"splom"===e?1:0}e.exports={sortBasePlotModules:function(t,e){return n(t.name,e.name)},sortModules:n}},{}],782:[function(t,e,r){"use strict";var n=t("../lib"),i=t("./domain").defaults;e.exports=function(t,e,r,a){var o,s,l=a.type,c=a.attributes,u=a.handleDefaults,f=a.partition||"x",h=e._subplots[l],p=h.length;function d(t,e){return n.coerce(o,s,c,t,e)}for(var g=0;g=f&&(p.min=0,d.min=0,g.min=0,t.aaxis&&delete t.aaxis.min,t.baxis&&delete t.baxis.min,t.caxis&&delete t.caxis.min)}e.exports=function(t,e,r){i(t,e,r,{type:"ternary",attributes:a,handleDefaults:l,font:e.font,paper_bgcolor:e.paper_bgcolor})}},{"../../../components/color":532,"../../subplot_defaults":782,"./axis_defaults":786,"./layout_attributes":788}],788:[function(t,e,r){"use strict";var n=t("../../../components/color/attributes"),i=t("../../domain").attributes,a=t("./axis_attributes"),o=t("../../../plot_api/edit_types").overrideAll;e.exports=o({domain:i({name:"ternary"}),bgcolor:{valType:"color",dflt:n.background},sum:{valType:"number",dflt:1,min:0},aaxis:a,baxis:a,caxis:a},"plot","from-root")},{"../../../components/color/attributes":531,"../../../plot_api/edit_types":691,"../../domain":731,"./axis_attributes":785}],789:[function(t,e,r){"use strict";var n=t("d3"),i=t("tinycolor2"),a=t("../../registry"),o=t("../../lib"),s=o._,l=t("../../components/color"),c=t("../../components/drawing"),u=t("../cartesian/set_convert"),f=t("../../lib/extend").extendFlat,h=t("../plots"),p=t("../cartesian/axes"),d=t("../../components/dragelement"),g=t("../../components/fx"),m=t("../../components/titles"),v=t("../cartesian/select").prepSelect,y=t("../cartesian/select").clearSelect,x=t("../cartesian/constants");function b(t,e){this.id=t.id,this.graphDiv=t.graphDiv,this.init(e),this.makeFramework(e)}e.exports=b;var _=b.prototype;_.init=function(t){this.container=t._ternarylayer,this.defs=t._defs,this.layoutId=t._uid,this.traceHash={},this.layers={}},_.plot=function(t,e){var r=e[this.id],n=e._size;this._hasClipOnAxisFalse=!1;for(var i=0;iw*x?i=(a=x)*w:a=(i=y)/w,o=m*i/y,s=v*a/x,r=e.l+e.w*d-i/2,n=e.t+e.h*(1-g)-a/2,h.x0=r,h.y0=n,h.w=i,h.h=a,h.sum=b,h.xaxis={type:"linear",range:[_+2*M-b,b-_-2*k],domain:[d-o/2,d+o/2],_id:"x"},u(h.xaxis,h.graphDiv._fullLayout),h.xaxis.setScale(),h.xaxis.isPtWithinRange=function(t){return t.a>=h.aaxis.range[0]&&t.a<=h.aaxis.range[1]&&t.b>=h.baxis.range[1]&&t.b<=h.baxis.range[0]&&t.c>=h.caxis.range[1]&&t.c<=h.caxis.range[0]},h.yaxis={type:"linear",range:[_,b-k-M],domain:[g-s/2,g+s/2],_id:"y"},u(h.yaxis,h.graphDiv._fullLayout),h.yaxis.setScale(),h.yaxis.isPtWithinRange=function(){return!0};var A=h.yaxis.domain[0],T=h.aaxis=f({},t.aaxis,{visible:!0,range:[_,b-k-M],side:"left",_counterangle:30,tickangle:(+t.aaxis.tickangle||0)-30,domain:[A,A+s*w],_axislayer:h.layers.aaxis,_gridlayer:h.layers.agrid,_pos:0,_id:"y",_length:i,_gridpath:"M0,0l"+a+",-"+i/2,automargin:!1});u(T,h.graphDiv._fullLayout),T.setScale();var S=h.baxis=f({},t.baxis,{visible:!0,range:[b-_-M,k],side:"bottom",_counterangle:30,domain:h.xaxis.domain,_axislayer:h.layers.baxis,_gridlayer:h.layers.bgrid,_counteraxis:h.aaxis,_pos:0,_id:"x",_length:i,_gridpath:"M0,0l-"+i/2+",-"+a,automargin:!1});u(S,h.graphDiv._fullLayout),S.setScale(),T._counteraxis=S;var C=h.caxis=f({},t.caxis,{visible:!0,range:[b-_-k,M],side:"right",_counterangle:30,tickangle:(+t.caxis.tickangle||0)+30,domain:[A,A+s*w],_axislayer:h.layers.caxis,_gridlayer:h.layers.cgrid,_counteraxis:h.baxis,_pos:0,_id:"y",_length:i,_gridpath:"M0,0l-"+a+","+i/2,automargin:!1});u(C,h.graphDiv._fullLayout),C.setScale();var E="M"+r+","+(n+a)+"h"+i+"l-"+i/2+",-"+a+"Z";h.clipDef.select("path").attr("d",E),h.layers.plotbg.select("path").attr("d",E);var L="M0,"+a+"h"+i+"l-"+i/2+",-"+a+"Z";h.clipDefRelative.select("path").attr("d",L);var z="translate("+r+","+n+")";h.plotContainer.selectAll(".scatterlayer,.maplayer").attr("transform",z),h.clipDefRelative.select("path").attr("transform",null);var P="translate("+(r-S._offset)+","+(n+a)+")";h.layers.baxis.attr("transform",P),h.layers.bgrid.attr("transform",P);var D="translate("+(r+i/2)+","+n+")rotate(30)translate(0,"+-T._offset+")";h.layers.aaxis.attr("transform",D),h.layers.agrid.attr("transform",D);var O="translate("+(r+i/2)+","+n+")rotate(-30)translate(0,"+-C._offset+")";h.layers.caxis.attr("transform",O),h.layers.cgrid.attr("transform",O),h.drawAxes(!0),h.plotContainer.selectAll(".crisp").classed("crisp",!1),h.layers.aline.select("path").attr("d",T.showline?"M"+r+","+(n+a)+"l"+i/2+",-"+a:"M0,0").call(l.stroke,T.linecolor||"#000").style("stroke-width",(T.linewidth||0)+"px"),h.layers.bline.select("path").attr("d",S.showline?"M"+r+","+(n+a)+"h"+i:"M0,0").call(l.stroke,S.linecolor||"#000").style("stroke-width",(S.linewidth||0)+"px"),h.layers.cline.select("path").attr("d",C.showline?"M"+(r+i/2)+","+n+"l"+i/2+","+a:"M0,0").call(l.stroke,C.linecolor||"#000").style("stroke-width",(C.linewidth||0)+"px"),h.graphDiv._context.staticPlot||h.initInteractions(),c.setClipUrl(h.layers.frontplot,h._hasClipOnAxisFalse?null:h.clipId)},_.drawAxes=function(t){var e=this.graphDiv,r=this.id.substr(7)+"title",n=this.aaxis,i=this.baxis,a=this.caxis;if(p.doTicksSingle(e,n,!0),p.doTicksSingle(e,i,!0),p.doTicksSingle(e,a,!0),t){var o=Math.max(n.showticklabels?n.tickfont.size/2:0,(a.showticklabels?.75*a.tickfont.size:0)+("outside"===a.ticks?.87*a.ticklen:0));this.layers["a-title"]=m.draw(e,"a"+r,{propContainer:n,propName:this.id+".aaxis.title",placeholder:s(e,"Click to enter Component A title"),attributes:{x:this.x0+this.w/2,y:this.y0-n.titlefont.size/3-o,"text-anchor":"middle"}});var l=(i.showticklabels?i.tickfont.size:0)+("outside"===i.ticks?i.ticklen:0)+3;this.layers["b-title"]=m.draw(e,"b"+r,{propContainer:i,propName:this.id+".baxis.title",placeholder:s(e,"Click to enter Component B title"),attributes:{x:this.x0-l,y:this.y0+this.h+.83*i.titlefont.size+l,"text-anchor":"middle"}}),this.layers["c-title"]=m.draw(e,"c"+r,{propContainer:a,propName:this.id+".caxis.title",placeholder:s(e,"Click to enter Component C title"),attributes:{x:this.x0+this.w+l,y:this.y0+this.h+.83*a.titlefont.size+l,"text-anchor":"middle"}})}};var k=x.MINZOOM/2+.87,M="m-0.87,.5h"+k+"v3h-"+(k+5.2)+"l"+(k/2+2.6)+",-"+(.87*k+4.5)+"l2.6,1.5l-"+k/2+","+.87*k+"Z",A="m0.87,.5h-"+k+"v3h"+(k+5.2)+"l-"+(k/2+2.6)+",-"+(.87*k+4.5)+"l-2.6,1.5l"+k/2+","+.87*k+"Z",T="m0,1l"+k/2+","+.87*k+"l2.6,-1.5l-"+(k/2+2.6)+",-"+(.87*k+4.5)+"l-"+(k/2+2.6)+","+(.87*k+4.5)+"l2.6,1.5l"+k/2+",-"+.87*k+"Z",S="m0.5,0.5h5v-2h-5v-5h-2v5h-5v2h5v5h2Z",C=!0;function E(t){n.select(t).selectAll(".zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners").remove()}_.initInteractions=function(){var t,e,r,n,u,f,h,p,m,b,_=this,k=_.layers.plotbg.select("path").node(),L=_.graphDiv,z=L._fullLayout._zoomlayer,P={element:k,gd:L,plotinfo:{xaxis:_.xaxis,yaxis:_.yaxis},subplot:_.id,prepFn:function(a,o,s){P.xaxes=[_.xaxis],P.yaxes=[_.yaxis];var c=L._fullLayout.dragmode;a.shiftKey&&(c="pan"===c?"zoom":"pan"),P.minDrag="lasso"===c?1:void 0,"zoom"===c?(P.moveFn=R,P.doneFn=B,function(a,o,s){var c=k.getBoundingClientRect();t=o-c.left,e=s-c.top,r={a:_.aaxis.range[0],b:_.baxis.range[1],c:_.caxis.range[1]},u=r,n=_.aaxis.range[1]-r.a,f=i(_.graphDiv._fullLayout[_.id].bgcolor).getLuminance(),h="M0,"+_.h+"L"+_.w/2+", 0L"+_.w+","+_.h+"Z",p=!1,m=z.append("path").attr("class","zoombox").attr("transform","translate("+_.x0+", "+_.y0+")").style({fill:f>.2?"rgba(0,0,0,0)":"rgba(255,255,255,0)","stroke-width":0}).attr("d",h),b=z.append("path").attr("class","zoombox-corners").attr("transform","translate("+_.x0+", "+_.y0+")").style({fill:l.background,stroke:l.defaultLine,"stroke-width":1,opacity:0}).attr("d","M0,0Z"),y(z)}(0,o,s)):"pan"===c?(P.moveFn=F,P.doneFn=N,r={a:_.aaxis.range[0],b:_.baxis.range[1],c:_.caxis.range[1]},u=r,y(z)):"select"!==c&&"lasso"!==c||v(a,o,s,P,c)},clickFn:function(t,e){if(E(L),2===t){var r={};r[_.id+".aaxis.min"]=0,r[_.id+".baxis.min"]=0,r[_.id+".caxis.min"]=0,L.emit("plotly_doubleclick",null),a.call("relayout",L,r)}g.click(L,e,_.id)}};function D(t,e){return 1-e/_.h}function O(t,e){return 1-(t+(_.h-e)/Math.sqrt(3))/_.w}function I(t,e){return(t-(_.h-e)/Math.sqrt(3))/_.w}function R(i,a){var o=t+i,s=e+a,l=Math.max(0,Math.min(1,D(0,e),D(0,s))),c=Math.max(0,Math.min(1,O(t,e),O(o,s))),d=Math.max(0,Math.min(1,I(t,e),I(o,s))),g=(l/2+d)*_.w,v=(1-l/2-c)*_.w,y=(g+v)/2,k=v-g,C=(1-l)*_.h,E=C-k/w;k.2?"rgba(0,0,0,0.4)":"rgba(255,255,255,0.3)").duration(200),b.transition().style("opacity",1).duration(200),p=!0)}function B(){if(E(L),u!==r){var t={};t[_.id+".aaxis.min"]=u.a,t[_.id+".baxis.min"]=u.b,t[_.id+".caxis.min"]=u.c,a.call("relayout",L,t),C&&L.data&&L._context.showTips&&(o.notifier(s(L,"Double-click to zoom back out"),"long"),C=!1)}}function F(t,e){var n=t/_.xaxis._m,i=e/_.yaxis._m,a=[(u={a:r.a-i,b:r.b+(n+i)/2,c:r.c-(n-i)/2}).a,u.b,u.c].sort(),o=a.indexOf(u.a),s=a.indexOf(u.b),l=a.indexOf(u.c);a[0]<0&&(a[1]+a[0]/2<0?(a[2]+=a[0]+a[1],a[0]=a[1]=0):(a[2]+=a[0]/2,a[1]+=a[0]/2,a[0]=0),u={a:a[o],b:a[s],c:a[l]},e=(r.a-u.a)*_.yaxis._m,t=(r.c-u.c-r.b+u.b)*_.xaxis._m);var f="translate("+(_.x0+t)+","+(_.y0+e)+")";_.plotContainer.selectAll(".scatterlayer,.maplayer").attr("transform",f);var h="translate("+-t+","+-e+")";_.clipDefRelative.select("path").attr("transform",h),_.aaxis.range=[u.a,_.sum-u.b-u.c],_.baxis.range=[_.sum-u.a-u.c,u.b],_.caxis.range=[_.sum-u.a-u.b,u.c],_.drawAxes(!1),_.plotContainer.selectAll(".crisp").classed("crisp",!1),_._hasClipOnAxisFalse&&_.plotContainer.select(".scatterlayer").selectAll(".trace").call(c.hideOutsideRangePoints,_)}function N(){var t={};t[_.id+".aaxis.min"]=u.a,t[_.id+".baxis.min"]=u.b,t[_.id+".caxis.min"]=u.c,a.call("relayout",L,t)}k.onmousemove=function(t){g.hover(L,t,_.id),L._fullLayout._lasthover=k,L._fullLayout._hoversubplot=_.id},k.onmouseout=function(t){L._dragging||d.unhover(L,t)},d.init(P)}},{"../../components/color":532,"../../components/dragelement":554,"../../components/drawing":557,"../../components/fx":574,"../../components/titles":625,"../../lib":660,"../../lib/extend":649,"../../registry":790,"../cartesian/axes":706,"../cartesian/constants":711,"../cartesian/select":723,"../cartesian/set_convert":724,"../plots":768,d3:131,tinycolor2:473}],790:[function(t,e,r){"use strict";var n=t("./lib/loggers"),i=t("./lib/noop"),a=t("./lib/push_unique"),o=t("./lib/is_plain_object"),s=t("./lib/extend"),l=t("./plots/attributes"),c=t("./plots/layout_attributes"),u=s.extendFlat,f=s.extendDeepAll;function h(t){var e=t.name,i=t.categories,a=t.meta;if(r.modules[e])n.log("Type "+e+" already registered");else{r.subplotsRegistry[t.basePlotModule.name]||function(t){var e=t.name;if(r.subplotsRegistry[e])return void n.log("Plot type "+e+" already registered.");for(var i in m(t),r.subplotsRegistry[e]=t,r.componentsRegistry)x(i,t.name)}(t.basePlotModule);for(var o={},s=0;s-1&&(u[h[r]].title="");for(r=0;r")?"":e.html(t).text()});return e.remove(),r}(_),_=(_=_.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g,"&")).replace(c,"'"),i.isIE()&&(_=(_=(_=_.replace(/"/gi,"'")).replace(/(\('#)([^']*)('\))/gi,'("#$2")')).replace(/(\\')/gi,'"')),_}},{"../components/color":532,"../components/drawing":557,"../constants/xmlns_namespaces":639,"../lib":660,d3:131}],799:[function(t,e,r){"use strict";var n=t("../../lib").mergeArray;e.exports=function(t,e){for(var r=0;ra;if(!o)return e}return void 0!==r?r:t.dflt}(t.size,s,n.size),color:function(t,e,r){return a(e).isValid()?e:void 0!==r?r:t.dflt}(t.color,l,n.color)}}function b(t,e){var r;return Array.isArray(t)?e.01?N:function(t,e){return Math.abs(t-e)>=2?N(t):t>e?Math.ceil(t):Math.floor(t)};A=F(A,C),C=F(C,A),E=F(E,L),L=F(L,E)}o.ensureSingle(z,"path").style("vector-effect","non-scaling-stroke").attr("d","M"+A+","+E+"V"+L+"H"+C+"V"+E+"Z").call(c.setClipUrl,e.layerClipId),function(t,e,r,n,i,a,l,u){var f;function w(e,r,n){var i=o.ensureSingle(e,"text").text(r).attr({class:"bartext bartext-"+f,transform:"","text-anchor":"middle","data-notex":1}).call(c.font,n).call(s.convertToTspans,t);return i}var k=r[0].trace,M=k.orientation,A=function(t,e){var r=b(t.text,e);return _(h,r)}(k,n);if(f=function(t,e){var r=b(t.textposition,e);return function(t,e,r){return t.coerceNumber&&(e=+e),-1!==t.values.indexOf(e)?e:void 0!==r?r:t.dflt}(p,r)}(k,n),!A||"none"===f)return void e.select("text").remove();var T,S,C,E,L,z,P=function(t,e,r){return x(d,t.textfont,e,r)}(k,n,t._fullLayout.font),D=function(t,e,r){return x(g,t.insidetextfont,e,r)}(k,n,P),O=function(t,e,r){return x(m,t.outsidetextfont,e,r)}(k,n,P),I=t._fullLayout.barmode,R="relative"===I,B="stack"===I||R,F=r[n],N=!B||F._outmost,j=Math.abs(a-i)-2*v,V=Math.abs(u-l)-2*v;"outside"===f&&(N||(f="inside"));if("auto"===f)if(N){f="inside",T=w(e,A,D),S=c.bBox(T.node()),C=S.width,E=S.height;var U=C>0&&E>0,q=C<=j&&E<=V,H=C<=V&&E<=j,G="h"===M?j>=C*(V/E):V>=E*(j/C);U&&(q||H||G)?f="inside":(f="outside",T.remove(),T=null)}else f="inside";if(!T&&(T=w(e,A,"outside"===f?O:D),S=c.bBox(T.node()),C=S.width,E=S.height,C<=0||E<=0))return void T.remove();"outside"===f?(z="both"===k.constraintext||"outside"===k.constraintext,L=function(t,e,r,n,i,a,o){var s,l="h"===a?Math.abs(n-r):Math.abs(e-t);l>2*v&&(s=v);var c=1;o&&(c="h"===a?Math.min(1,l/i.height):Math.min(1,l/i.width));var u,f,h,p,d=(i.left+i.right)/2,g=(i.top+i.bottom)/2;u=c*i.width,f=c*i.height,"h"===a?er?(h=(t+e)/2,p=n+s+f/2):(h=(t+e)/2,p=n-s-f/2);return y(d,g,h,p,c,!1)}(i,a,l,u,S,M,z)):(z="both"===k.constraintext||"inside"===k.constraintext,L=function(t,e,r,n,i,a,o){var s,l,c,u,f,h,p,d=i.width,g=i.height,m=(i.left+i.right)/2,x=(i.top+i.bottom)/2,b=Math.abs(e-t),_=Math.abs(n-r);b>2*v&&_>2*v?(b-=2*(f=v),_-=2*f):f=0;d<=b&&g<=_?(h=!1,p=1):d<=_&&g<=b?(h=!0,p=1):dr?(c=(t+e)/2,u=n-f-l/2):(c=(t+e)/2,u=n+f+l/2);return y(m,x,c,u,p,h)}(i,a,l,u,S,M,z));T.attr("transform",L)}(t,z,r,u,A,C,E,L),e.layerClipId&&c.hideOutsideRangePoint(r[u],z.select("text"),f,w,M.xcalendar,M.ycalendar)}else z.remove();function N(t){return 0===k.bargap&&0===k.bargroupgap?n.round(Math.round(t)-B,2):t}});var E=!1===r[0].trace.cliponaxis;c.setClipUrl(A,E?null:e.layerClipId)}),u.getComponentMethod("errorbars","plot")(M,e)}},{"../../components/color":532,"../../components/drawing":557,"../../lib":660,"../../lib/svg_text_utils":684,"../../registry":790,"./attributes":800,d3:131,"fast-isnumeric":197,tinycolor2:473}],808:[function(t,e,r){"use strict";e.exports=function(t,e){var r,n=t.cd,i=t.xaxis,a=t.yaxis,o=[];if(!1===e)for(r=0;rf+c||!n(u))&&(p=!0,g(h,t))}for(var m=0;m1||0===s.bargap&&0===s.bargroupgap&&!t[0].trace.marker.line.width)&&n.select(this).attr("shape-rendering","crispEdges")}),r.selectAll("g.points").each(function(e){o(n.select(this),e[0].trace,t)}),a.getComponentMethod("errorbars","style")(r)},styleOnSelect:function(t,e){var r=e[0].node3,n=e[0].trace;n.selectedpoints?(i.selectedPointStyle(r.selectAll("path"),n),i.selectedTextStyle(r.selectAll("text"),n)):o(r,n,t)}}},{"../../components/drawing":557,"../../registry":790,d3:131}],812:[function(t,e,r){"use strict";var n=t("../../components/color"),i=t("../../components/colorscale/has_colorscale"),a=t("../../components/colorscale/defaults");e.exports=function(t,e,r,o,s){r("marker.color",o),i(t,"marker")&&a(t,e,s,r,{prefix:"marker.",cLetter:"c"}),r("marker.line.color",n.defaultLine),i(t,"marker.line")&&a(t,e,s,r,{prefix:"marker.line.",cLetter:"c"}),r("marker.line.width"),r("marker.opacity"),r("selected.marker.color"),r("unselected.marker.color")}},{"../../components/color":532,"../../components/colorscale/defaults":542,"../../components/colorscale/has_colorscale":546}],813:[function(t,e,r){"use strict";var n=t("../scatter/attributes"),i=t("../../components/color/attributes"),a=t("../../lib/extend").extendFlat,o=n.marker,s=o.line;e.exports={y:{valType:"data_array",editType:"calc+clearAxisTypes"},x:{valType:"data_array",editType:"calc+clearAxisTypes"},x0:{valType:"any",editType:"calc+clearAxisTypes"},y0:{valType:"any",editType:"calc+clearAxisTypes"},name:{valType:"string",editType:"calc+clearAxisTypes"},text:a({},n.text,{}),whiskerwidth:{valType:"number",min:0,max:1,dflt:.5,editType:"calcIfAutorange"},notched:{valType:"boolean",editType:"calcIfAutorange"},notchwidth:{valType:"number",min:0,max:.5,dflt:.25,editType:"calcIfAutorange"},boxpoints:{valType:"enumerated",values:["all","outliers","suspectedoutliers",!1],dflt:"outliers",editType:"calcIfAutorange"},boxmean:{valType:"enumerated",values:[!0,"sd",!1],dflt:!1,editType:"calcIfAutorange"},jitter:{valType:"number",min:0,max:1,editType:"calcIfAutorange"},pointpos:{valType:"number",min:-2,max:2,editType:"calcIfAutorange"},orientation:{valType:"enumerated",values:["v","h"],editType:"calc+clearAxisTypes"},marker:{outliercolor:{valType:"color",dflt:"rgba(0, 0, 0, 0)",editType:"style"},symbol:a({},o.symbol,{arrayOk:!1,editType:"plot"}),opacity:a({},o.opacity,{arrayOk:!1,dflt:1,editType:"style"}),size:a({},o.size,{arrayOk:!1,editType:"calcIfAutorange"}),color:a({},o.color,{arrayOk:!1,editType:"style"}),line:{color:a({},s.color,{arrayOk:!1,dflt:i.defaultLine,editType:"style"}),width:a({},s.width,{arrayOk:!1,dflt:0,editType:"style"}),outliercolor:{valType:"color",editType:"style"},outlierwidth:{valType:"number",min:0,dflt:1,editType:"style"},editType:"style"},editType:"plot"},line:{color:{valType:"color",editType:"style"},width:{valType:"number",min:0,dflt:2,editType:"style"},editType:"plot"},fillcolor:n.fillcolor,selected:{marker:n.selected.marker,editType:"style"},unselected:{marker:n.unselected.marker,editType:"style"},hoveron:{valType:"flaglist",flags:["boxes","points"],dflt:"boxes+points",editType:"style"}}},{"../../components/color/attributes":531,"../../lib/extend":649,"../scatter/attributes":990}],814:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../../lib"),a=i._,o=t("../../plots/cartesian/axes");function s(t,e,r){var n={text:"tx"};for(var i in n)Array.isArray(e[i])&&(t[n[i]]=e[i][r])}function l(t,e){return t.v-e.v}function c(t){return t.v}e.exports=function(t,e){var r,u,f,h,p,d=t._fullLayout,g=o.getFromId(t,e.xaxis||"x"),m=o.getFromId(t,e.yaxis||"y"),v=[],y="violin"===e.type?"_numViolins":"_numBoxes";"h"===e.orientation?(u=g,f="x",h=m,p="y"):(u=m,f="y",h=g,p="x");var x=u.makeCalcdata(e,f),b=function(t,e,r,a,o){if(e in t)return r.makeCalcdata(t,e);var s;s=e+"0"in t?t[e+"0"]:"name"in t&&("category"===r.type||n(t.name)&&-1!==["linear","log"].indexOf(r.type)||i.isDateTime(t.name)&&"date"===r.type)?t.name:o;var l=r.d2c(s,0,t[e+"calendar"]);return a.map(function(){return l})}(e,p,h,x,d[y]),_=i.distinctVals(b),w=_.vals,k=_.minDiff/2,M=function(t,e){for(var r=t.length,n=new Array(r+1),i=0;i=0&&C0){var L=T[r].sort(l),z=L.map(c),P=z.length,D={pos:w[r],pts:L};D.min=z[0],D.max=z[P-1],D.mean=i.mean(z,P),D.sd=i.stdev(z,P,D.mean),D.q1=i.interp(z,.25),D.med=i.interp(z,.5),D.q3=i.interp(z,.75),D.lf=Math.min(D.q1,z[Math.min(i.findBin(2.5*D.q1-1.5*D.q3,z,!0)+1,P-1)]),D.uf=Math.max(D.q3,z[Math.max(i.findBin(2.5*D.q3-1.5*D.q1,z),0)]),D.lo=4*D.q1-3*D.q3,D.uo=4*D.q3-3*D.q1;var O=1.57*(D.q3-D.q1)/Math.sqrt(P);D.ln=D.med-O,D.un=D.med+O,v.push(D)}return function(t,e){if(i.isArrayOrTypedArray(e.selectedpoints))for(var r=0;r0?(v[0].t={num:d[y],dPos:k,posLetter:p,valLetter:f,labels:{med:a(t,"median:"),min:a(t,"min:"),q1:a(t,"q1:"),q3:a(t,"q3:"),max:a(t,"max:"),mean:"sd"===e.boxmean?a(t,"mean \xb1 \u03c3:"):a(t,"mean:"),lf:a(t,"lower fence:"),uf:a(t,"upper fence:")}},d[y]++,v):[{t:{empty:!0}}]}},{"../../lib":660,"../../plots/cartesian/axes":706,"fast-isnumeric":197}],815:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../../registry"),a=t("../../components/color"),o=t("./attributes");function s(t,e,r,n){var a,o,s=r("y"),l=r("x"),c=l&&l.length;if(s&&s.length)a="v",c?o=Math.min(l.length,s.length):(r("x0"),o=s.length);else{if(!c)return void(e.visible=!1);a="h",r("y0"),o=l.length}e._length=o,i.getComponentMethod("calendars","handleTraceDefaults")(t,e,["x","y"],n),r("orientation",a)}function l(t,e,r,i){var a=i.prefix,s=n.coerce2(t,e,o,"marker.outliercolor"),l=r("marker.line.outliercolor"),c=r(a+"points",s||l?"suspectedoutliers":void 0);c?(r("jitter","all"===c?.3:0),r("pointpos","all"===c?-1.5:0),r("marker.symbol"),r("marker.opacity"),r("marker.size"),r("marker.color",e.line.color),r("marker.line.color"),r("marker.line.width"),"suspectedoutliers"===c&&(r("marker.line.outliercolor",e.marker.color),r("marker.line.outlierwidth")),r("selected.marker.color"),r("unselected.marker.color"),r("selected.marker.size"),r("unselected.marker.size"),r("text")):delete e.marker,r("hoveron"),n.coerceSelectionMarkerOpacity(e,r)}e.exports={supplyDefaults:function(t,e,r,i){function c(r,i){return n.coerce(t,e,o,r,i)}s(t,e,c,i),!1!==e.visible&&(c("line.color",(t.marker||{}).color||r),c("line.width"),c("fillcolor",a.addOpacity(e.line.color,.5)),c("whiskerwidth"),c("boxmean"),c("notched",void 0!==t.notchwidth)&&c("notchwidth"),l(t,e,c,{prefix:"box"}))},handleSampleDefaults:s,handlePointsDefaults:l}},{"../../components/color":532,"../../lib":660,"../../registry":790,"./attributes":813}],816:[function(t,e,r){"use strict";var n=t("../../plots/cartesian/axes"),i=t("../../lib"),a=t("../../components/fx"),o=t("../../components/color"),s=t("../scatter/fill_hover_text");function l(t,e,r,s){var l,c,u,f,h,p,d,g,m,v,y,x,b=t.cd,_=t.xa,w=t.ya,k=b[0].trace,M=b[0].t,A="violin"===k.type,T=[],S=M.bdPos,C=M.wHover,E=function(t){return t.pos+M.bPos-p};A&&"both"!==k.side?("positive"===k.side&&(m=function(t){var e=E(t);return a.inbox(e,e+C,v)}),"negative"===k.side&&(m=function(t){var e=E(t);return a.inbox(e-C,e,v)})):m=function(t){var e=E(t);return a.inbox(e-C,e+C,v)},x=A?function(t){return a.inbox(t.span[0]-h,t.span[1]-h,v)}:function(t){return a.inbox(t.min-h,t.max-h,v)},"h"===k.orientation?(h=e,p=r,d=x,g=m,l="y",u=w,c="x",f=_):(h=r,p=e,d=m,g=x,l="x",u=_,c="y",f=w);var L=Math.min(1,S/Math.abs(u.r2c(u.range[1])-u.r2c(u.range[0])));function z(t){return(d(t)+g(t))/2}v=t.maxHoverDistance-L,y=t.maxSpikeDistance-L;var P=a.getDistanceFunction(s,d,g,z);if(a.getClosest(b,P,t),!1===t.index)return[];var D=b[t.index],O=k.line.color,I=(k.marker||{}).color;o.opacity(O)&&k.line.width?t.color=O:o.opacity(I)&&k.boxpoints?t.color=I:t.color=k.fillcolor,t[l+"0"]=u.c2p(D.pos+M.bPos-S,!0),t[l+"1"]=u.c2p(D.pos+M.bPos+S,!0),t[l+"LabelVal"]=D.pos;var R=l+"Spike";t.spikeDistance=z(D)*y/v,t[R]=u.c2p(D.pos,!0);var B={},F=["med","min","q1","q3","max"];(k.boxmean||(k.meanline||{}).visible)&&F.push("mean"),(k.boxpoints||k.points)&&F.push("lf","uf");for(var N=0;Nt.uf}),l=Math.max((t.max-t.min)/10,t.q3-t.q1),c=1e-9*l,p=l*s,d=[],g=0;if(r.jitter){if(0===l)for(g=1,d=new Array(a.length),e=0;et.lo&&(_.so=!0)}return a});d.enter().append("path").classed("point",!0),d.exit().remove(),d.call(a.translatePoints,l,c)}function u(t,e,r,a){var o,s,l=e.pos,c=e.val,u=a.bPos,f=a.bPosPxOffset||0;Array.isArray(a.bdPos)?(o=a.bdPos[0],s=a.bdPos[1]):(o=a.bdPos,s=a.bdPos);var h=t.selectAll("path.mean").data(i.identity);h.enter().append("path").attr("class","mean").style({fill:"none","vector-effect":"non-scaling-stroke"}),h.exit().remove(),h.each(function(t){var e=l.c2p(t.pos+u,!0)+f,i=l.c2p(t.pos+u-o,!0)+f,a=l.c2p(t.pos+u+s,!0)+f,h=c.c2p(t.mean,!0),p=c.c2p(t.mean-t.sd,!0),d=c.c2p(t.mean+t.sd,!0);"h"===r.orientation?n.select(this).attr("d","M"+h+","+i+"V"+a+("sd"===r.boxmean?"m0,0L"+p+","+e+"L"+h+","+i+"L"+d+","+e+"Z":"")):n.select(this).attr("d","M"+i+","+h+"H"+a+("sd"===r.boxmean?"m0,0L"+e+","+p+"L"+i+","+h+"L"+e+","+d+"Z":""))})}e.exports={plot:function(t,e,r,i){var a=t._fullLayout,o=e.xaxis,s=e.yaxis,f=i.selectAll("g.trace.boxes").data(r,function(t){return t[0].trace.uid});f.enter().append("g").attr("class","trace boxes"),f.exit().remove(),f.order(),f.each(function(t){var r=t[0],i=r.t,f=r.trace,h=n.select(this);e.isRangePlot||(r.node3=h);var p,d,g=a._numBoxes,m=1-a.boxgap,v="group"===a.boxmode&&g>1,y=i.dPos*m*(1-a.boxgroupgap)/(v?g:1),x=v?2*i.dPos*((i.num+.5)/g-.5)*m:0,b=y*f.whiskerwidth;!0!==f.visible||i.empty?h.remove():("h"===f.orientation?(p=s,d=o):(p=o,d=s),i.bPos=x,i.bdPos=y,i.wdPos=b,i.wHover=i.dPos*(v?m/g:1),l(h,{pos:p,val:d},f,i),f.boxpoints&&c(h,{x:o,y:s},f,i),f.boxmean&&u(h,{pos:p,val:d},f,i))})},plotBoxAndWhiskers:l,plotPoints:c,plotBoxMean:u}},{"../../components/drawing":557,"../../lib":660,d3:131}],821:[function(t,e,r){"use strict";e.exports=function(t,e){var r,n,i=t.cd,a=t.xaxis,o=t.yaxis,s=[];if(!1===e)for(r=0;r=10)return null;var i=1/0;var a=-1/0;var o=e.length;for(var s=0;s0?Math.floor:Math.ceil,P=E>0?Math.ceil:Math.floor,D=E>0?Math.min:Math.max,O=E>0?Math.max:Math.min,I=z(S+L),R=P(C-L),B=[[f=T(S)]];for(a=I;a*E=0;i--)a[u-i]=t[f][i],o[u-i]=e[f][i];for(s.push({x:a,y:o,bicubic:l}),i=f,a=[],o=[];i>=0;i--)a[f-i]=t[i][0],o[f-i]=e[i][0];return s.push({x:a,y:o,bicubic:c}),s}},{}],836:[function(t,e,r){"use strict";var n=t("../../plots/cartesian/axes"),i=t("../../lib/extend").extendFlat;e.exports=function(t,e,r){var a,o,s,l,c,u,f,h,p,d,g,m,v,y,x=t["_"+e],b=t[e+"axis"],_=b._gridlines=[],w=b._minorgridlines=[],k=b._boundarylines=[],M=t["_"+r],A=t[r+"axis"];"array"===b.tickmode&&(b.tickvals=x.slice());var T=t._xctrl,S=t._yctrl,C=T[0].length,E=T.length,L=t._a.length,z=t._b.length;n.prepTicks(b),"array"===b.tickmode&&delete b.tickvals;var P=b.smoothing?3:1;function D(n){var i,a,o,s,l,c,u,f,p,d,g,m,v=[],y=[],x={};if("b"===e)for(a=t.b2j(n),o=Math.floor(Math.max(0,Math.min(z-2,a))),s=a-o,x.length=z,x.crossLength=L,x.xy=function(e){return t.evalxy([],e,a)},x.dxy=function(e,r){return t.dxydi([],e,o,r,s)},i=0;i0&&(p=t.dxydi([],i-1,o,0,s),v.push(l[0]+p[0]/3),y.push(l[1]+p[1]/3),d=t.dxydi([],i-1,o,1,s),v.push(f[0]-d[0]/3),y.push(f[1]-d[1]/3)),v.push(f[0]),y.push(f[1]),l=f;else for(i=t.a2i(n),c=Math.floor(Math.max(0,Math.min(L-2,i))),u=i-c,x.length=L,x.crossLength=z,x.xy=function(e){return t.evalxy([],i,e)},x.dxy=function(e,r){return t.dxydj([],c,e,u,r)},a=0;a0&&(g=t.dxydj([],c,a-1,u,0),v.push(l[0]+g[0]/3),y.push(l[1]+g[1]/3),m=t.dxydj([],c,a-1,u,1),v.push(f[0]-m[0]/3),y.push(f[1]-m[1]/3)),v.push(f[0]),y.push(f[1]),l=f;return x.axisLetter=e,x.axis=b,x.crossAxis=A,x.value=n,x.constvar=r,x.index=h,x.x=v,x.y=y,x.smoothing=A.smoothing,x}function O(n){var i,a,o,s,l,c=[],u=[],f={};if(f.length=x.length,f.crossLength=M.length,"b"===e)for(o=Math.max(0,Math.min(z-2,n)),l=Math.min(1,Math.max(0,n-o)),f.xy=function(e){return t.evalxy([],e,n)},f.dxy=function(e,r){return t.dxydi([],e,o,r,l)},i=0;ix.length-1||_.push(i(O(o),{color:b.gridcolor,width:b.gridwidth}));for(h=u;hx.length-1||g<0||g>x.length-1))for(m=x[s],v=x[g],a=0;ax[x.length-1]||w.push(i(D(d),{color:b.minorgridcolor,width:b.minorgridwidth}));b.startline&&k.push(i(O(0),{color:b.startlinecolor,width:b.startlinewidth})),b.endline&&k.push(i(O(x.length-1),{color:b.endlinecolor,width:b.endlinewidth}))}else{for(l=5e-15,u=(c=[Math.floor((x[x.length-1]-b.tick0)/b.dtick*(1+l)),Math.ceil((x[0]-b.tick0)/b.dtick/(1+l))].sort(function(t,e){return t-e}))[0],f=c[1],h=u;h<=f;h++)p=b.tick0+b.dtick*h,_.push(i(D(p),{color:b.gridcolor,width:b.gridwidth}));for(h=u-1;hx[x.length-1]||w.push(i(D(d),{color:b.minorgridcolor,width:b.minorgridwidth}));b.startline&&k.push(i(D(x[0]),{color:b.startlinecolor,width:b.startlinewidth})),b.endline&&k.push(i(D(x[x.length-1]),{color:b.endlinecolor,width:b.endlinewidth}))}}},{"../../lib/extend":649,"../../plots/cartesian/axes":706}],837:[function(t,e,r){"use strict";var n=t("../../plots/cartesian/axes"),i=t("../../lib/extend").extendFlat;e.exports=function(t,e){var r,a,o,s=e._labels=[],l=e._gridlines;for(r=0;re.length&&(t=t.slice(0,e.length)):t=[],i=0;i90&&(p-=180,l=-l),{angle:p,flip:l,p:t.c2p(n,e,r),offsetMultplier:c}}},{}],851:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../components/drawing"),a=t("./map_1d_array"),o=t("./makepath"),s=t("./orient_text"),l=t("../../lib/svg_text_utils"),c=t("../../lib"),u=t("../../constants/alignment"),f=t("../../plots/get_data").getUidsFromCalcData;function h(t,e,r,n){var i=r[0],l=r[0].trace,u=e.xaxis,f=e.yaxis,h=l.aaxis,g=l.baxis,m=t._fullLayout._clips,y=c.ensureSingle(n,"g","carpet"+l.uid).classed("trace",!0),x=c.ensureSingle(y,"g","minorlayer"),b=c.ensureSingle(y,"g","majorlayer"),_=c.ensureSingle(y,"g","boundarylayer"),w=c.ensureSingle(y,"g","labellayer");y.style("opacity",l.opacity),p(u,f,b,h,"a",h._gridlines),p(u,f,b,g,"b",g._gridlines),p(u,f,x,h,"a",h._minorgridlines),p(u,f,x,g,"b",g._minorgridlines),p(u,f,_,h,"a-boundary",h._boundarylines),p(u,f,_,g,"b-boundary",g._boundarylines),function(t,e,r,n,i,a,o,l){var u,f,h,p;u=.5*(r.a[0]+r.a[r.a.length-1]),f=r.b[0],h=r.ab2xy(u,f,!0),p=r.dxyda_rough(u,f),void 0===o.angle&&c.extendFlat(o,s(r,i,a,h,r.dxydb_rough(u,f)));v(t,e,r,n,h,p,r.aaxis,i,a,o,"a-title"),u=r.a[0],f=.5*(r.b[0]+r.b[r.b.length-1]),h=r.ab2xy(u,f,!0),p=r.dxydb_rough(u,f),void 0===l.angle&&c.extendFlat(l,s(r,i,a,h,r.dxyda_rough(u,f)));v(t,e,r,n,h,p,r.baxis,i,a,l,"b-title")}(t,w,l,i,u,f,d(t,u,f,l,i,w,h._labels,"a-label"),d(t,u,f,l,i,w,g._labels,"b-label")),function(t,e,r,n,i){var s,l,u,f,h=r.select("#"+t._clipPathId);h.size()||(h=r.append("clipPath").classed("carpetclip",!0));var p=c.ensureSingle(h,"path","carpetboundary"),d=e.clipsegments,g=[];for(f=0;f0?"start":"end","data-notex":1}).call(i.font,o.font).text(o.text).call(l.convertToTspans,t),m=i.bBox(this);g.attr("transform","translate("+u.p[0]+","+u.p[1]+") rotate("+u.angle+")translate("+o.axis.labelpadding*h+","+.3*m.height+")"),p=Math.max(p,m.width+o.axis.labelpadding)}),h.exit().remove(),d.maxExtent=p,d}e.exports=function(t,e,r,i){var a=f(r);i.selectAll("g.trace").each(function(){var t=n.select(this).attr("class").split("carpet")[1].split(/\s/)[0];a[t]||n.select(this).remove()});for(var o=0;o90&&d<270,y=n.select(this);y.text(u.title||"").call(l.convertToTspans,t),v&&(x=(-l.lineCount(y)+m)*g*a-x),y.attr("transform","translate("+e.p[0]+","+e.p[1]+") rotate("+e.angle+") translate(0,"+x+")").classed("user-select-none",!0).attr("text-anchor","middle").call(i.font,u.titlefont)}),y.exit().remove()}},{"../../components/drawing":557,"../../constants/alignment":632,"../../lib":660,"../../lib/svg_text_utils":684,"../../plots/get_data":742,"./makepath":848,"./map_1d_array":849,"./orient_text":850,d3:131}],852:[function(t,e,r){"use strict";var n=t("./constants"),i=t("../../lib/search").findBin,a=t("./compute_control_points"),o=t("./create_spline_evaluator"),s=t("./create_i_derivative_evaluator"),l=t("./create_j_derivative_evaluator");e.exports=function(t){var e=t._a,r=t._b,c=e.length,u=r.length,f=t.aaxis,h=t.baxis,p=e[0],d=e[c-1],g=r[0],m=r[u-1],v=e[e.length-1]-e[0],y=r[r.length-1]-r[0],x=v*n.RELATIVE_CULL_TOLERANCE,b=y*n.RELATIVE_CULL_TOLERANCE;p-=x,d+=x,g-=b,m+=b,t.isVisible=function(t,e){return t>p&&tg&&ed||em},t.setScale=function(){var e=t._x,r=t._y,n=a(t._xctrl,t._yctrl,e,r,f.smoothing,h.smoothing);t._xctrl=n[0],t._yctrl=n[1],t.evalxy=o([t._xctrl,t._yctrl],c,u,f.smoothing,h.smoothing),t.dxydi=s([t._xctrl,t._yctrl],f.smoothing,h.smoothing),t.dxydj=l([t._xctrl,t._yctrl],f.smoothing,h.smoothing)},t.i2a=function(t){var r=Math.max(0,Math.floor(t[0]),c-2),n=t[0]-r;return(1-n)*e[r]+n*e[r+1]},t.j2b=function(t){var e=Math.max(0,Math.floor(t[1]),c-2),n=t[1]-e;return(1-n)*r[e]+n*r[e+1]},t.ij2ab=function(e){return[t.i2a(e[0]),t.j2b(e[1])]},t.a2i=function(t){var r=Math.max(0,Math.min(i(t,e),c-2)),n=e[r],a=e[r+1];return Math.max(0,Math.min(c-1,r+(t-n)/(a-n)))},t.b2j=function(t){var e=Math.max(0,Math.min(i(t,r),u-2)),n=r[e],a=r[e+1];return Math.max(0,Math.min(u-1,e+(t-n)/(a-n)))},t.ab2ij=function(e){return[t.a2i(e[0]),t.b2j(e[1])]},t.i2c=function(e,r){return t.evalxy([],e,r)},t.ab2xy=function(n,i,a){if(!a&&(ne[c-1]|ir[u-1]))return[!1,!1];var o=t.a2i(n),s=t.b2j(i),l=t.evalxy([],o,s);if(a){var f,h,p,d,g=0,m=0,v=[];ne[c-1]?(f=c-2,h=1,g=(n-e[c-1])/(e[c-1]-e[c-2])):h=o-(f=Math.max(0,Math.min(c-2,Math.floor(o)))),ir[u-1]?(p=u-2,d=1,m=(i-r[u-1])/(r[u-1]-r[u-2])):d=s-(p=Math.max(0,Math.min(u-2,Math.floor(s)))),g&&(t.dxydi(v,f,p,h,d),l[0]+=v[0]*g,l[1]+=v[1]*g),m&&(t.dxydj(v,f,p,h,d),l[0]+=v[0]*m,l[1]+=v[1]*m)}return l},t.c2p=function(t,e,r){return[e.c2p(t[0]),r.c2p(t[1])]},t.p2x=function(t,e,r){return[e.p2c(t[0]),r.p2c(t[1])]},t.dadi=function(t){var r=Math.max(0,Math.min(e.length-2,t));return e[r+1]-e[r]},t.dbdj=function(t){var e=Math.max(0,Math.min(r.length-2,t));return r[e+1]-r[e]},t.dxyda=function(e,r,n,i){var a=t.dxydi(null,e,r,n,i),o=t.dadi(e,n);return[a[0]/o,a[1]/o]},t.dxydb=function(e,r,n,i){var a=t.dxydj(null,e,r,n,i),o=t.dbdj(r,i);return[a[0]/o,a[1]/o]},t.dxyda_rough=function(e,r,n){var i=v*(n||.1),a=t.ab2xy(e+i,r,!0),o=t.ab2xy(e-i,r,!0);return[.5*(a[0]-o[0])/i,.5*(a[1]-o[1])/i]},t.dxydb_rough=function(e,r,n){var i=y*(n||.1),a=t.ab2xy(e,r+i,!0),o=t.ab2xy(e,r-i,!0);return[.5*(a[0]-o[0])/i,.5*(a[1]-o[1])/i]},t.dpdx=function(t){return t._m},t.dpdy=function(t){return t._m}}},{"../../lib/search":679,"./compute_control_points":840,"./constants":841,"./create_i_derivative_evaluator":842,"./create_j_derivative_evaluator":843,"./create_spline_evaluator":844}],853:[function(t,e,r){"use strict";var n=t("../../lib");e.exports=function(t,e,r){var i,a,o,s=[],l=[],c=t[0].length,u=t.length;function f(e,r){var n,i=0,a=0;return e>0&&void 0!==(n=t[r][e-1])&&(a++,i+=n),e0&&void 0!==(n=t[r-1][e])&&(a++,i+=n),r0&&a0&&i1e-5);return n.log("Smoother converged to",M,"after",A,"iterations"),t}},{"../../lib":660}],854:[function(t,e,r){"use strict";var n=t("../../lib").isArray1D;e.exports=function(t,e,r){var i=r("x"),a=i&&i.length,o=r("y"),s=o&&o.length;if(!a&&!s)return!1;if(e._cheater=!i,a&&!n(i)||s&&!n(o))e._length=null;else{var l=a?i.length:1/0;s&&(l=Math.min(l,o.length)),e.a&&e.a.length&&(l=Math.min(l,e.a.length)),e.b&&e.b.length&&(l=Math.min(l,e.b.length)),e._length=l}return!0}},{"../../lib":660}],855:[function(t,e,r){"use strict";var n=t("../scattergeo/attributes"),i=t("../../components/colorscale/attributes"),a=t("../../components/colorbar/attributes"),o=t("../../plots/attributes"),s=t("../../lib/extend"),l=s.extendFlat,c=s.extendDeepAll,u=n.marker.line;e.exports=l({locations:{valType:"data_array",editType:"calc"},locationmode:n.locationmode,z:{valType:"data_array",editType:"calc"},text:l({},n.text,{}),marker:{line:{color:u.color,width:l({},u.width,{dflt:1}),editType:"calc"},opacity:{valType:"number",arrayOk:!0,min:0,max:1,dflt:1,editType:"style"},editType:"calc"},selected:{marker:{opacity:n.selected.marker.opacity,editType:"plot"},editType:"plot"},unselected:{marker:{opacity:n.unselected.marker.opacity,editType:"plot"},editType:"plot"},hoverinfo:l({},o.hoverinfo,{editType:"calc",flags:["location","z","text","name"]})},c({},i,{zmax:{editType:"calc"},zmin:{editType:"calc"}}),{colorbar:a})},{"../../components/colorbar/attributes":533,"../../components/colorscale/attributes":538,"../../lib/extend":649,"../../plots/attributes":703,"../scattergeo/attributes":1028}],856:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../../constants/numerical").BADNUM,a=t("../../components/colorscale/calc"),o=t("../scatter/arrays_to_calcdata"),s=t("../scatter/calc_selection");e.exports=function(t,e){for(var r=e._length,l=new Array(r),c=0;c")}(t,f,o,h.mockAxis),[t]}},{"../../plots/cartesian/axes":706,"../scatter/fill_hover_text":998,"./attributes":855}],860:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.colorbar=t("../heatmap/colorbar"),n.calc=t("./calc"),n.plot=t("./plot"),n.style=t("./style").style,n.styleOnSelect=t("./style").styleOnSelect,n.hoverPoints=t("./hover"),n.eventData=t("./event_data"),n.selectPoints=t("./select"),n.moduleType="trace",n.name="choropleth",n.basePlotModule=t("../../plots/geo"),n.categories=["geo","noOpacity"],n.meta={},e.exports=n},{"../../plots/geo":736,"../heatmap/colorbar":902,"./attributes":855,"./calc":856,"./defaults":857,"./event_data":858,"./hover":859,"./plot":861,"./select":862,"./style":863}],861:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../lib"),a=t("../../lib/polygon"),o=t("../../lib/topojson_utils").getTopojsonFeatures,s=t("../../lib/geo_location_utils").locationToFeature,l=t("./style").style;function c(t,e){for(var r=t[0].trace,n=t.length,i=o(r,e),a=0;a0&&t[e+1][0]<0)return e;return null}switch(e="RUS"===l||"FJI"===l?function(t){var e;if(null===u(t))e=t;else for(e=new Array(t.length),i=0;ie?r[n++]=[t[i][0]+360,t[i][1]]:i===e?(r[n++]=t[i],r[n++]=[t[i][0],-90]):r[n++]=t[i];var o=a.tester(r);o.pts.pop(),c.push(o)}:function(t){c.push(a.tester(t))},o.type){case"MultiPolygon":for(r=0;r":f.value>h&&(s.prefixBoundary=!0);break;case"<":f.valueh)&&(s.prefixBoundary=!0);break;case"][":a=Math.min.apply(null,f.value),o=Math.max.apply(null,f.value),ah&&(s.prefixBoundary=!0)}}},{}],873:[function(t,e,r){"use strict";var n=t("../../plots/plots"),i=t("../../components/colorbar/draw"),a=t("./make_color_map"),o=t("./end_plus");e.exports=function(t,e){var r=e[0].trace,s="cb"+r.uid;if(t._fullLayout._infolayer.selectAll("."+s).remove(),r.showscale){var l=i(t,s);e[0].t.cb=l;var c=r.contours,u=r.line,f=c.size||1,h=c.coloring,p=a(r,{isColorbar:!0});"heatmap"===h&&l.filllevels({start:r.zmin,end:r.zmax,size:(r.zmax-r.zmin)/254}),l.fillcolor("fill"===h||"heatmap"===h?p:"").line({color:"lines"===h?p:u.color,width:!1!==c.showlines?u.width:0,dash:u.dash}).levels({start:c.start,end:o(c),size:f}).options(r.colorbar)()}else n.autoMargin(t,s)}},{"../../components/colorbar/draw":536,"../../plots/plots":768,"./end_plus":881,"./make_color_map":886}],874:[function(t,e,r){"use strict";e.exports={BOTTOMSTART:[1,9,13,104,713],TOPSTART:[4,6,7,104,713],LEFTSTART:[8,12,14,208,1114],RIGHTSTART:[2,3,11,208,1114],NEWDELTA:[null,[-1,0],[0,-1],[-1,0],[1,0],null,[0,-1],[-1,0],[0,1],[0,1],null,[0,1],[1,0],[1,0],[0,-1]],CHOOSESADDLE:{104:[4,1],208:[2,8],713:[7,13],1114:[11,14]},SADDLEREMAINDER:{1:4,2:8,4:1,7:13,8:2,11:14,13:7,14:11},LABELDISTANCE:2,LABELINCREASE:10,LABELMIN:3,LABELMAX:10,LABELOPTIMIZER:{EDGECOST:1,ANGLECOST:1,NEIGHBORCOST:5,SAMELEVELFACTOR:10,SAMELEVELDISTANCE:5,MAXCOST:100,INITIALSEARCHPOINTS:10,ITERATIONS:5}}},{}],875:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("./label_defaults"),a=t("../../components/color"),o=a.addOpacity,s=a.opacity,l=t("../../constants/filter_ops"),c=l.CONSTRAINT_REDUCTION,u=l.COMPARISON_OPS2;e.exports=function(t,e,r,a,l,f){var h,p,d,g=e.contours,m=r("contours.operation");(g._operation=c[m],function(t,e){var r;-1===u.indexOf(e.operation)?(t("contours.value",[0,1]),Array.isArray(e.value)?e.value.length>2?e.value=e.value.slice(2):0===e.length?e.value=[0,1]:e.length<2?(r=parseFloat(e.value[0]),e.value=[r,r+1]):e.value=[parseFloat(e.value[0]),parseFloat(e.value[1])]:n(e.value)&&(r=parseFloat(e.value),e.value=[r,r+1])):(t("contours.value",0),n(e.value)||(Array.isArray(e.value)?e.value=parseFloat(e.value[0]):e.value=0))}(r,g),"="===m?h=g.showlines=!0:(h=r("contours.showlines"),d=r("fillcolor",o((t.line||{}).color||l,.5))),h)&&(p=r("line.color",d&&s(d)?o(e.fillcolor,1):l),r("line.width",2),r("line.dash"));r("line.smoothing"),i(r,a,p,f)}},{"../../components/color":532,"../../constants/filter_ops":633,"./label_defaults":885,"fast-isnumeric":197}],876:[function(t,e,r){"use strict";var n=t("../../constants/filter_ops"),i=t("fast-isnumeric");function a(t,e){var r,a=Array.isArray(e);function o(t){return i(t)?+t:null}return-1!==n.COMPARISON_OPS2.indexOf(t)?r=o(a?e[0]:e):-1!==n.INTERVAL_OPS.indexOf(t)?r=a?[o(e[0]),o(e[1])]:[o(e),o(e)]:-1!==n.SET_OPS.indexOf(t)&&(r=a?e.map(o):[o(e)]),r}function o(t){return function(e){e=a(t,e);var r=Math.min(e[0],e[1]),n=Math.max(e[0],e[1]);return{start:r,end:n,size:n-r}}}function s(t){return function(e){return{start:e=a(t,e),end:1/0,size:1/0}}}e.exports={"[]":o("[]"),"][":o("]["),">":s(">"),"<":s("<"),"=":s("=")}},{"../../constants/filter_ops":633,"fast-isnumeric":197}],877:[function(t,e,r){"use strict";e.exports=function(t,e,r,n){var i=n("contours.start"),a=n("contours.end"),o=!1===i||!1===a,s=r("contours.size");!(o?e.autocontour=!0:r("autocontour",!1))&&s||r("ncontours")}},{}],878:[function(t,e,r){"use strict";var n=t("../../lib");function i(t){return n.extendFlat({},t,{edgepaths:n.extendDeep([],t.edgepaths),paths:n.extendDeep([],t.paths)})}e.exports=function(t,e){var r,a,o,s=function(t){return t.reverse()},l=function(t){return t};switch(e){case"=":case"<":return t;case">":for(1!==t.length&&n.warn("Contour data invalid for the specified inequality operation."),a=t[0],r=0;r1e3){n.warn("Too many contours, clipping at 1000",t);break}return l}},{"../../lib":660,"./constraint_mapping":876,"./end_plus":881}],881:[function(t,e,r){"use strict";e.exports=function(t){return t.end+t.size/1e6}},{}],882:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("./constants");function a(t,e,r,n){return Math.abs(t[0]-e[0])20&&e?208===t||1114===t?n=0===r[0]?1:-1:a=0===r[1]?1:-1:-1!==i.BOTTOMSTART.indexOf(t)?a=1:-1!==i.LEFTSTART.indexOf(t)?n=1:-1!==i.TOPSTART.indexOf(t)?a=-1:n=-1;return[n,a]}(h,r,e),d=[s(t,e,[-p[0],-p[1]])],g=p.join(","),m=t.z.length,v=t.z[0].length;for(c=0;c<1e4;c++){if(h>20?(h=i.CHOOSESADDLE[h][(p[0]||p[1])<0?0:1],t.crossings[f]=i.SADDLEREMAINDER[h]):delete t.crossings[f],!(p=i.NEWDELTA[h])){n.log("Found bad marching index:",h,e,t.level);break}d.push(s(t,e,p)),e[0]+=p[0],e[1]+=p[1],a(d[d.length-1],d[d.length-2],o,l)&&d.pop(),f=e.join(",");var y=p[0]&&(e[0]<0||e[0]>v-2)||p[1]&&(e[1]<0||e[1]>m-2);if(f===u&&p.join(",")===g||r&&y)break;h=t.crossings[f]}1e4===c&&n.log("Infinite loop in contour?");var x,b,_,w,k,M,A,T,S,C,E,L,z,P,D,O=a(d[0],d[d.length-1],o,l),I=0,R=.2*t.smoothing,B=[],F=0;for(c=1;c=F;c--)if((x=B[c])=F&&x+B[b]T&&S--,t.edgepaths[S]=E.concat(d,C));break}U||(t.edgepaths[T]=d.concat(C))}for(T=0;Tt?0:1)+(e[0][1]>t?0:2)+(e[1][1]>t?0:4)+(e[1][0]>t?0:8);return 5===r||10===r?t>(e[0][0]+e[0][1]+e[1][0]+e[1][1])/4?5===r?713:1114:5===r?104:208:15===r?0:r}e.exports=function(t){var e,r,a,o,s,l,c,u,f,h=t[0].z,p=h.length,d=h[0].length,g=2===p||2===d;for(r=0;rt.level}return r?"M"+e.join("L")+"Z":""}(t,e),h=0,p=t.edgepaths.map(function(t,e){return e}),d=!0;function g(t){return Math.abs(t[1]-e[2][1])<.01}function m(t){return Math.abs(t[0]-e[0][0])<.01}function v(t){return Math.abs(t[0]-e[2][0])<.01}for(;p.length;){for(c=a.smoothopen(t.edgepaths[h],t.smoothing),f+=d?c:c.replace(/^M/,"L"),p.splice(p.indexOf(h),1),r=t.edgepaths[h][t.edgepaths[h].length-1],s=-1,o=0;o<4;o++){if(!r){i.log("Missing end?",h,t);break}for(u=r,Math.abs(u[1]-e[0][1])<.01&&!v(r)?n=e[1]:m(r)?n=e[0]:g(r)?n=e[3]:v(r)&&(n=e[2]),l=0;l=0&&(n=y,s=l):Math.abs(r[1]-n[1])<.01?Math.abs(r[1]-y[1])<.01&&(y[0]-r[0])*(n[0]-y[0])>=0&&(n=y,s=l):i.log("endpt to newendpt is not vert. or horz.",r,n,y)}if(r=n,s>=0)break;f+="L"+n}if(s===t.edgepaths.length){i.log("unclosed perimeter path");break}h=s,(d=-1===p.indexOf(h))&&(h=p[0],f+="Z")}for(h=0;hn.center?n.right-s:s-n.left)/(u+Math.abs(Math.sin(c)*o)),p=(l>n.middle?n.bottom-l:l-n.top)/(Math.abs(f)+Math.cos(c)*o);if(h<1||p<1)return 1/0;var d=v.EDGECOST*(1/(h-1)+1/(p-1));d+=v.ANGLECOST*c*c;for(var g=s-u,m=l-f,y=s+u,x=l+f,b=0;b2*v.MAXCOST)break;p&&(s/=2),l=(o=c-s/2)+1.5*s}if(h<=v.MAXCOST)return u},r.addLabelData=function(t,e,r,n){var i=e.width/2,a=e.height/2,o=t.x,s=t.y,l=t.theta,c=Math.sin(l),u=Math.cos(l),f=i*u,h=a*c,p=i*c,d=-a*u,g=[[o-f-h,s-p-d],[o+f-h,s+p-d],[o+f+h,s+p+d],[o-f+h,s-p+d]];r.push({text:e.text,x:o,y:s,dy:e.dy,theta:l,level:e.level,width:e.width,height:e.height}),n.push(g)},r.drawLabels=function(t,e,r,a,s){var l=t.selectAll("text").data(e,function(t){return t.text+","+t.x+","+t.y+","+t.theta});if(l.exit().remove(),l.enter().append("text").attr({"data-notex":1,"text-anchor":"middle"}).each(function(t){var e=t.x+Math.sin(t.theta)*t.dy,i=t.y-Math.cos(t.theta)*t.dy;n.select(this).text(t.text).attr({x:e,y:i,transform:"rotate("+180*t.theta/Math.PI+" "+e+" "+i+")"}).call(o.convertToTspans,r)}),s){for(var c="",u=0;ue.end&&(e.start=e.end=(e.start+e.end)/2),t._input.contours||(t._input.contours={}),i.extendFlat(t._input.contours,{start:e.start,end:e.end,size:e.size}),t._input.autocontour=!0}else if("constraint"!==e.type){var l,c=e.start,u=e.end,f=t._input.contours;if(c>u&&(e.start=f.start=u,u=e.end=f.end=c,c=e.start),!(e.size>0))l=c===u?1:a(c,u,t.ncontours).dtick,f.size=e.size=l}}},{"../../lib":660,"../../plots/cartesian/axes":706}],890:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../components/drawing"),a=t("../heatmap/style"),o=t("./make_color_map");e.exports=function(t){var e=n.select(t).selectAll("g.contour");e.style("opacity",function(t){return t.trace.opacity}),e.each(function(t){var e=n.select(this),r=t.trace,a=r.contours,s=r.line,l=a.size||1,c=a.start,u="constraint"===a.type,f=!u&&"lines"===a.coloring,h=!u&&"fill"===a.coloring,p=f||h?o(r):null;e.selectAll("g.contourlevel").each(function(t){n.select(this).selectAll("path").call(i.lineGroupStyle,s.width,f?p(t.level):s.color,s.dash)});var d=a.labelfont;if(e.selectAll("g.contourlabels text").each(function(t){i.font(n.select(this),{family:d.family,size:d.size,color:d.color||(f?p(t.level):s.color)})}),u)e.selectAll("g.contourfill path").style("fill",r.fillcolor);else if(h){var g;e.selectAll("g.contourfill path").style("fill",function(t){return void 0===g&&(g=t.level),p(t.level+.5*l)}),void 0===g&&(g=c),e.selectAll("g.contourbg path").style("fill",p(g-.5*l))}}),a(t)}},{"../../components/drawing":557,"../heatmap/style":912,"./make_color_map":886,d3:131}],891:[function(t,e,r){"use strict";var n=t("../../components/colorscale/defaults"),i=t("./label_defaults");e.exports=function(t,e,r,a,o){var s,l=r("contours.coloring"),c="";"fill"===l&&(s=r("contours.showlines")),!1!==s&&("lines"!==l&&(c=r("line.color","#000")),r("line.width",.5),r("line.dash")),"none"!==l&&n(t,e,a,r,{prefix:"",cLetter:"z"}),r("line.smoothing"),i(r,a,c,o)}},{"../../components/colorscale/defaults":542,"./label_defaults":885}],892:[function(t,e,r){"use strict";var n=t("../heatmap/attributes"),i=t("../contour/attributes"),a=i.contours,o=t("../scatter/attributes"),s=t("../../components/colorscale/attributes"),l=t("../../components/colorbar/attributes"),c=t("../../lib/extend").extendFlat,u=o.line;e.exports=c({},{carpet:{valType:"string",editType:"calc"},z:n.z,a:n.x,a0:n.x0,da:n.dx,b:n.y,b0:n.y0,db:n.dy,text:n.text,transpose:n.transpose,atype:n.xtype,btype:n.ytype,fillcolor:i.fillcolor,autocontour:i.autocontour,ncontours:i.ncontours,contours:{type:a.type,start:a.start,end:a.end,size:a.size,coloring:{valType:"enumerated",values:["fill","lines","none"],dflt:"fill",editType:"calc"},showlines:a.showlines,showlabels:a.showlabels,labelfont:a.labelfont,labelformat:a.labelformat,operation:a.operation,value:a.value,editType:"calc",impliedEdits:{autocontour:!1}},line:{color:c({},u.color,{}),width:u.width,dash:u.dash,smoothing:c({},u.smoothing,{}),editType:"plot"}},s,{autocolorscale:c({},s.autocolorscale,{dflt:!1})},{colorbar:l})},{"../../components/colorbar/attributes":533,"../../components/colorscale/attributes":538,"../../lib/extend":649,"../contour/attributes":870,"../heatmap/attributes":899,"../scatter/attributes":990}],893:[function(t,e,r){"use strict";var n=t("../../components/colorscale/calc"),i=t("../../lib").isArray1D,a=t("../heatmap/convert_column_xyz"),o=t("../heatmap/clean_2d_array"),s=t("../heatmap/max_row_length"),l=t("../heatmap/interp2d"),c=t("../heatmap/find_empties"),u=t("../heatmap/make_bound_array"),f=t("./defaults"),h=t("../carpet/lookup_carpetid"),p=t("../contour/set_contours");e.exports=function(t,e){var r=e._carpetTrace=h(t,e);if(r&&r.visible&&"legendonly"!==r.visible){if(!e.a||!e.b){var d=t.data[r.index],g=t.data[e.index];g.a||(g.a=d.a),g.b||(g.b=d.b),f(g,e,e._defaultColor,t._fullLayout)}var m=function(t,e){var r,f,h,p,d,g,m,v=e._carpetTrace,y=v.aaxis,x=v.baxis;y._minDtick=0,x._minDtick=0,i(e.z)&&a(e,y,x,"a","b",["z"]);r=e._a=e._a||e.a,p=e._b=e._b||e.b,r=r?y.makeCalcdata(e,"_a"):[],p=p?x.makeCalcdata(e,"_b"):[],f=e.a0||0,h=e.da||1,d=e.b0||0,g=e.db||1,m=e._z=o(e._z||e.z,e.transpose),e._emptypoints=c(m),l(m,e._emptypoints);var b=s(m),_="scaled"===e.xtype?"":r,w=u(e,_,f,h,b,y),k="scaled"===e.ytype?"":p,M=u(e,k,d,g,m.length,x),A={a:w,b:M,z:m};"levels"===e.contours.type&&"none"!==e.contours.coloring&&n(e,m,"","z");return[A]}(0,e);return p(e),m}}},{"../../components/colorscale/calc":539,"../../lib":660,"../carpet/lookup_carpetid":847,"../contour/set_contours":889,"../heatmap/clean_2d_array":901,"../heatmap/convert_column_xyz":903,"../heatmap/find_empties":905,"../heatmap/interp2d":908,"../heatmap/make_bound_array":909,"../heatmap/max_row_length":910,"./defaults":894}],894:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../heatmap/xyz_defaults"),a=t("./attributes"),o=t("../contour/constraint_defaults"),s=t("../contour/contours_defaults"),l=t("../contour/style_defaults");e.exports=function(t,e,r,c){function u(r,i){return n.coerce(t,e,a,r,i)}if(u("carpet"),t.a&&t.b){if(!i(t,e,u,c,"a","b"))return void(e.visible=!1);u("text");var f="constraint"===u("contours.type");f||delete e.showlegend,f?o(t,e,u,c,r,{hasHover:!1}):(s(t,e,u,function(r){return n.coerce2(t,e,a,r)}),l(t,e,u,c,{hasHover:!1}))}else e._defaultColor=r,e._length=null}},{"../../lib":660,"../contour/constraint_defaults":875,"../contour/contours_defaults":877,"../contour/style_defaults":891,"../heatmap/xyz_defaults":914,"./attributes":892}],895:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.colorbar=t("../contour/colorbar"),n.calc=t("./calc"),n.plot=t("./plot"),n.style=t("../contour/style"),n.moduleType="trace",n.name="contourcarpet",n.basePlotModule=t("../../plots/cartesian"),n.categories=["cartesian","svg","carpet","contour","symbols","showLegend","hasLines","carpetDependent"],n.meta={},e.exports=n},{"../../plots/cartesian":717,"../contour/colorbar":873,"../contour/style":890,"./attributes":892,"./calc":893,"./defaults":894,"./plot":898}],896:[function(t,e,r){"use strict";var n=t("../../components/drawing"),i=t("../carpet/axis_aligned_line"),a=t("../../lib");e.exports=function(t,e,r,o,s,l,c,u){var f,h,p,d,g,m,v,y="",x=e.edgepaths.map(function(t,e){return e}),b=!0,_=1e-4*Math.abs(r[0][0]-r[2][0]),w=1e-4*Math.abs(r[0][1]-r[2][1]);function k(t){return Math.abs(t[1]-r[0][1])=0&&(p=E,g=m):Math.abs(h[1]-p[1])=0&&(p=E,g=m):a.log("endpt to newendpt is not vert. or horz.",h,p,E)}if(g>=0)break;y+=S(h,p),h=p}if(g===e.edgepaths.length){a.log("unclosed perimeter path");break}f=g,(b=-1===x.indexOf(f))&&(f=x[0],y+=S(h,p)+"Z",h=null)}for(f=0;f=0;q--)j=M.clipsegments[q],V=i([],j.x,E.c2p),U=i([],j.y,L.c2p),V.reverse(),U.reverse(),G.push(a(V,U,j.bicubic));var W="M"+G.join("L")+"Z";!function(t,e,r,n,o,l){var c,u,f,h,p=s.ensureSingle(t,"g","contourbg").selectAll("path").data("fill"!==l||o?[]:[0]);p.enter().append("path"),p.exit().remove();var d=[];for(h=0;hm&&(n.max=m);n.len=n.max-n.min}(this,r,t,n,c,e.height),!(n.len<(e.width+e.height)*h.LABELMIN)))for(var i=Math.min(Math.ceil(n.len/P),h.LABELMAX),a=0;az){E("x scale is not linear");break}}if(m.length&&"fast"===S){var P=(m[m.length-1]-m[0])/(m.length-1),D=Math.abs(P/100);for(b=0;bD){E("y scale is not linear");break}}}var O=c(x),I="scaled"===e.xtype?"":r,R=p(e,I,d,g,O,w),B="scaled"===e.ytype?"":m,F=p(e,B,v,y,x.length,k);T||(a.expand(w,R),a.expand(k,F));var N={x:R,y:F,z:x,text:e._text||e.text};if(I&&I.length===R.length-1&&(N.xCenter=I),B&&B.length===F.length-1&&(N.yCenter=B),A&&(N.xRanges=_.xRanges,N.yRanges=_.yRanges,N.pts=_.pts),M&&"constraint"===e.contours.type||s(e,x,"","z"),M&&e.contours&&"heatmap"===e.contours.coloring){var j={type:"contour"===e.type?"heatmap":"histogram2d",xcalendar:e.xcalendar,ycalendar:e.ycalendar};N.xfill=p(j,I,d,g,O,w),N.yfill=p(j,B,v,y,x.length,k)}return[N]}},{"../../components/colorscale/calc":539,"../../lib":660,"../../plots/cartesian/axes":706,"../../registry":790,"../histogram2d/calc":931,"./clean_2d_array":901,"./convert_column_xyz":903,"./find_empties":905,"./interp2d":908,"./make_bound_array":909,"./max_row_length":910}],901:[function(t,e,r){"use strict";var n=t("fast-isnumeric");e.exports=function(t,e){var r,i,a,o,s,l;function c(t){if(n(t))return+t}if(e){for(r=0,s=0;s=0;o--)(s=((f[[(r=(a=h[o])[0])-1,i=a[1]]]||g)[2]+(f[[r+1,i]]||g)[2]+(f[[r,i-1]]||g)[2]+(f[[r,i+1]]||g)[2])/20)&&(l[a]=[r,i,s],h.splice(o,1),c=!0);if(!c)throw"findEmpties iterated with no new neighbors";for(a in l)f[a]=l[a],u.push(l[a])}return u.sort(function(t,e){return e[2]-t[2]})}},{"./max_row_length":910}],906:[function(t,e,r){"use strict";var n=t("../../components/fx"),i=t("../../lib"),a=t("../../plots/cartesian/axes");e.exports=function(t,e,r,o,s,l){var c,u,f,h,p=t.cd[0],d=p.trace,g=t.xa,m=t.ya,v=p.x,y=p.y,x=p.z,b=p.xCenter,_=p.yCenter,w=p.zmask,k=[d.zmin,d.zmax],M=d.zhoverformat,A=v,T=y;if(!1!==t.index){try{f=Math.round(t.index[1]),h=Math.round(t.index[0])}catch(e){return void i.error("Error hovering on heatmap, pointNumber must be [row,col], found:",t.index)}if(f<0||f>=x[0].length||h<0||h>x.length)return}else{if(n.inbox(e-v[0],e-v[v.length-1],0)>0||n.inbox(r-y[0],r-y[y.length-1],0)>0)return;if(l){var S;for(A=[2*v[0]-v[1]],S=1;Sg&&(v=Math.max(v,Math.abs(t[a][o]-d)/(m-g))))}return v}e.exports=function(t,e){var r,i=1;for(o(t,e),r=0;r.01;r++)i=o(t,e,a(i));return i>.01&&n.log("interp2d didn't converge quickly",i),t}},{"../../lib":660}],909:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("../../lib").isArrayOrTypedArray;e.exports=function(t,e,r,a,o,s){var l,c,u,f=[],h=n.traceIs(t,"contour"),p=n.traceIs(t,"histogram"),d=n.traceIs(t,"gl2d");if(i(e)&&e.length>1&&!p&&"category"!==s.type){var g=e.length;if(!(g<=o))return h?e.slice(0,o):e.slice(0,o+1);if(h||d)f=e.slice(0,o);else if(1===o)f=[e[0]-.5,e[0]+.5];else{for(f=[1.5*e[0]-.5*e[1]],u=1;u0;)f=_.c2p(A[y]),y--;for(f0;)v=w.c2p(T[y]),y--;if(v image").each(function(t){var e=t.trace||{};a[e.uid]||n.select(this.parentNode).remove()});for(var o=0;o0&&(a=!0);for(var l=0;la){var o=a-r[t];return r[t]=a,o}}return 0},max:function(t,e,r,i){var a=i[e];if(n(a)){if(a=Number(a),!n(r[t]))return r[t]=a,a;if(r[t]c?t>o?t>1.1*i?i:t>1.1*a?a:o:t>s?s:t>l?l:c:Math.pow(10,Math.floor(Math.log(t)/Math.LN10))}function p(t,e,r,n,a,s){if(n&&t>o){var l=d(e,a,s),c=d(r,a,s),u=t===i?0:1;return l[u]!==c[u]}return Math.floor(r/t)-Math.floor(e/t)>.1}function d(t,e,r){var n=e.c2d(t,i,r).split("-");return""===n[0]&&(n.unshift(),n[0]="-"+n[0]),n}e.exports=function(t,e,r,n,a){var s,l,c=-1.1*e,h=-.1*e,p=t-h,d=r[0],g=r[1],m=Math.min(f(d+h,d+p,n,a),f(g+h,g+p,n,a)),v=Math.min(f(d+c,d+h,n,a),f(g+c,g+h,n,a));if(m>v&&vo){var y=s===i?1:6,x=s===i?"M12":"M1";return function(e,r){var o=n.c2d(e,i,a),s=o.indexOf("-",y);s>0&&(o=o.substr(0,s));var c=n.d2c(o,0,a);if(cu.size/1.9?u.size:u.size/Math.ceil(u.size/x);var A=u.start+(u.size-x)/2;b=A-x*Math.ceil((A-b)/x)}for(s=0;s=0&&w=0;n--)s(n);else if("increasing"===e){for(n=1;n=0;n--)t[n]+=t[n+1];"exclude"===r&&(t.push(0),t.shift())}}(d,x.direction,x.currentbin);var Z=Math.min(f.length,d.length),J=[],K=0,Q=Z-1;for(r=0;r=K;r--)if(d[r]){Q=r;break}for(r=K;r<=Q;r++)if(n(f[r])&&n(d[r])){var $={p:f[r],s:d[r],b:0};x.enabled||($.pts=z[r],H?$.p0=$.p1=z[r].length?A[z[r][0]]:f[r]:($.p0=U(S[r]),$.p1=U(S[r+1],!0))),J.push($)}return 1===J.length&&(J[0].width1=a.tickIncrement(J[0].p,M.size,!1,y)-J[0].p),o(J,e),i.isArrayOrTypedArray(e.selectedpoints)&&i.tagSelected(J,e,Y),J}}},{"../../constants/numerical":637,"../../lib":660,"../../plots/cartesian/axes":706,"../bar/arrays_to_calcdata":799,"./average":919,"./bin_functions":921,"./bin_label_vals":922,"./clean_bins":924,"./norm_functions":929,"fast-isnumeric":197}],924:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../../lib").cleanDate,a=t("../../constants/numerical"),o=a.ONEDAY,s=a.BADNUM;e.exports=function(t,e,r){var a=e.type,l=r+"bins",c=t[l];c||(c=t[l]={});var u="date"===a?function(t){return t||0===t?i(t,s,c.calendar):null}:function(t){return n(t)?Number(t):null};c.start=u(c.start),c.end=u(c.end);var f="date"===a?o:1,h=c.size;if(n(h))c.size=h>0?Number(h):f;else if("string"!=typeof h)c.size=f;else{var p=h.charAt(0),d=h.substr(1);((d=n(d)?Number(d):0)<=0||"date"!==a||"M"!==p||d!==Math.round(d))&&(c.size=f)}var g="autobin"+r;"boolean"!=typeof t[g]&&(t[g]=t._fullInput[g]=t._input[g]=!((c.start||0===c.start)&&(c.end||0===c.end))),t[g]||(delete t["nbins"+r],delete t._fullInput["nbins"+r])}},{"../../constants/numerical":637,"../../lib":660,"fast-isnumeric":197}],925:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("../../lib"),a=t("../../components/color"),o=t("./bin_defaults"),s=t("../bar/style_defaults"),l=t("./attributes");e.exports=function(t,e,r,c){function u(r,n){return i.coerce(t,e,l,r,n)}var f=u("x"),h=u("y");u("cumulative.enabled")&&(u("cumulative.direction"),u("cumulative.currentbin")),u("text");var p=u("orientation",h&&!f?"h":"v"),d="v"===p?"x":"y",g="v"===p?"y":"x",m=f&&h?Math.min(f.length&&h.length):(e[d]||[]).length;if(m){e._length=m,n.getComponentMethod("calendars","handleTraceDefaults")(t,e,["x","y"],c),e[g]&&u("histfunc"),o(t,e,u,[d]),s(t,e,u,r,c);var v=n.getComponentMethod("errorbars","supplyDefaults");v(t,e,a.defaultLine,{axis:"y"}),v(t,e,a.defaultLine,{axis:"x",inherit:"y"}),i.coerceSelectionMarkerOpacity(e,u)}else e.visible=!1}},{"../../components/color":532,"../../lib":660,"../../registry":790,"../bar/style_defaults":812,"./attributes":918,"./bin_defaults":920}],926:[function(t,e,r){"use strict";e.exports=function(t,e,r,n,i){if(t.x="xVal"in e?e.xVal:e.x,t.y="yVal"in e?e.yVal:e.y,e.xa&&(t.xaxis=e.xa),e.ya&&(t.yaxis=e.ya),!(r.cumulative||{}).enabled){var a,o=Array.isArray(i)?n[0].pts[i[0]][i[1]]:n[i].pts;if(t.pointNumbers=o,t.binNumber=t.pointNumber,delete t.pointNumber,delete t.pointIndex,r._indexToPoints){a=[];for(var s=0;sA&&m.splice(A,m.length-A),y.length>A&&y.splice(A,y.length-A),u(e,"x",m,g,_,k,x),u(e,"y",y,v,w,M,b);var T=[],S=[],C=[],E="string"==typeof e.xbins.size,L="string"==typeof e.ybins.size,z=[],P=[],D=E?z:e.xbins,O=L?P:e.ybins,I=0,R=[],B=[],F=e.histnorm,N=e.histfunc,j=-1!==F.indexOf("density"),V="max"===N||"min"===N?null:0,U=a.count,q=o[F],H=!1,G=[],W=[],Y="z"in e?e.z:"marker"in e&&Array.isArray(e.marker.color)?e.marker.color:"";Y&&"count"!==N&&(H="avg"===N,U=a[N]);var X=e.xbins,Z=_(X.start),J=_(X.end)+(Z-i.tickIncrement(Z,X.size,!1,x))/1e6;for(r=Z;r=0&&c=0&&d0)c=a(t.alphahull,u);else{var p=["x","y","z"].indexOf(t.delaunayaxis);c=i(u.map(function(t){return[t[(p+1)%3],t[(p+2)%3]]}))}var d={positions:u,cells:c,lightPosition:[t.lightposition.x,t.lightposition.y,t.lightposition.z],ambient:t.lighting.ambient,diffuse:t.lighting.diffuse,specular:t.lighting.specular,roughness:t.lighting.roughness,fresnel:t.lighting.fresnel,vertexNormalsEpsilon:t.lighting.vertexnormalsepsilon,faceNormalsEpsilon:t.lighting.facenormalsepsilon,opacity:t.opacity,contourEnable:t.contour.show,contourColor:l(t.contour.color).slice(0,3),contourWidth:t.contour.width,useFacetNormals:t.flatshading};t.intensity?(this.color="#fff",d.vertexIntensity=t.intensity,d.vertexIntensityBounds=[t.cmin,t.cmax],d.colormap=s(t.colorscale)):t.vertexcolor?(this.color=t.vertexcolor[0],d.vertexColors=f(t.vertexcolor)):t.facecolor?(this.color=t.facecolor[0],d.cellColors=f(t.facecolor)):(this.color=t.color,d.meshColor=l(t.color)),this.mesh.update(d)},u.dispose=function(){this.scene.glplot.remove(this.mesh),this.mesh.dispose()},e.exports=function(t,e){var r=t.glplot.gl,i=n({gl:r}),a=new c(t,i,e.uid);return i._trace=a,a.update(e),t.glplot.add(i),a}},{"../../lib/gl_format_color":656,"../../lib/str2rgbarray":683,"alpha-shape":49,"convex-hull":111,"delaunay-triangulate":133,"gl-mesh3d":249}],943:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("../../lib"),a=t("../../components/colorscale/defaults"),o=t("./attributes");e.exports=function(t,e,r,s){function l(r,n){return i.coerce(t,e,o,r,n)}function c(t){var e=t.map(function(t){var e=l(t);return e&&i.isArrayOrTypedArray(e)?e:null});return e.every(function(t){return t&&t.length===e[0].length})&&e}var u=c(["x","y","z"]),f=c(["i","j","k"]);u?(f&&f.forEach(function(t){for(var e=0;ed):p=_>y,d=_;var w=s(y,x,b,_);w.pos=v,w.yc=(y+_)/2,w.i=m,w.dir=p?"increasing":"decreasing",h&&(w.tx=e.text[m]),g.push(w)}}return a.expand(n,u.concat(c),{padded:!0}),g.length&&(g[0].t={labels:{open:i(t,"open:")+" ",high:i(t,"high:")+" ",low:i(t,"low:")+" ",close:i(t,"close:")+" "}}),g}e.exports={calc:function(t,e){var r=a.getFromId(t,e.xaxis),i=a.getFromId(t,e.yaxis),o=function(t,e,r){var i=r._minDiff;if(!i){var a,o=t._fullData,s=[];for(i=1/0,a=0;a"),t.y0=t.y1=f.c2p(C.yc,!0),[t]}},{"../../components/color":532,"../../components/fx":574,"../../plots/cartesian/axes":706,"../scatter/fill_hover_text":998}],949:[function(t,e,r){"use strict";e.exports={moduleType:"trace",name:"ohlc",basePlotModule:t("../../plots/cartesian"),categories:["cartesian","svg","showLegend"],meta:{},attributes:t("./attributes"),supplyDefaults:t("./defaults"),calc:t("./calc").calc,plot:t("./plot"),style:t("./style"),hoverPoints:t("./hover"),selectPoints:t("./select")}},{"../../plots/cartesian":717,"./attributes":945,"./calc":946,"./defaults":947,"./hover":948,"./plot":951,"./select":952,"./style":953}],950:[function(t,e,r){"use strict";var n=t("../../registry");e.exports=function(t,e,r,i){var a=r("x"),o=r("open"),s=r("high"),l=r("low"),c=r("close");if(n.getComponentMethod("calendars","handleTraceDefaults")(t,e,["x"],i),o&&s&&l&&c){var u=Math.min(o.length,s.length,l.length,c.length);return a&&(u=Math.min(u,a.length)),e._length=u,u}}},{"../../registry":790}],951:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../lib");e.exports=function(t,e,r,a){var o=e.xaxis,s=e.yaxis,l=a.selectAll("g.trace").data(r,function(t){return t[0].trace.uid});l.enter().append("g").attr("class","trace ohlc"),l.exit().remove(),l.order(),l.each(function(t){var r=t[0],a=r.t,l=r.trace,c=n.select(this);if(e.isRangePlot||(r.node3=c),!0!==l.visible||a.empty)c.remove();else{var u=a.tickLen,f=c.selectAll("path").data(i.identity);f.enter().append("path"),f.exit().remove(),f.attr("d",function(t){var e=o.c2p(t.pos,!0),r=o.c2p(t.pos-u,!0),n=o.c2p(t.pos+u,!0);return"M"+r+","+s.c2p(t.o,!0)+"H"+e+"M"+e+","+s.c2p(t.h,!0)+"V"+s.c2p(t.l,!0)+"M"+n+","+s.c2p(t.c,!0)+"H"+e})}})}},{"../../lib":660,d3:131}],952:[function(t,e,r){"use strict";e.exports=function(t,e){var r,n=t.cd,i=t.xaxis,a=t.yaxis,o=[],s=n[0].t.bPos||0;if(!1===e)for(r=0;r=0;a--){var o=t[a];if(e>f(n,o))return c(n,i);if(e>o||a===t.length-1)return c(o,n);i=n,n=o}}function d(t,e){for(var r=0;r=e[r][0]&&t<=e[r][1])return!0;return!1}function g(t){t.attr("x",-n.bar.captureWidth/2).attr("width",n.bar.captureWidth)}function m(t){t.attr("visibility","visible").style("visibility","visible").attr("fill","yellow").attr("opacity",0)}function v(t){if(!t.brush.filterSpecified)return"0,"+t.height;for(var e,r,n,i=y(t.brush.filter.getConsolidated(),t.height),a=[0],o=i.length?i[0][0]:null,s=0;se){h=r;break}}if(a=u,isNaN(a)&&(a=isNaN(f)||isNaN(h)?isNaN(f)?h:f:e-c[f][1]t[1]+r||e=.9*t[1]+.1*t[0]?"n":e<=.9*t[0]+.1*t[1]?"s":"ns"}(d,e);g&&(o.interval=l[a],o.intervalPix=d,o.region=g)}}if(t.ordinal&&!o.region){var m=t.unitTickvals,v=t.unitToPaddedPx.invert(e);for(r=0;r=x[0]&&v<=x[1]){o.clickableOrdinalRange=x;break}}}return o}function k(t){t.on("mousemove",function(t){if(i.event.preventDefault(),!t.parent.inBrushDrag){var e=w(t,t.height-i.mouse(this)[1]-2*n.verticalPadding),r="crosshair";e.clickableOrdinalRange?r="pointer":e.region&&(r=e.region+"-resize"),i.select(document.body).style("cursor",r)}}).on("mouseleave",function(t){t.parent.inBrushDrag||x()}).call(i.behavior.drag().on("dragstart",function(t){i.event.sourceEvent.stopPropagation();var e=t.height-i.mouse(this)[1]-2*n.verticalPadding,r=t.unitToPaddedPx.invert(e),a=t.brush,o=w(t,e),s=o.interval,l=a.svgBrush;if(l.wasDragged=!1,l.grabbingBar="ns"===o.region,l.grabbingBar){var c=s.map(t.unitToPaddedPx);l.grabPoint=e-c[0]-n.verticalPadding,l.barLength=c[1]-c[0]}l.clickableOrdinalRange=o.clickableOrdinalRange,l.stayingIntervals=t.multiselect&&a.filterSpecified?a.filter.getConsolidated():[],s&&(l.stayingIntervals=l.stayingIntervals.filter(function(t){return t[0]!==s[0]&&t[1]!==s[1]})),l.startExtent=o.region?s["s"===o.region?1:0]:r,t.parent.inBrushDrag=!0,l.brushStartCallback()}).on("drag",function(t){i.event.sourceEvent.stopPropagation();var e=t.height-i.mouse(this)[1]-2*n.verticalPadding,r=t.brush.svgBrush;r.wasDragged=!0,r.grabbingBar?r.newExtent=[e-r.grabPoint,e+r.barLength-r.grabPoint].map(t.unitToPaddedPx.invert):r.newExtent=[r.startExtent,t.unitToPaddedPx.invert(e)].sort(s);var a=Math.max(0,-r.newExtent[0]),o=Math.max(0,r.newExtent[1]-1);r.newExtent[0]+=a,r.newExtent[1]-=o,r.grabbingBar&&(r.newExtent[1]+=a,r.newExtent[0]-=o),t.brush.filterSpecified=!0,r.extent=r.stayingIntervals.concat([r.newExtent]),r.brushCallback(t),_(this.parentNode)}).on("dragend",function(t){i.event.sourceEvent.stopPropagation();var e=t.brush,r=e.filter,n=e.svgBrush,a=n.grabbingBar;if(n.grabbingBar=!1,n.grabLocation=void 0,t.parent.inBrushDrag=!1,x(),!n.wasDragged)return n.wasDragged=void 0,n.clickableOrdinalRange?e.filterSpecified&&t.multiselect?n.extent.push(n.clickableOrdinalRange):(n.extent=[n.clickableOrdinalRange],e.filterSpecified=!0):a?(n.extent=n.stayingIntervals,0===n.extent.length&&A(e)):A(e),n.brushCallback(t),_(this.parentNode),void n.brushEndCallback(e.filterSpecified?r.getConsolidated():[]);var o=function(){r.set(r.getConsolidated())};if(t.ordinal){var s=t.unitTickvals;s[s.length-1]n.newExtent[0];n.extent=n.stayingIntervals.concat(l?[n.newExtent]:[]),n.extent.length||A(e),n.brushCallback(t),l?_(this.parentNode,o):(o(),_(this.parentNode))}else o();n.brushEndCallback(e.filterSpecified?r.getConsolidated():[])}))}function M(t,e){return t[0]-e[0]}function A(t){t.filterSpecified=!1,t.svgBrush.extent=[[0,1]]}function T(t){for(var e,r=t.slice(),n=[],i=r.shift();i;){for(e=i.slice();(i=r.shift())&&i[0]<=e[1];)e[1]=Math.max(e[1],i[1]);n.push(e)}return n}e.exports={makeBrush:function(t,e,r,n,i,a){var o,l=function(){var t,e,r=[];return{set:function(n){r=n.map(function(t){return t.slice().sort(s)}).sort(M),t=T(r),e=r.reduce(function(t,e){return[Math.min(t[0],e[0]),Math.max(t[1],e[1])]},[1/0,-1/0])},get:function(){return r.slice()},getConsolidated:function(){return t},getBounds:function(){return e}}}();return l.set(r),{filter:l,filterSpecified:e,svgBrush:{extent:[],brushStartCallback:n,brushCallback:(o=i,function(t){var e=t.brush,r=function(t){return t.svgBrush.extent.map(function(t){return t.slice()})}(e).slice();e.filter.set(r),o()}),brushEndCallback:a}}},ensureAxisBrush:function(t){var e=t.selectAll("."+n.cn.axisBrush).data(o,a);e.enter().append("g").classed(n.cn.axisBrush,!0),function(t){var e=t.selectAll(".background").data(o);e.enter().append("rect").classed("background",!0).call(g).call(m).style("pointer-events","auto").attr("transform","translate(0 "+n.verticalPadding+")"),e.call(k).attr("height",function(t){return t.height-n.verticalPadding});var r=t.selectAll(".highlight-shadow").data(o);r.enter().append("line").classed("highlight-shadow",!0).attr("x",-n.bar.width/2).attr("stroke-width",n.bar.width+n.bar.strokeWidth).attr("stroke",n.bar.strokeColor).attr("opacity",n.bar.strokeOpacity).attr("stroke-linecap","butt"),r.attr("y1",function(t){return t.height}).call(b);var i=t.selectAll(".highlight").data(o);i.enter().append("line").classed("highlight",!0).attr("x",-n.bar.width/2).attr("stroke-width",n.bar.width-n.bar.strokeWidth).attr("stroke",n.bar.fillColor).attr("opacity",n.bar.fillOpacity).attr("stroke-linecap","butt"),i.attr("y1",function(t){return t.height}).call(b)}(e)},cleanRanges:function(t,e){if(Array.isArray(t[0])?(t=t.map(function(t){return t.sort(s)}),t=e.multiselect?T(t.sort(M)):[t[0]]):t=[t.sort(s)],e.tickvals){var r=e.tickvals.slice().sort(s);if(!(t=t.map(function(t){var e=[h(r,t[0],[]),p(r,t[1],[])];if(e[1]>e[0])return e}).filter(function(t){return t})).length)return}return t.length>1?t:t[0]}}},{"../../lib":660,"../../lib/gup":657,"./constants":959,d3:131}],956:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../plots/get_data").getModuleCalcData,a=t("./plot"),o=t("../../constants/xmlns_namespaces");r.name="parcoords",r.plot=function(t){var e=i(t.calcdata,"parcoords")[0];e.length&&a(t,e)},r.clean=function(t,e,r,n){var i=n._has&&n._has("parcoords"),a=e._has&&e._has("parcoords");i&&!a&&(n._paperdiv.selectAll(".parcoords").remove(),n._glimages.selectAll("*").remove())},r.toSVG=function(t){var e=t._fullLayout._glimages,r=n.select(t).selectAll(".svg-container");r.filter(function(t,e){return e===r.size()-1}).selectAll(".gl-canvas-context, .gl-canvas-focus").each(function(){var t=this.toDataURL("image/png");e.append("svg:image").attr({xmlns:o.svg,"xlink:href":t,preserveAspectRatio:"none",x:0,y:0,width:this.width,height:this.height})}),window.setTimeout(function(){n.selectAll("#filterBarPattern").attr("id","filterBarPattern")},60)}},{"../../constants/xmlns_namespaces":639,"../../plots/get_data":742,"./plot":964,d3:131}],957:[function(t,e,r){"use strict";var n=t("../../components/colorscale/has_colorscale"),i=t("../../components/colorscale/calc"),a=t("../../lib"),o=t("../../lib/gup").wrap;e.exports=function(t,e){var r=!!e.line.colorscale&&a.isArrayOrTypedArray(e.line.color),s=r?e.line.color:function(t){for(var e=new Array(t),r=0;rs&&(n.log("parcoords traces support up to "+s+" dimensions at the moment"),l.splice(s)),o=0;o>>8*e)%256/255}function M(t,e,r){var n,i,a,o=[];for(i=0;i=h-4?k(o,h-2-s):.5);return a}(d,p,i);!function(t,e,r){for(var n=0;n<16;n++)t["p"+n.toString(16)](M(e,r,n))}(E,d,o),L=S.texture(l.extendFlat({data:function(t,e,r){for(var n=[],i=0;i<256;i++){var a=t(i/255);n.push((e?v:a).concat(r))}return n}(r.unitToColor,A,Math.round(255*(A?a:1)))},b))}var D=[0,1];var O=[];function I(t,e,n,i,a,o,s,c,u,f,h){var p,d,g,m,v=[t,e],y=[0,1].map(function(){return[0,1,2,3].map(function(){return new Float32Array(16)})});for(p=0;p<2;p++)for(m=v[p],d=0;d<4;d++)for(g=0;g<16;g++)y[p][d][g]=g+16*d===m?1:0;var x=r.lines.canvasOverdrag,b=r.domain,_=r.canvasWidth,w=r.canvasHeight;return l.extendFlat({key:s,resolution:[_,w],viewBoxPosition:[n+x,i],viewBoxSize:[a,o],i:t,ii:e,dim1A:y[0][0],dim1B:y[0][1],dim1C:y[0][2],dim1D:y[0][3],dim2A:y[1][0],dim2B:y[1][1],dim2C:y[1][2],dim2D:y[1][3],colorClamp:D,scissorX:(c===u?0:n+x)+(r.pad.l-x)+r.layoutWidth*b.x[0],scissorWidth:(c===f?_-n+x:a+.5)+(c===u?n+x:0),scissorY:i+r.pad.b+r.layoutHeight*b.y[0],scissorHeight:o,viewportX:r.pad.l-x+r.layoutWidth*b.x[0],viewportY:r.pad.b+r.layoutHeight*b.y[0],viewportWidth:_,viewportHeight:w},h)}return{setColorDomain:function(t){D[0]=t[0],D[1]=t[1]},render:function(t,e,n){var i,a,o,s=t.length,l=1/0,c=-1/0;for(i=0;ic&&(c=t[i].dim2.canvasX,o=i),t[i].dim1.canvasXn._length&&(M=M.slice(0,n._length));var A,T=n.tickvals;function S(t,e){return{val:t,text:A[e]}}function C(t,e){return t.val-e.val}if(Array.isArray(T)&&T.length){A=n.ticktext,Array.isArray(A)&&A.length?A.length>T.length?A=A.slice(0,T.length):T.length>A.length&&(T=T.slice(0,A.length)):A=T.map(o.format(n.tickformat));for(var E=1;E=r||s>=n)return;var l=t.lineLayer.readPixel(a,n-1-s),c=0!==l[3],u=c?l[2]+256*(l[1]+256*l[0]):null,f={x:a,y:s,clientX:e.clientX,clientY:e.clientY,dataIndex:t.model.key,curveNumber:u};u!==M&&(c?d.hover(f):d.unhover&&d.unhover(f),M=u)}}),k.style("opacity",function(t){return t.pick?.01:1}),e.style("background","rgba(255, 255, 255, 0)");var A=e.selectAll("."+i.cn.parcoords).data(w,c);A.exit().remove(),A.enter().append("g").classed(i.cn.parcoords,!0).style("shape-rendering","crispEdges").style("pointer-events","none"),A.attr("transform",function(t){return"translate("+t.model.translateX+","+t.model.translateY+")"});var T=A.selectAll("."+i.cn.parcoordsControlView).data(u,c);T.enter().append("g").classed(i.cn.parcoordsControlView,!0),T.attr("transform",function(t){return"translate("+t.model.pad.l+","+t.model.pad.t+")"});var S=T.selectAll("."+i.cn.yAxis).data(function(t){return t.dimensions},c);function C(t,e){for(var r=e.panels||(e.panels=[]),n=t.data(),i=n.length-1,a=0;aline").attr("fill","none").attr("stroke","black").attr("stroke-opacity",.25).attr("stroke-width","1px"),L.selectAll("text").style("text-shadow","1px 1px 1px #fff, -1px -1px 1px #fff, 1px -1px 1px #fff, -1px 1px 1px #fff").style("cursor","default").style("user-select","none");var z=E.selectAll("."+i.cn.axisHeading).data(u,c);z.enter().append("g").classed(i.cn.axisHeading,!0);var P=z.selectAll("."+i.cn.axisTitle).data(u,c);P.enter().append("text").classed(i.cn.axisTitle,!0).attr("text-anchor","middle").style("cursor","ew-resize").style("user-select","none").style("pointer-events","auto"),P.attr("transform","translate(0,"+-i.axisTitleOffset+")").text(function(t){return t.label}).each(function(t){s.font(o.select(this),t.model.labelFont)});var D=E.selectAll("."+i.cn.axisExtent).data(u,c);D.enter().append("g").classed(i.cn.axisExtent,!0);var O=D.selectAll("."+i.cn.axisExtentTop).data(u,c);O.enter().append("g").classed(i.cn.axisExtentTop,!0),O.attr("transform","translate(0,"+-i.axisExtentOffset+")");var I=O.selectAll("."+i.cn.axisExtentTopText).data(u,c);function R(t,e){if(t.ordinal)return"";var r=t.domainScale.domain();return o.format(t.tickFormat)(r[e?r.length-1:0])}I.enter().append("text").classed(i.cn.axisExtentTopText,!0).call(y),I.text(function(t){return R(t,!0)}).each(function(t){s.font(o.select(this),t.model.rangeFont)});var B=D.selectAll("."+i.cn.axisExtentBottom).data(u,c);B.enter().append("g").classed(i.cn.axisExtentBottom,!0),B.attr("transform",function(t){return"translate(0,"+(t.model.height+i.axisExtentOffset)+")"});var F=B.selectAll("."+i.cn.axisExtentBottomText).data(u,c);F.enter().append("text").classed(i.cn.axisExtentBottomText,!0).attr("dy","0.75em").call(y),F.text(function(t){return R(t)}).each(function(t){s.font(o.select(this),t.model.rangeFont)}),h.ensureAxisBrush(E)}},{"../../components/drawing":557,"../../lib":660,"../../lib/gup":657,"./axisbrush":955,"./constants":959,"./lines":962,d3:131}],964:[function(t,e,r){"use strict";var n=t("./parcoords"),i=t("../../lib/prepare_regl");e.exports=function(t,e){var r=t._fullLayout,a=r._toppaper,o=r._paperdiv,s=r._glcontainer;i(t);var l={},c={},u=r._size;e.forEach(function(e,r){l[r]=t.data[r].dimensions,c[r]=t.data[r].dimensions.slice()});n(o,a,s,e,{width:u.w,height:u.h,margin:{t:u.t,r:u.r,b:u.b,l:u.l}},{filterChanged:function(e,r,n){var i=c[e][r],a=n.map(function(t){return t.slice()});a.length?(1===a.length&&(a=a[0]),i.constraintrange=a,a=[a]):(delete i.constraintrange,a=null);var o={};o["dimensions["+r+"].constraintrange"]=a,t.emit("plotly_restyle",[o,[e]])},hover:function(e){t.emit("plotly_hover",e)},unhover:function(e){t.emit("plotly_unhover",e)},axesMoved:function(e,r){function n(t){return!("visible"in t)||t.visible}function i(t,e,r){var n=e.indexOf(r),i=t.indexOf(n);return-1===i&&(i+=e.length),i}var a=function(t){return function(e,n){return i(r,t,e)-i(r,t,n)}}(c[e].filter(n));l[e].sort(a),c[e].filter(function(t){return!n(t)}).sort(function(t){return c[e].indexOf(t)}).forEach(function(t){l[e].splice(l[e].indexOf(t),1),l[e].splice(c[e].indexOf(t),0,t)}),t.emit("plotly_restyle")}})}},{"../../lib/prepare_regl":673,"./parcoords":963}],965:[function(t,e,r){"use strict";var n=t("../../components/color/attributes"),i=t("../../plots/font_attributes"),a=t("../../plots/attributes"),o=t("../../plots/domain").attributes,s=t("../../lib/extend").extendFlat,l=i({editType:"calc",colorEditType:"style"});e.exports={labels:{valType:"data_array",editType:"calc"},label0:{valType:"number",dflt:0,editType:"calc"},dlabel:{valType:"number",dflt:1,editType:"calc"},values:{valType:"data_array",editType:"calc"},marker:{colors:{valType:"data_array",editType:"calc"},line:{color:{valType:"color",dflt:n.defaultLine,arrayOk:!0,editType:"style"},width:{valType:"number",min:0,dflt:0,arrayOk:!0,editType:"style"},editType:"calc"},editType:"calc"},text:{valType:"data_array",editType:"calc"},hovertext:{valType:"string",dflt:"",arrayOk:!0,editType:"style"},scalegroup:{valType:"string",dflt:"",editType:"calc"},textinfo:{valType:"flaglist",flags:["label","text","value","percent"],extras:["none"],editType:"calc"},hoverinfo:s({},a.hoverinfo,{flags:["label","text","value","percent","name"]}),textposition:{valType:"enumerated",values:["inside","outside","auto","none"],dflt:"auto",arrayOk:!0,editType:"calc"},textfont:s({},l,{}),insidetextfont:s({},l,{}),outsidetextfont:s({},l,{}),domain:o({name:"pie",trace:!0,editType:"calc"}),hole:{valType:"number",min:0,max:1,dflt:0,editType:"calc"},sort:{valType:"boolean",dflt:!0,editType:"calc"},direction:{valType:"enumerated",values:["clockwise","counterclockwise"],dflt:"counterclockwise",editType:"calc"},rotation:{valType:"number",min:-360,max:360,dflt:0,editType:"calc"},pull:{valType:"number",min:0,max:1,dflt:0,arrayOk:!0,editType:"calc"}}},{"../../components/color/attributes":531,"../../lib/extend":649,"../../plots/attributes":703,"../../plots/domain":731,"../../plots/font_attributes":732}],966:[function(t,e,r){"use strict";var n=t("../../registry"),i=t("../../plots/get_data").getModuleCalcData;r.name="pie",r.plot=function(t){var e=n.getModule("pie"),r=i(t.calcdata,e)[0];r.length&&e.plot(t,r)},r.clean=function(t,e,r,n){var i=n._has&&n._has("pie"),a=e._has&&e._has("pie");i&&!a&&n._pielayer.selectAll("g.trace").remove()}},{"../../plots/get_data":742,"../../registry":790}],967:[function(t,e,r){"use strict";var n,i=t("fast-isnumeric"),a=t("../../lib").isArrayOrTypedArray,o=t("tinycolor2"),s=t("../../components/color"),l=t("./helpers");function c(t,e){if(!n){var r=s.defaults;n=u(r)}var i=e||n;return i[t%i.length]}function u(t){var e,r=t.slice();for(e=0;e")}}return y}},{"../../components/color":532,"../../lib":660,"./helpers":970,"fast-isnumeric":197,tinycolor2:473}],968:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("./attributes"),a=t("../../plots/domain").defaults;e.exports=function(t,e,r,o){function s(r,a){return n.coerce(t,e,i,r,a)}var l,c=n.coerceFont,u=s("values"),f=n.isArrayOrTypedArray(u),h=s("labels");if(Array.isArray(h)&&(l=h.length,f&&(l=Math.min(l,u.length))),!Array.isArray(h)){if(!f)return void(e.visible=!1);l=u.length,s("label0"),s("dlabel")}if(l){e._length=l,s("marker.line.width")&&s("marker.line.color"),s("marker.colors"),s("scalegroup");var p=s("text"),d=s("textinfo",Array.isArray(p)?"text+percent":"percent");if(s("hovertext"),d&&"none"!==d){var g=s("textposition"),m=Array.isArray(g)||"auto"===g,v=m||"inside"===g,y=m||"outside"===g;if(v||y){var x=c(s,"textfont",o.font);v&&c(s,"insidetextfont",x),y&&c(s,"outsidetextfont",x)}}a(e,o,s),s("hole"),s("sort"),s("direction"),s("rotation"),s("pull")}else e.visible=!1}},{"../../lib":660,"../../plots/domain":731,"./attributes":965}],969:[function(t,e,r){"use strict";var n=t("../../components/fx/helpers").appendArrayMultiPointValues;e.exports=function(t,e){var r={curveNumber:e.index,pointNumbers:t.pts,data:e._input,fullData:e,label:t.label,color:t.color,value:t.v,v:t.v};return 1===t.pts.length&&(r.pointNumber=r.i=t.pts[0]),n(r,e,t.pts),r}},{"../../components/fx/helpers":571}],970:[function(t,e,r){"use strict";var n=t("../../lib");r.formatPiePercent=function(t,e){var r=(100*t).toPrecision(3);return-1!==r.lastIndexOf(".")&&(r=r.replace(/[.]?0+$/,"")),n.numSeparate(r,e)+"%"},r.formatPieValue=function(t,e){var r=t.toPrecision(10);return-1!==r.lastIndexOf(".")&&(r=r.replace(/[.]?0+$/,"")),n.numSeparate(r,e)},r.getFirstFilled=function(t,e){if(Array.isArray(t))for(var r=0;r0?1:-1)/2,y:a/(1+r*r/(n*n)),outside:!0}}e.exports=function(t,e){var r=t._fullLayout;!function(t,e){var r,n,i,a,o,s,l,c,u,f=[];for(i=0;il&&(l=s.pull[a]);o.r=Math.min(r,n)/(2+2*l),o.cx=e.l+e.w*(s.domain.x[1]+s.domain.x[0])/2,o.cy=e.t+e.h*(2-s.domain.y[1]-s.domain.y[0])/2,s.scalegroup&&-1===f.indexOf(s.scalegroup)&&f.push(s.scalegroup)}for(a=0;ai.vTotal/2?1:0)}(e),p.each(function(){var p=n.select(this).selectAll("g.slice").data(e);p.enter().append("g").classed("slice",!0),p.exit().remove();var m=[[[],[]],[[],[]]],v=!1;p.each(function(e){if(e.hidden)n.select(this).selectAll("path,g").remove();else{e.pointNumber=e.i,e.curveNumber=g.index,m[e.pxmid[1]<0?0:1][e.pxmid[0]<0?0:1].push(e);var a=d.cx,p=d.cy,y=n.select(this),x=y.selectAll("path.surface").data([e]),b=!1,_=!1;if(x.enter().append("path").classed("surface",!0).style({"pointer-events":"all"}),y.select("path.textline").remove(),y.on("mouseover",function(){var o=t._fullLayout,s=t._fullData[g.index];if(!t._dragging&&!1!==o.hovermode){var l=s.hoverinfo;if(Array.isArray(l)&&(l=i.castHoverinfo({hoverinfo:[c.castOption(l,e.pts)],_module:g._module},o,0)),"all"===l&&(l="label+text+value+percent+name"),"none"!==l&&"skip"!==l&&l){var h=f(e,d),m=a+e.pxmid[0]*(1-h),v=p+e.pxmid[1]*(1-h),y=r.separators,x=[];if(-1!==l.indexOf("label")&&x.push(e.label),-1!==l.indexOf("text")){var w=c.castOption(s.hovertext||s.text,e.pts);w&&x.push(w)}-1!==l.indexOf("value")&&x.push(c.formatPieValue(e.v,y)),-1!==l.indexOf("percent")&&x.push(c.formatPiePercent(e.v/d.vTotal,y));var k=g.hoverlabel,M=k.font;i.loneHover({x0:m-h*d.r,x1:m+h*d.r,y:v,text:x.join("
"),name:-1!==l.indexOf("name")?s.name:void 0,idealAlign:e.pxmid[0]<0?"left":"right",color:c.castOption(k.bgcolor,e.pts)||e.color,borderColor:c.castOption(k.bordercolor,e.pts),fontFamily:c.castOption(M.family,e.pts),fontSize:c.castOption(M.size,e.pts),fontColor:c.castOption(M.color,e.pts)},{container:o._hoverlayer.node(),outerContainer:o._paper.node(),gd:t}),b=!0}t.emit("plotly_hover",{points:[u(e,s)],event:n.event}),_=!0}}).on("mouseout",function(r){var a=t._fullLayout,o=t._fullData[g.index];_&&(r.originalEvent=n.event,t.emit("plotly_unhover",{points:[u(e,o)],event:n.event}),_=!1),b&&(i.loneUnhover(a._hoverlayer.node()),b=!1)}).on("click",function(){var r=t._fullLayout,a=t._fullData[g.index];t._dragging||!1===r.hovermode||(t._hoverdata=[u(e,a)],i.click(t,n.event))}),g.pull){var w=+c.castOption(g.pull,e.pts)||0;w>0&&(a+=w*e.pxmid[0],p+=w*e.pxmid[1])}e.cxFinal=a,e.cyFinal=p;var k=g.hole;if(e.v===d.vTotal){var M="M"+(a+e.px0[0])+","+(p+e.px0[1])+E(e.px0,e.pxmid,!0,1)+E(e.pxmid,e.px0,!0,1)+"Z";k?x.attr("d","M"+(a+k*e.px0[0])+","+(p+k*e.px0[1])+E(e.px0,e.pxmid,!1,k)+E(e.pxmid,e.px0,!1,k)+"Z"+M):x.attr("d",M)}else{var A=E(e.px0,e.px1,!0,1);if(k){var T=1-k;x.attr("d","M"+(a+k*e.px1[0])+","+(p+k*e.px1[1])+E(e.px1,e.px0,!1,k)+"l"+T*e.px0[0]+","+T*e.px0[1]+A+"Z")}else x.attr("d","M"+a+","+p+"l"+e.px0[0]+","+e.px0[1]+A+"Z")}var S=c.castOption(g.textposition,e.pts),C=y.selectAll("g.slicetext").data(e.text&&"none"!==S?[0]:[]);C.enter().append("g").classed("slicetext",!0),C.exit().remove(),C.each(function(){var r=s.ensureSingle(n.select(this),"text","",function(t){t.attr("data-notex",1)});r.text(e.text).attr({class:"slicetext",transform:"","text-anchor":"middle"}).call(o.font,"outside"===S?g.outsidetextfont:g.insidetextfont).call(l.convertToTspans,t);var i,c=o.bBox(r.node());"outside"===S?i=h(c,e):(i=function(t,e,r){var n=Math.sqrt(t.width*t.width+t.height*t.height),i=t.width/t.height,a=Math.PI*Math.min(e.v/r.vTotal,.5),o=1-r.trace.hole,s=f(e,r),l={scale:s*r.r*2/n,rCenter:1-s,rotate:0};if(l.scale>=1)return l;var c=i+1/(2*Math.tan(a)),u=r.r*Math.min(1/(Math.sqrt(c*c+.5)+c),o/(Math.sqrt(i*i+o/2)+i)),h={scale:2*u/t.height,rCenter:Math.cos(u/r.r)-u*i/r.r,rotate:(180/Math.PI*e.midangle+720)%180-90},p=1/i,d=p+1/(2*Math.tan(a)),g=r.r*Math.min(1/(Math.sqrt(d*d+.5)+d),o/(Math.sqrt(p*p+o/2)+p)),m={scale:2*g/t.width,rCenter:Math.cos(g/r.r)-g/i/r.r,rotate:(180/Math.PI*e.midangle+810)%180-90},v=m.scale>h.scale?m:h;return l.scale<1&&v.scale>l.scale?v:l}(c,e,d),"auto"===S&&i.scale<1&&(r.call(o.font,g.outsidetextfont),g.outsidetextfont.family===g.insidetextfont.family&&g.outsidetextfont.size===g.insidetextfont.size||(c=o.bBox(r.node())),i=h(c,e)));var u=a+e.pxmid[0]*i.rCenter+(i.x||0),m=p+e.pxmid[1]*i.rCenter+(i.y||0);i.outside&&(e.yLabelMin=m-c.height/2,e.yLabelMid=m,e.yLabelMax=m+c.height/2,e.labelExtraX=0,e.labelExtraY=0,v=!0),r.attr("transform","translate("+u+","+m+")"+(i.scale<1?"scale("+i.scale+")":"")+(i.rotate?"rotate("+i.rotate+")":"")+"translate("+-(c.left+c.right)/2+","+-(c.top+c.bottom)/2+")")})}function E(t,r,n,i){return"a"+i*d.r+","+i*d.r+" 0 "+e.largeArc+(n?" 1 ":" 0 ")+i*(r[0]-t[0])+","+i*(r[1]-t[1])}}),v&&function(t,e){var r,n,i,a,o,s,l,u,f,h,p,d,g;function m(t,e){return t.pxmid[1]-e.pxmid[1]}function v(t,e){return e.pxmid[1]-t.pxmid[1]}function y(t,r){r||(r={});var i,u,f,p,d,g,m=r.labelExtraY+(n?r.yLabelMax:r.yLabelMin),v=n?t.yLabelMin:t.yLabelMax,y=n?t.yLabelMax:t.yLabelMin,x=t.cyFinal+o(t.px0[1],t.px1[1]),b=m-v;if(b*l>0&&(t.labelExtraY=b),Array.isArray(e.pull))for(u=0;u=(c.castOption(e.pull,f.pts)||0)||((t.pxmid[1]-f.pxmid[1])*l>0?(p=f.cyFinal+o(f.px0[1],f.px1[1]),(b=p-v-t.labelExtraY)*l>0&&(t.labelExtraY+=b)):(y+t.labelExtraY-x)*l>0&&(i=3*s*Math.abs(u-h.indexOf(t)),d=f.cxFinal+a(f.px0[0],f.px1[0]),(g=d+i-(t.cxFinal+t.pxmid[0])-t.labelExtraX)*s>0&&(t.labelExtraX+=g)))}for(n=0;n<2;n++)for(i=n?m:v,o=n?Math.max:Math.min,l=n?1:-1,r=0;r<2;r++){for(a=r?Math.max:Math.min,s=r?1:-1,(u=t[n][r]).sort(i),f=t[1-n][r],h=f.concat(u),d=[],p=0;pMath.abs(c)?o+="l"+c*t.pxmid[0]/t.pxmid[1]+","+c+"H"+(i+t.labelExtraX+s):o+="l"+t.labelExtraX+","+l+"v"+(c-l)+"h"+s}else o+="V"+(t.yLabelMid+t.labelExtraY)+"h"+s;e.append("path").classed("textline",!0).call(a.stroke,g.outsidetextfont.color).attr({"stroke-width":Math.min(2,g.outsidetextfont.size/8),d:o,fill:"none"})}})})}),setTimeout(function(){p.selectAll("tspan").each(function(){var t=n.select(this);t.attr("dy")&&t.attr("dy",t.attr("dy"))})},0)}},{"../../components/color":532,"../../components/drawing":557,"../../components/fx":574,"../../lib":660,"../../lib/svg_text_utils":684,"./event_data":969,"./helpers":970,d3:131}],975:[function(t,e,r){"use strict";var n=t("d3"),i=t("./style_one");e.exports=function(t){t._fullLayout._pielayer.selectAll(".trace").each(function(t){var e=t[0].trace,r=n.select(this);r.style({opacity:e.opacity}),r.selectAll("path.surface").each(function(t){n.select(this).call(i,t,e)})})}},{"./style_one":976,d3:131}],976:[function(t,e,r){"use strict";var n=t("../../components/color"),i=t("./helpers").castOption;e.exports=function(t,e,r){var a=r.marker.line,o=i(a.color,e.pts)||n.defaultLine,s=i(a.width,e.pts)||0;t.style({"stroke-width":s}).call(n.fill,e.color).call(n.stroke,o)}},{"../../components/color":532,"./helpers":970}],977:[function(t,e,r){"use strict";var n=t("../scatter/attributes");e.exports={x:n.x,y:n.y,xy:{valType:"data_array",editType:"calc"},indices:{valType:"data_array",editType:"calc"},xbounds:{valType:"data_array",editType:"calc"},ybounds:{valType:"data_array",editType:"calc"},text:n.text,marker:{color:{valType:"color",arrayOk:!1,editType:"calc"},opacity:{valType:"number",min:0,max:1,dflt:1,arrayOk:!1,editType:"calc"},blend:{valType:"boolean",dflt:null,editType:"calc"},sizemin:{valType:"number",min:.1,max:2,dflt:.5,editType:"calc"},sizemax:{valType:"number",min:.1,dflt:20,editType:"calc"},border:{color:{valType:"color",arrayOk:!1,editType:"calc"},arearatio:{valType:"number",min:0,max:1,dflt:0,editType:"calc"},editType:"calc"},editType:"calc"}}},{"../scatter/attributes":990}],978:[function(t,e,r){"use strict";var n=t("gl-pointcloud2d"),i=t("../../lib/str2rgbarray"),a=t("../../plots/cartesian/autorange").expand,o=t("../scatter/get_trace_color");function s(t,e){this.scene=t,this.uid=e,this.type="pointcloud",this.pickXData=[],this.pickYData=[],this.xData=[],this.yData=[],this.textLabels=[],this.color="rgb(0, 0, 0)",this.name="",this.hoverinfo="all",this.idToIndex=new Int32Array(0),this.bounds=[0,0,0,0],this.pointcloudOptions={positions:new Float32Array(0),idToIndex:this.idToIndex,sizemin:.5,sizemax:12,color:[0,0,0,1],areaRatio:1,borderColor:[0,0,0,1]},this.pointcloud=n(t.glplot,this.pointcloudOptions),this.pointcloud._trace=this}var l=s.prototype;l.handlePick=function(t){var e=this.idToIndex[t.pointId];return{trace:this,dataCoord:t.dataCoord,traceCoord:this.pickXYData?[this.pickXYData[2*e],this.pickXYData[2*e+1]]:[this.pickXData[e],this.pickYData[e]],textLabel:Array.isArray(this.textLabels)?this.textLabels[e]:this.textLabels,color:this.color,name:this.name,pointIndex:e,hoverinfo:this.hoverinfo}},l.update=function(t){this.index=t.index,this.textLabels=t.text,this.name=t.name,this.hoverinfo=t.hoverinfo,this.bounds=[1/0,1/0,-1/0,-1/0],this.updateFast(t),this.color=o(t,{})},l.updateFast=function(t){var e,r,n,a,o,s,l=this.xData=this.pickXData=t.x,c=this.yData=this.pickYData=t.y,u=this.pickXYData=t.xy,f=t.xbounds&&t.ybounds,h=t.indices,p=this.bounds;if(u){if(n=u,e=u.length>>>1,f)p[0]=t.xbounds[0],p[2]=t.xbounds[1],p[1]=t.ybounds[0],p[3]=t.ybounds[1];else for(s=0;sp[2]&&(p[2]=a),op[3]&&(p[3]=o);if(h)r=h;else for(r=new Int32Array(e),s=0;sp[2]&&(p[2]=a),op[3]&&(p[3]=o);this.idToIndex=r,this.pointcloudOptions.idToIndex=r,this.pointcloudOptions.positions=n;var d=i(t.marker.color),g=i(t.marker.border.color),m=t.opacity*t.marker.opacity;d[3]*=m,this.pointcloudOptions.color=d;var v=t.marker.blend;if(null===v){v=l.length<100||c.length<100}this.pointcloudOptions.blend=v,g[3]*=m,this.pointcloudOptions.borderColor=g;var y=t.marker.sizemin,x=Math.max(t.marker.sizemax,t.marker.sizemin);this.pointcloudOptions.sizeMin=y,this.pointcloudOptions.sizeMax=x,this.pointcloudOptions.areaRatio=t.marker.border.arearatio,this.pointcloud.update(this.pointcloudOptions),this.expandAxesFast(p,x/2)},l.expandAxesFast=function(t,e){var r=e||.5;a(this.scene.xaxis,[t[0],t[2]],{ppad:r}),a(this.scene.yaxis,[t[1],t[3]],{ppad:r})},l.dispose=function(){this.pointcloud.dispose()},e.exports=function(t,e){var r=new s(t,e.uid);return r.update(e),r}},{"../../lib/str2rgbarray":683,"../../plots/cartesian/autorange":705,"../scatter/get_trace_color":1e3,"gl-pointcloud2d":260}],979:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("./attributes");e.exports=function(t,e,r){function a(r,a){return n.coerce(t,e,i,r,a)}a("x"),a("y"),a("xbounds"),a("ybounds"),t.xy&&t.xy instanceof Float32Array&&(e.xy=t.xy),t.indices&&t.indices instanceof Int32Array&&(e.indices=t.indices),a("text"),a("marker.color",r),a("marker.opacity"),a("marker.blend"),a("marker.sizemin"),a("marker.sizemax"),a("marker.border.color",r),a("marker.border.arearatio"),e._length=null}},{"../../lib":660,"./attributes":977}],980:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.calc=t("../scatter3d/calc"),n.plot=t("./convert"),n.moduleType="trace",n.name="pointcloud",n.basePlotModule=t("../../plots/gl2d"),n.categories=["gl","gl2d","showLegend"],n.meta={},e.exports=n},{"../../plots/gl2d":745,"../scatter3d/calc":1016,"./attributes":977,"./convert":978,"./defaults":979}],981:[function(t,e,r){"use strict";var n=t("../../plots/font_attributes"),i=t("../../plots/attributes"),a=t("../../components/color/attributes"),o=t("../../components/fx/attributes"),s=t("../../plots/domain").attributes,l=t("../../lib/extend").extendFlat,c=t("../../plot_api/edit_types").overrideAll;e.exports=c({hoverinfo:l({},i.hoverinfo,{flags:["label","text","value","percent","name"]}),hoverlabel:o.hoverlabel,domain:s({name:"sankey",trace:!0}),orientation:{valType:"enumerated",values:["v","h"],dflt:"h"},valueformat:{valType:"string",dflt:".3s"},valuesuffix:{valType:"string",dflt:""},arrangement:{valType:"enumerated",values:["snap","perpendicular","freeform","fixed"],dflt:"snap"},textfont:n({}),node:{label:{valType:"data_array",dflt:[]},color:{valType:"color",arrayOk:!0},line:{color:{valType:"color",dflt:a.defaultLine,arrayOk:!0},width:{valType:"number",min:0,dflt:.5,arrayOk:!0}},pad:{valType:"number",arrayOk:!1,min:0,dflt:20},thickness:{valType:"number",arrayOk:!1,min:1,dflt:20}},link:{label:{valType:"data_array",dflt:[]},color:{valType:"color",arrayOk:!0},line:{color:{valType:"color",dflt:a.defaultLine,arrayOk:!0},width:{valType:"number",min:0,dflt:0,arrayOk:!0}},source:{valType:"data_array",dflt:[]},target:{valType:"data_array",dflt:[]},value:{valType:"data_array",dflt:[]}}},"calc","nested")},{"../../components/color/attributes":531,"../../components/fx/attributes":566,"../../lib/extend":649,"../../plot_api/edit_types":691,"../../plots/attributes":703,"../../plots/domain":731,"../../plots/font_attributes":732}],982:[function(t,e,r){"use strict";var n=t("../../plot_api/edit_types").overrideAll,i=t("../../plots/get_data").getModuleCalcData,a=t("./plot"),o=t("../../components/fx/layout_attributes");r.name="sankey",r.baseLayoutAttrOverrides=n({hoverlabel:o.hoverlabel},"plot","nested"),r.plot=function(t){var e=i(t.calcdata,"sankey")[0];a(t,e)},r.clean=function(t,e,r,n){var i=n._has&&n._has("sankey"),a=e._has&&e._has("sankey");i&&!a&&n._paperdiv.selectAll(".sankey").remove()}},{"../../components/fx/layout_attributes":575,"../../plot_api/edit_types":691,"../../plots/get_data":742,"./plot":987}],983:[function(t,e,r){"use strict";var n=t("strongly-connected-components"),i=t("../../lib"),a=t("../../lib/gup").wrap;e.exports=function(t,e){return function(t,e,r){for(var a=t.length,o=i.init2dArray(a,0),s=0;s1})}(e.node.label,e.link.source,e.link.target)&&(i.error("Circularity is present in the Sankey data. Removing all nodes and links."),e.link.label=[],e.link.source=[],e.link.target=[],e.link.value=[],e.link.color=[],e.node.label=[],e.node.color=[]),a({link:e.link,node:e.node})}},{"../../lib":660,"../../lib/gup":657,"strongly-connected-components":465}],984:[function(t,e,r){"use strict";e.exports={nodeTextOffsetHorizontal:4,nodeTextOffsetVertical:3,nodePadAcross:10,sankeyIterations:50,forceIterations:5,forceTicksPerFrame:10,duration:500,ease:"cubic-in-out",cn:{sankey:"sankey",sankeyLinks:"sankey-links",sankeyLink:"sankey-link",sankeyNodeSet:"sankey-node-set",sankeyNode:"sankey-node",nodeRect:"node-rect",nodeCapture:"node-capture",nodeCentered:"node-entered",nodeLabelGuide:"node-label-guide",nodeLabel:"node-label",nodeLabelTextPath:"node-label-text-path"}}},{}],985:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("./attributes"),a=t("../../components/color"),o=t("tinycolor2"),s=t("../../plots/domain").defaults;e.exports=function(t,e,r,l){function c(r,a){return n.coerce(t,e,i,r,a)}c("node.label"),c("node.pad"),c("node.thickness"),c("node.line.color"),c("node.line.width");var u=l.colorway;c("node.color",e.node.label.map(function(t,e){return a.addOpacity(function(t){return u[t%u.length]}(e),.8)})),c("link.label"),c("link.source"),c("link.target"),c("link.value"),c("link.line.color"),c("link.line.width"),c("link.color",e.link.value.map(function(){return o(l.paper_bgcolor).getLuminance()<.333?"rgba(255, 255, 255, 0.6)":"rgba(0, 0, 0, 0.2)"})),s(e,l,c),c("orientation"),c("valueformat"),c("valuesuffix"),c("arrangement"),n.coerceFont(c,"textfont",n.extendFlat({},l.font)),e._length=null}},{"../../components/color":532,"../../lib":660,"../../plots/domain":731,"./attributes":981,tinycolor2:473}],986:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.calc=t("./calc"),n.plot=t("./plot"),n.moduleType="trace",n.name="sankey",n.basePlotModule=t("./base_plot"),n.categories=["noOpacity"],n.meta={},e.exports=n},{"./attributes":981,"./base_plot":982,"./calc":983,"./defaults":985,"./plot":987}],987:[function(t,e,r){"use strict";var n=t("d3"),i=t("./render"),a=t("../../components/fx"),o=t("../../components/color"),s=t("../../lib"),l=t("./constants").cn,c=s._;function u(t){return""!==t}function f(t,e){return t.filter(function(t){return t.key===e.traceId})}function h(t,e){n.select(t).select("path").style("fill-opacity",e),n.select(t).select("rect").style("fill-opacity",e)}function p(t){n.select(t).select("text.name").style("fill","black")}function d(t){return function(e){return-1!==t.node.sourceLinks.indexOf(e.link)||-1!==t.node.targetLinks.indexOf(e.link)}}function g(t){return function(e){return-1!==e.node.sourceLinks.indexOf(t.link)||-1!==e.node.targetLinks.indexOf(t.link)}}function m(t,e,r){e&&r&&f(r,e).selectAll("."+l.sankeyLink).filter(d(e)).call(y.bind(0,e,r,!1))}function v(t,e,r){e&&r&&f(r,e).selectAll("."+l.sankeyLink).filter(d(e)).call(x.bind(0,e,r,!1))}function y(t,e,r,n){var i=n.datum().link.label;n.style("fill-opacity",.4),i&&f(e,t).selectAll("."+l.sankeyLink).filter(function(t){return t.link.label===i}).style("fill-opacity",.4),r&&f(e,t).selectAll("."+l.sankeyNode).filter(g(t)).call(m)}function x(t,e,r,n){var i=n.datum().link.label;n.style("fill-opacity",function(t){return t.tinyColorAlpha}),i&&f(e,t).selectAll("."+l.sankeyLink).filter(function(t){return t.link.label===i}).style("fill-opacity",function(t){return t.tinyColorAlpha}),r&&f(e,t).selectAll(l.sankeyNode).filter(g(t)).call(v)}function b(t,e){var r=t.hoverlabel||{},n=s.nestedProperty(r,e).get();return!Array.isArray(n)&&n}e.exports=function(t,e){var r=t._fullLayout,s=r._paper,f=r._size,d=c(t,"source:")+" ",g=c(t,"target:")+" ",_=c(t,"incoming flow count:")+" ",w=c(t,"outgoing flow count:")+" ";i(s,e,{width:f.w,height:f.h,margin:{t:f.t,r:f.r,b:f.b,l:f.l}},{linkEvents:{hover:function(e,r,i){n.select(e).call(y.bind(0,r,i,!0)),t.emit("plotly_hover",{event:n.event,points:[r.link]})},follow:function(e,i){var s=i.link.trace,l=t._fullLayout._paperdiv.node().getBoundingClientRect(),c=e.getBoundingClientRect(),f=c.left+c.width/2,m=c.top+c.height/2,v=a.loneHover({x:f-l.left,y:m-l.top,name:n.format(i.valueFormat)(i.link.value)+i.valueSuffix,text:[i.link.label||"",d+i.link.source.label,g+i.link.target.label].filter(u).join("
"),color:b(s,"bgcolor")||o.addOpacity(i.tinyColorHue,1),borderColor:b(s,"bordercolor"),fontFamily:b(s,"font.family"),fontSize:b(s,"font.size"),fontColor:b(s,"font.color"),idealAlign:n.event.x"),color:b(o,"bgcolor")||i.tinyColorHue,borderColor:b(o,"bordercolor"),fontFamily:b(o,"font.family"),fontSize:b(o,"font.size"),fontColor:b(o,"font.color"),idealAlign:"left"},{container:r._hoverlayer.node(),outerContainer:r._paper.node(),gd:t});h(v,.85),p(v)},unhover:function(e,i,o){n.select(e).call(v,i,o),t.emit("plotly_unhover",{event:n.event,points:[i.node]}),a.loneUnhover(r._hoverlayer.node())},select:function(e,r,i){var o=r.node;o.originalEvent=n.event,t._hoverdata=[o],n.select(e).call(v,r,i),a.click(t,{target:!0})}}})}},{"../../components/color":532,"../../components/fx":574,"../../lib":660,"./constants":984,"./render":988,d3:131}],988:[function(t,e,r){"use strict";var n=t("./constants"),i=t("d3"),a=t("tinycolor2"),o=t("../../components/color"),s=t("../../components/drawing"),l=t("@plotly/d3-sankey").sankey,c=t("d3-force"),u=t("../../lib"),f=u.isArrayOrTypedArray,h=u.isIndex,p=t("../../lib/gup"),d=p.keyFun,g=p.repeat,m=p.unwrap;function v(t){t.lastDraggedX=t.x,t.lastDraggedY=t.y}function y(t){return function(e){return e.node.originalX===t.node.originalX}}function x(t){for(var e=0;e1||t.linkLineWidth>0}function T(t){return"translate("+t.translateX+","+t.translateY+")"+(t.horizontal?"matrix(1 0 0 1 0 0)":"matrix(0 1 1 0 0 0)")}function S(t){return"translate("+(t.horizontal?0:t.labelY)+" "+(t.horizontal?t.labelY:0)+")"}function C(t){return i.svg.line()([[t.horizontal?t.left?-t.sizeAcross:t.visibleWidth+n.nodeTextOffsetHorizontal:n.nodeTextOffsetHorizontal,0],[t.horizontal?t.left?-n.nodeTextOffsetHorizontal:t.sizeAcross:t.visibleHeight-n.nodeTextOffsetHorizontal,0]])}function E(t){return t.horizontal?"matrix(1 0 0 1 0 0)":"matrix(0 1 1 0 0 0)"}function L(t){return t.horizontal?"scale(1 1)":"scale(-1 1)"}function z(t){return t.darkBackground&&!t.horizontal?"rgb(255,255,255)":"rgb(0,0,0)"}function P(t){return t.horizontal&&t.left?"100%":"0%"}function D(t,e,r){t.on(".basic",null).on("mouseover.basic",function(t){t.interactionState.dragInProgress||(r.hover(this,t,e),t.interactionState.hovered=[this,t])}).on("mousemove.basic",function(t){t.interactionState.dragInProgress||(r.follow(this,t),t.interactionState.hovered=[this,t])}).on("mouseout.basic",function(t){t.interactionState.dragInProgress||(r.unhover(this,t,e),t.interactionState.hovered=!1)}).on("click.basic",function(t){t.interactionState.hovered&&(r.unhover(this,t,e),t.interactionState.hovered=!1),t.interactionState.dragInProgress||r.select(this,t,e)})}function O(t,e,r){var a=i.behavior.drag().origin(function(t){return t.node}).on("dragstart",function(i){if("fixed"!==i.arrangement&&(u.raiseToTop(this),i.interactionState.dragInProgress=i.node,v(i.node),i.interactionState.hovered&&(r.nodeEvents.unhover.apply(0,i.interactionState.hovered),i.interactionState.hovered=!1),"snap"===i.arrangement)){var a=i.traceId+"|"+Math.floor(i.node.originalX);i.forceLayouts[a]?i.forceLayouts[a].alpha(1):function(t,e,r){var i=r.sankey.nodes().filter(function(t){return t.originalX===r.node.originalX});r.forceLayouts[e]=c.forceSimulation(i).alphaDecay(0).force("collide",c.forceCollide().radius(function(t){return t.dy/2+r.nodePad/2}).strength(1).iterations(n.forceIterations)).force("constrain",function(t,e,r,i){return function(){for(var t=0,a=0;a0&&i.forceLayouts[e].alpha(0)}}(0,e,i,r)).stop()}(0,a,i),function(t,e,r,i){window.requestAnimationFrame(function a(){for(var o=0;o0&&window.requestAnimationFrame(a)})}(t,e,i,a)}}).on("drag",function(r){if("fixed"!==r.arrangement){var n=i.event.x,a=i.event.y;"snap"===r.arrangement?(r.node.x=n,r.node.y=a):("freeform"===r.arrangement&&(r.node.x=n),r.node.y=Math.max(r.node.dy/2,Math.min(r.size-r.node.dy/2,a))),v(r.node),"snap"!==r.arrangement&&(r.sankey.relayout(),k(t.filter(y(r)),e))}}).on("dragend",function(t){t.interactionState.dragInProgress=!1});t.on(".drag",null).call(a)}e.exports=function(t,e,r,i){var c=t.selectAll("."+n.cn.sankey).data(e.filter(function(t){return m(t).trace.visible}).map(function(t,e,r){var i,a=m(e).trace,o=a.domain,s=a.node,c=a.link,u=a.arrangement,p="h"===a.orientation,d=a.node.pad,g=a.node.thickness,v=a.node.line.color,y=a.node.line.width,b=a.link.line.color,_=a.link.line.width,w=a.valueformat,k=a.valuesuffix,M=a.textfont,A=t.width*(o.x[1]-o.x[0]),T=t.height*(o.y[1]-o.y[0]),S=[],C=f(c.color),E={},L=s.label.length;for(i=0;i0&&h(P,L)&&h(D,L)&&(D=+D,E[P=+P]=E[D]=!0,S.push({pointNumber:i,label:c.label[i],color:C?c.color[i]:c.color,source:P,target:D,value:+z}))}var O=f(s.color),I=[],R=!1,B={};for(i=0;i5?t.node.label:""}).attr("text-anchor",function(t){return t.horizontal&&t.left?"end":"start"}),F.transition().ease(n.ease).duration(n.duration).attr("startOffset",P).style("fill",z)}},{"../../components/color":532,"../../components/drawing":557,"../../lib":660,"../../lib/gup":657,"./constants":984,"@plotly/d3-sankey":43,d3:131,"d3-force":127,tinycolor2:473}],989:[function(t,e,r){"use strict";var n=t("../../lib");e.exports=function(t,e){for(var r=0;r=0;i--){var a=t[i];if("scatter"===a.type&&a.xaxis===r.xaxis&&a.yaxis===r.yaxis){a.opacity=void 0;break}}}}}},{}],994:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../../lib"),a=t("../../plots/plots"),o=t("../../components/colorscale"),s=t("../../components/colorbar/draw");e.exports=function(t,e){var r=e[0].trace,l=r.marker,c="cb"+r.uid;if(t._fullLayout._infolayer.selectAll("."+c).remove(),void 0!==l&&l.showscale){var u=l.color,f=l.cmin,h=l.cmax;n(f)||(f=i.aggNums(Math.min,null,u)),n(h)||(h=i.aggNums(Math.max,null,u));var p=e[0].t.cb=s(t,c),d=o.makeColorScaleFunc(o.extractScale(l.colorscale,f,h),{noNumericCheck:!0});p.fillcolor(d).filllevels({start:f,end:h,size:(h-f)/254}).options(l.colorbar)()}else a.autoMargin(t,c)}},{"../../components/colorbar/draw":536,"../../components/colorscale":547,"../../lib":660,"../../plots/plots":768,"fast-isnumeric":197}],995:[function(t,e,r){"use strict";var n=t("../../components/colorscale/has_colorscale"),i=t("../../components/colorscale/calc"),a=t("./subtypes");e.exports=function(t){a.hasLines(t)&&n(t,"line")&&i(t,t.line.color,"line","c"),a.hasMarkers(t)&&(n(t,"marker")&&i(t,t.marker.color,"marker","c"),n(t,"marker.line")&&i(t,t.marker.line.color,"marker.line","c"))}},{"../../components/colorscale/calc":539,"../../components/colorscale/has_colorscale":546,"./subtypes":1012}],996:[function(t,e,r){"use strict";e.exports={PTS_LINESONLY:20,minTolerance:.2,toleranceGrowth:10,maxScreensAway:20}},{}],997:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("../../registry"),a=t("./attributes"),o=t("./constants"),s=t("./subtypes"),l=t("./xy_defaults"),c=t("./marker_defaults"),u=t("./line_defaults"),f=t("./line_shape_defaults"),h=t("./text_defaults"),p=t("./fillcolor_defaults");e.exports=function(t,e,r,d){function g(r,i){return n.coerce(t,e,a,r,i)}var m=l(t,e,d,g),v=mV!=(D=C[T][1])>=V&&(L=C[T-1][0],z=C[T][0],D-P&&(E=L+(z-L)*(V-P)/(D-P),B=Math.min(B,E),F=Math.max(F,E)));B=Math.max(B,0),F=Math.min(F,h._length);var U=s.defaultLine;return s.opacity(f.fillcolor)?U=f.fillcolor:s.opacity((f.line||{}).color)&&(U=f.line.color),n.extendFlat(t,{distance:t.maxHoverDistance,x0:B,x1:F,y0:V,y1:V,color:U}),delete t.index,f.text&&!Array.isArray(f.text)?t.text=String(f.text):t.text=f.name,[t]}}}},{"../../components/color":532,"../../components/fx":574,"../../lib":660,"../../registry":790,"./fill_hover_text":998,"./get_trace_color":1e3}],1002:[function(t,e,r){"use strict";var n={},i=t("./subtypes");n.hasLines=i.hasLines,n.hasMarkers=i.hasMarkers,n.hasText=i.hasText,n.isBubble=i.isBubble,n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.cleanData=t("./clean_data"),n.calc=t("./calc").calc,n.arraysToCalcdata=t("./arrays_to_calcdata"),n.plot=t("./plot"),n.colorbar=t("./colorbar"),n.style=t("./style").style,n.styleOnSelect=t("./style").styleOnSelect,n.hoverPoints=t("./hover"),n.selectPoints=t("./select"),n.animatable=!0,n.moduleType="trace",n.name="scatter",n.basePlotModule=t("../../plots/cartesian"),n.categories=["cartesian","svg","symbols","markerColorscale","errorBarsOK","showLegend","scatter-like","zoomScale"],n.meta={},e.exports=n},{"../../plots/cartesian":717,"./arrays_to_calcdata":989,"./attributes":990,"./calc":991,"./clean_data":993,"./colorbar":994,"./defaults":997,"./hover":1001,"./plot":1009,"./select":1010,"./style":1011,"./subtypes":1012}],1003:[function(t,e,r){"use strict";var n=t("../../lib").isArrayOrTypedArray,i=t("../../components/colorscale/has_colorscale"),a=t("../../components/colorscale/defaults");e.exports=function(t,e,r,o,s,l){var c=(t.marker||{}).color;(s("line.color",r),i(t,"line"))?a(t,e,o,s,{prefix:"line.",cLetter:"c"}):s("line.color",!n(c)&&c||r);s("line.width"),(l||{}).noDash||s("line.dash")}},{"../../components/colorscale/defaults":542,"../../components/colorscale/has_colorscale":546,"../../lib":660}],1004:[function(t,e,r){"use strict";var n=t("../../constants/numerical").BADNUM,i=t("../../lib"),a=i.segmentsIntersect,o=i.constrain,s=t("./constants");e.exports=function(t,e){var r,l,c,u,f,h,p,d,g,m,v,y,x,b,_,w,k=e.xaxis,M=e.yaxis,A=e.simplify,T=e.connectGaps,S=e.baseTolerance,C=e.shape,E="linear"===C,L=[],z=s.minTolerance,P=new Array(t.length),D=0;function O(e){var r=t[e],i=k.c2p(r.x),a=M.c2p(r.y);return i===n||a===n?r.intoCenter||!1:[i,a]}function I(t){var e=t[0]/k._length,r=t[1]/M._length;return(1+s.toleranceGrowth*Math.max(0,-e,e-1,-r,r-1))*S}function R(t,e){var r=t[0]-e[0],n=t[1]-e[1];return Math.sqrt(r*r+n*n)}A||(S=z=-1);var B,F,N,j,V,U,q,H=s.maxScreensAway,G=-k._length*H,W=k._length*(1+H),Y=-M._length*H,X=M._length*(1+H),Z=[[G,Y,W,Y],[W,Y,W,X],[W,X,G,X],[G,X,G,Y]];function J(t){if(t[0]W||t[1]X)return[o(t[0],G,W),o(t[1],Y,X)]}function K(t,e){return t[0]===e[0]&&(t[0]===G||t[0]===W)||(t[1]===e[1]&&(t[1]===Y||t[1]===X)||void 0)}function Q(t,e,r){return function(n,a){var o=J(n),s=J(a),l=[];if(o&&s&&K(o,s))return l;o&&l.push(o),s&&l.push(s);var c=2*i.constrain((n[t]+a[t])/2,e,r)-((o||n)[t]+(s||a)[t]);c&&((o&&s?c>0==o[t]>s[t]?o:s:o||s)[t]+=c);return l}}function $(t){var e=t[0],r=t[1],n=e===P[D-1][0],i=r===P[D-1][1];if(!n||!i)if(D>1){var a=e===P[D-2][0],o=r===P[D-2][1];n&&(e===G||e===W)&&a?o?D--:P[D-1]=t:i&&(r===Y||r===X)&&o?a?D--:P[D-1]=t:P[D++]=t}else P[D++]=t}function tt(t){P[D-1][0]!==t[0]&&P[D-1][1]!==t[1]&&$([N,j]),$(t),V=null,N=j=0}function et(t){if(B=t[0]W?W:0,F=t[1]X?X:0,B||F){if(D)if(V){var e=q(V,t);e.length>1&&(tt(e[0]),P[D++]=e[1])}else U=q(P[D-1],t)[0],P[D++]=U;else P[D++]=[B||t[0],F||t[1]];var r=P[D-1];B&&F&&(r[0]!==B||r[1]!==F)?(V&&(N!==B&&j!==F?$(N&&j?(n=V,a=(i=t)[0]-n[0],o=(i[1]-n[1])/a,(n[1]*i[0]-i[1]*n[0])/a>0?[o>0?G:W,X]:[o>0?W:G,Y]):[N||B,j||F]):N&&j&&$([N,j])),$([B,F])):N-B&&j-F&&$([B||N,F||j]),V=t,N=B,j=F}else V&&tt(q(V,t)[0]),P[D++]=t;var n,i,a,o}for("linear"===C||"spline"===C?q=function(t,e){for(var r=[],n=0,i=0;i<4;i++){var o=Z[i],s=a(t[0],t[1],e[0],e[1],o[0],o[1],o[2],o[3]);s&&(!n||Math.abs(s.x-r[0][0])>1||Math.abs(s.y-r[0][1])>1)&&(s=[s.x,s.y],n&&R(s,t)I(h))break;c=h,(x=g[0]*d[0]+g[1]*d[1])>v?(v=x,u=h,p=!1):x=t.length||!h)break;et(h),l=h}}else et(u)}V&&$([N||V[0],j||V[1]]),L.push(P.slice(0,D))}return L}},{"../../constants/numerical":637,"../../lib":660,"./constants":996}],1005:[function(t,e,r){"use strict";e.exports=function(t,e,r){"spline"===r("line.shape")&&r("line.smoothing")}},{}],1006:[function(t,e,r){"use strict";e.exports=function(t,e,r){var n,i,a=null;for(i=0;i0?Math.max(e,i):0}}},{"fast-isnumeric":197}],1008:[function(t,e,r){"use strict";var n=t("../../components/color"),i=t("../../components/colorscale/has_colorscale"),a=t("../../components/colorscale/defaults"),o=t("./subtypes");e.exports=function(t,e,r,s,l,c){var u=o.isBubble(t),f=(t.line||{}).color;(c=c||{},f&&(r=f),l("marker.symbol"),l("marker.opacity",u?.7:1),l("marker.size"),l("marker.color",r),i(t,"marker")&&a(t,e,s,l,{prefix:"marker.",cLetter:"c"}),c.noSelect||(l("selected.marker.color"),l("unselected.marker.color"),l("selected.marker.size"),l("unselected.marker.size")),c.noLine||(l("marker.line.color",f&&!Array.isArray(f)&&e.marker.color!==f?f:u?n.background:n.defaultLine),i(t,"marker.line")&&a(t,e,s,l,{prefix:"marker.line.",cLetter:"c"}),l("marker.line.width",u?1:0)),u&&(l("marker.sizeref"),l("marker.sizemin"),l("marker.sizemode")),c.gradient)&&("none"!==l("marker.gradient.type")&&l("marker.gradient.color"))}},{"../../components/color":532,"../../components/colorscale/defaults":542,"../../components/colorscale/has_colorscale":546,"./subtypes":1012}],1009:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../registry"),a=t("../../lib"),o=t("../../components/drawing"),s=t("./subtypes"),l=t("./line_points"),c=t("./link_traces"),u=t("../../lib/polygon").tester;function f(t,e,r,c,f,h,p){var d,g;!function(t,e,r,i,o){var l=r.xaxis,c=r.yaxis,u=n.extent(a.simpleMap(l.range,l.r2c)),f=n.extent(a.simpleMap(c.range,c.r2c)),h=i[0].trace;if(!s.hasMarkers(h))return;var p=h.marker.maxdisplayed;if(0===p)return;var d=i.filter(function(t){return t.x>=u[0]&&t.x<=u[1]&&t.y>=f[0]&&t.y<=f[1]}),g=Math.ceil(d.length/p),m=0;o.forEach(function(t,r){var n=t[0].trace;s.hasMarkers(n)&&n.marker.maxdisplayed>0&&r0;function v(t){return m?t.transition():t}var y=r.xaxis,x=r.yaxis,b=c[0].trace,_=b.line,w=n.select(h);if(i.getComponentMethod("errorbars","plot")(w,r,p),!0===b.visible){var k,M;v(w).style("opacity",b.opacity);var A=b.fill.charAt(b.fill.length-1);"x"!==A&&"y"!==A&&(A=""),r.isRangePlot||(c[0].node3=w);var T="",S=[],C=b._prevtrace;C&&(T=C._prevRevpath||"",M=C._nextFill,S=C._polygons);var E,L,z,P,D,O,I,R,B,F="",N="",j=[],V=a.noop;if(k=b._ownFill,s.hasLines(b)||"none"!==b.fill){for(M&&M.datum(c),-1!==["hv","vh","hvh","vhv"].indexOf(_.shape)?(z=o.steps(_.shape),P=o.steps(_.shape.split("").reverse().join(""))):z=P="spline"===_.shape?function(t){var e=t[t.length-1];return t.length>1&&t[0][0]===e[0]&&t[0][1]===e[1]?o.smoothclosed(t.slice(1),_.smoothing):o.smoothopen(t,_.smoothing)}:function(t){return"M"+t.join("L")},D=function(t){return P(t.reverse())},j=l(c,{xaxis:y,yaxis:x,connectGaps:b.connectgaps,baseTolerance:Math.max(_.width||1,3)/4,shape:_.shape,simplify:_.simplify}),B=b._polygons=new Array(j.length),g=0;g1){var r=n.select(this);if(r.datum(c),t)v(r.style("opacity",0).attr("d",E).call(o.lineGroupStyle)).style("opacity",1);else{var i=v(r);i.attr("d",E),o.singleLineStyle(c,i)}}}}}var U=w.selectAll(".js-line").data(j);v(U.exit()).style("opacity",0).remove(),U.each(V(!1)),U.enter().append("path").classed("js-line",!0).style("vector-effect","non-scaling-stroke").call(o.lineGroupStyle).each(V(!0)),o.setClipUrl(U,r.layerClipId),j.length?(k?O&&R&&(A?("y"===A?O[1]=R[1]=x.c2p(0,!0):"x"===A&&(O[0]=R[0]=y.c2p(0,!0)),v(k).attr("d","M"+R+"L"+O+"L"+F.substr(1)).call(o.singleFillStyle)):v(k).attr("d",F+"Z").call(o.singleFillStyle)):M&&("tonext"===b.fill.substr(0,6)&&F&&T?("tonext"===b.fill?v(M).attr("d",F+"Z"+T+"Z").call(o.singleFillStyle):v(M).attr("d",F+"L"+T.substr(1)+"Z").call(o.singleFillStyle),b._polygons=b._polygons.concat(S)):(H(M),b._polygons=null)),b._prevRevpath=N,b._prevPolygons=B):(k?H(k):M&&H(M),b._polygons=b._prevRevpath=b._prevPolygons=null);var q=w.selectAll(".points");d=q.data([c]),q.each(Z),d.enter().append("g").classed("points",!0).each(Z),d.exit().remove(),d.each(function(t){var e=!1===t[0].trace.cliponaxis;o.setClipUrl(n.select(this),e?null:r.layerClipId)})}function H(t){v(t).attr("d","M0,0Z")}function G(t){return t.filter(function(t){return t.vis})}function W(t){return t.id}function Y(t){if(t.ids)return W}function X(){return!1}function Z(e){var i,l=e[0].trace,c=n.select(this),u=s.hasMarkers(l),f=s.hasText(l),h=Y(l),p=X,d=X;u&&(p=l.marker.maxdisplayed||l._needsCull?G:a.identity),f&&(d=l.marker.maxdisplayed||l._needsCull?G:a.identity);var g,b=(i=c.selectAll("path.point").data(p,h)).enter().append("path").classed("point",!0);m&&b.call(o.pointStyle,l,t).call(o.translatePoints,y,x).style("opacity",0).transition().style("opacity",1),i.order(),u&&(g=o.makePointStyleFns(l)),i.each(function(e){var i=n.select(this),a=v(i);o.translatePoint(e,a,y,x)?(o.singlePointStyle(e,a,l,g,t),r.layerClipId&&o.hideOutsideRangePoint(e,a,y,x,l.xcalendar,l.ycalendar),l.customdata&&i.classed("plotly-customdata",null!==e.data&&void 0!==e.data)):a.remove()}),m?i.exit().transition().style("opacity",0).remove():i.exit().remove(),(i=c.selectAll("g").data(d,h)).enter().append("g").classed("textpoint",!0).append("text"),i.order(),i.each(function(t){var e=n.select(this),i=v(e.select("text"));o.translatePoint(t,i,y,x)?r.layerClipId&&o.hideOutsideRangePoint(t,e,y,x,l.xcalendar,l.ycalendar):e.remove()}),i.selectAll("text").call(o.textPointStyle,l,t).each(function(t){var e=y.c2p(t.x),r=x.c2p(t.y);n.select(this).selectAll("tspan.line").each(function(){v(n.select(this)).attr({x:e,y:r})})}),i.exit().remove()}}e.exports=function(t,e,r,i,a,s){var l,u,h,p,d=!a,g=!!a&&a.duration>0;for((h=i.selectAll("g.trace").data(r,function(t){return t[0].trace.uid})).enter().append("g").attr("class",function(t){return"trace scatter trace"+t[0].trace.uid}).style("stroke-miterlimit",2),c(t,e,r),function(t,e,r){var i;e.selectAll("g.trace").each(function(t){var e=n.select(this);if((i=t[0].trace)._nexttrace){if(i._nextFill=e.select(".js-fill.js-tonext"),!i._nextFill.size()){var a=":first-child";e.select(".js-fill.js-tozero").size()&&(a+=" + *"),i._nextFill=e.insert("path",a).attr("class","js-fill js-tonext")}}else e.selectAll(".js-fill.js-tonext").remove(),i._nextFill=null;i.fill&&("tozero"===i.fill.substr(0,6)||"toself"===i.fill||"to"===i.fill.substr(0,2)&&!i._prevtrace)?(i._ownFill=e.select(".js-fill.js-tozero"),i._ownFill.size()||(i._ownFill=e.insert("path",":first-child").attr("class","js-fill js-tozero"))):(e.selectAll(".js-fill.js-tozero").remove(),i._ownFill=null),e.selectAll(".js-fill").call(o.setClipUrl,r.layerClipId)})}(0,i,e),l=0,u={};lu[e[0].trace.uid]?1:-1}),g)?(s&&(p=s()),n.transition().duration(a.duration).ease(a.easing).each("end",function(){p&&p()}).each("interrupt",function(){p&&p()}).each(function(){i.selectAll("g.trace").each(function(n,i){f(t,i,e,n,r,this,a)})})):i.selectAll("g.trace").each(function(n,i){f(t,i,e,n,r,this,a)});d&&h.exit().remove(),i.selectAll("path:not([d])").remove()}},{"../../components/drawing":557,"../../lib":660,"../../lib/polygon":672,"../../registry":790,"./line_points":1004,"./link_traces":1006,"./subtypes":1012,d3:131}],1010:[function(t,e,r){"use strict";var n=t("./subtypes");e.exports=function(t,e){var r,i,a,o,s=t.cd,l=t.xaxis,c=t.yaxis,u=[],f=s[0].trace;if(!n.hasMarkers(f)&&!n.hasText(f))return[];if(!1===e)for(r=0;r=0&&(p[1]+=1),h.indexOf("top")>=0&&(p[1]-=1),h.indexOf("left")>=0&&(p[0]-=1),h.indexOf("right")>=0&&(p[0]+=1),p)),r.textColor=u(e.textfont,1,E),r.textSize=x(e.textfont.size,E,l.identity,12),r.textFont=e.textfont.family,r.textAngle=0);var O=["x","y","z"];for(r.project=[!1,!1,!1],r.projectScale=[1,1,1],r.projectOpacity=[1,1,1],n=0;n<3;++n){var I=e.projection[O[n]];(r.project[n]=I.show)&&(r.projectOpacity[n]=I.opacity,r.projectScale[n]=I.scale)}r.errorBounds=d(e,b);var R=function(t){for(var e=[0,0,0],r=[[0,0,0],[0,0,0],[0,0,0]],n=[0,0,0],i=0;i<3;i++){var a=t[i];a&&!1!==a.copy_zstyle&&(a=t[2]),a&&(e[i]=a.width/2,r[i]=c(a.color),n=a.thickness)}return{capSize:e,color:r,lineWidth:n}}([e.error_x,e.error_y,e.error_z]);return r.errorColor=R.color,r.errorLineWidth=R.lineWidth,r.errorCapSize=R.capSize,r.delaunayAxis=e.surfaceaxis,r.delaunayColor=c(e.surfacecolor),r}function _(t){if(Array.isArray(t)){var e=t[0];return Array.isArray(e)&&(t=e),"rgb("+t.slice(0,3).map(function(t){return Math.round(255*t)})+")"}return null}m.handlePick=function(t){if(t.object&&(t.object===this.linePlot||t.object===this.delaunayMesh||t.object===this.textMarkers||t.object===this.scatterPlot)){t.object.highlight&&t.object.highlight(null),this.scatterPlot&&(t.object=this.scatterPlot,this.scatterPlot.highlight(t.data)),this.textLabels?void 0!==this.textLabels[t.data.index]?t.textLabel=this.textLabels[t.data.index]:t.textLabel=this.textLabels:t.textLabel="";var e=t.index=t.data.index;return t.traceCoordinate=[this.data.x[e],this.data.y[e],this.data.z[e]],!0}},m.update=function(t){var e,r,l,c,u=this.scene.glplot.gl,f=h.solid;this.data=t;var p=b(this.scene,t);"mode"in p&&(this.mode=p.mode),"lineDashes"in p&&p.lineDashes in h&&(f=h[p.lineDashes]),this.color=_(p.scatterColor)||_(p.lineColor),this.dataPoints=p.position,e={gl:u,position:p.position,color:p.lineColor,lineWidth:p.lineWidth||1,dashes:f[0],dashScale:f[1],opacity:t.opacity,connectGaps:t.connectgaps},-1!==this.mode.indexOf("lines")?this.linePlot?this.linePlot.update(e):(this.linePlot=n(e),this.linePlot._trace=this,this.scene.glplot.add(this.linePlot)):this.linePlot&&(this.scene.glplot.remove(this.linePlot),this.linePlot.dispose(),this.linePlot=null);var d=t.opacity;if(t.marker&&t.marker.opacity&&(d*=t.marker.opacity),r={gl:u,position:p.position,color:p.scatterColor,size:p.scatterSize,glyph:p.scatterMarker,opacity:d,orthographic:!0,lineWidth:p.scatterLineWidth,lineColor:p.scatterLineColor,project:p.project,projectScale:p.projectScale,projectOpacity:p.projectOpacity},-1!==this.mode.indexOf("markers")?this.scatterPlot?this.scatterPlot.update(r):(this.scatterPlot=i(r),this.scatterPlot._trace=this,this.scatterPlot.highlightScale=1,this.scene.glplot.add(this.scatterPlot)):this.scatterPlot&&(this.scene.glplot.remove(this.scatterPlot),this.scatterPlot.dispose(),this.scatterPlot=null),c={gl:u,position:p.position,glyph:p.text,color:p.textColor,size:p.textSize,angle:p.textAngle,alignment:p.textOffset,font:p.textFont,orthographic:!0,lineWidth:0,project:!1,opacity:t.opacity},this.textLabels=t.hovertext||t.text,-1!==this.mode.indexOf("text")?this.textMarkers?this.textMarkers.update(c):(this.textMarkers=i(c),this.textMarkers._trace=this,this.textMarkers.highlightScale=1,this.scene.glplot.add(this.textMarkers)):this.textMarkers&&(this.scene.glplot.remove(this.textMarkers),this.textMarkers.dispose(),this.textMarkers=null),l={gl:u,position:p.position,color:p.errorColor,error:p.errorBounds,lineWidth:p.errorLineWidth,capSize:p.errorCapSize,opacity:t.opacity},this.errorBars?p.errorBounds?this.errorBars.update(l):(this.scene.glplot.remove(this.errorBars),this.errorBars.dispose(),this.errorBars=null):p.errorBounds&&(this.errorBars=a(l),this.errorBars._trace=this,this.scene.glplot.add(this.errorBars)),p.delaunayAxis>=0){var g=function(t,e,r){var n,i=(r+1)%3,a=(r+2)%3,o=[],l=[];for(n=0;n=0&&f("surfacecolor",h||p);for(var d=["x","y","z"],g=0;g<3;++g){var m="projection."+d[g];f(m+".show")&&(f(m+".opacity"),f(m+".scale"))}var v=n.getComponentMethod("errorbars","supplyDefaults");v(t,e,r,{axis:"z"}),v(t,e,r,{axis:"y",inherit:"z"}),v(t,e,r,{axis:"x",inherit:"z"})}else e.visible=!1}},{"../../lib":660,"../../registry":790,"../scatter/line_defaults":1003,"../scatter/marker_defaults":1008,"../scatter/subtypes":1012,"../scatter/text_defaults":1013,"./attributes":1015}],1020:[function(t,e,r){"use strict";var n={};n.plot=t("./convert"),n.attributes=t("./attributes"),n.markerSymbols=t("../../constants/gl3d_markers"),n.supplyDefaults=t("./defaults"),n.colorbar=t("../scatter/colorbar"),n.calc=t("./calc"),n.moduleType="trace",n.name="scatter3d",n.basePlotModule=t("../../plots/gl3d"),n.categories=["gl3d","symbols","markerColorscale","showLegend"],n.meta={},e.exports=n},{"../../constants/gl3d_markers":635,"../../plots/gl3d":748,"../scatter/colorbar":994,"./attributes":1015,"./calc":1016,"./convert":1018,"./defaults":1019}],1021:[function(t,e,r){"use strict";var n=t("../scatter/attributes"),i=t("../../plots/attributes"),a=t("../../components/colorscale/color_attributes"),o=t("../../components/colorbar/attributes"),s=t("../../lib/extend").extendFlat,l=n.marker,c=n.line,u=l.line;e.exports={carpet:{valType:"string",editType:"calc"},a:{valType:"data_array",editType:"calc"},b:{valType:"data_array",editType:"calc"},mode:s({},n.mode,{dflt:"markers"}),text:s({},n.text,{}),line:{color:c.color,width:c.width,dash:c.dash,shape:s({},c.shape,{values:["linear","spline"]}),smoothing:c.smoothing,editType:"calc"},connectgaps:n.connectgaps,fill:s({},n.fill,{values:["none","toself","tonext"]}),fillcolor:n.fillcolor,marker:s({symbol:l.symbol,opacity:l.opacity,maxdisplayed:l.maxdisplayed,size:l.size,sizeref:l.sizeref,sizemin:l.sizemin,sizemode:l.sizemode,line:s({width:u.width,editType:"calc"},a("marker".line)),gradient:l.gradient,editType:"calc"},a("marker"),{showscale:l.showscale,colorbar:o}),textfont:n.textfont,textposition:n.textposition,selected:n.selected,unselected:n.unselected,hoverinfo:s({},i.hoverinfo,{flags:["a","b","text","name"]}),hoveron:n.hoveron}},{"../../components/colorbar/attributes":533,"../../components/colorscale/color_attributes":540,"../../lib/extend":649,"../../plots/attributes":703,"../scatter/attributes":990}],1022:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../scatter/colorscale_calc"),a=t("../scatter/arrays_to_calcdata"),o=t("../scatter/calc_selection"),s=t("../scatter/calc").calcMarkerSize,l=t("../carpet/lookup_carpetid");e.exports=function(t,e){var r=e._carpetTrace=l(t,e);if(r&&r.visible&&"legendonly"!==r.visible){var c;e.xaxis=r.xaxis,e.yaxis=r.yaxis;var u,f,h=e._length,p=new Array(h),d=!1;for(c=0;c"),a}function w(t,e){var r;r=t.labelprefix&&t.labelprefix.length>0?t.labelprefix.replace(/ = $/,""):t._hovertitle,g.push(r+": "+e.toFixed(3)+t.labelsuffix)}}},{"../scatter/hover":1001}],1026:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.colorbar=t("../scatter/colorbar"),n.calc=t("./calc"),n.plot=t("./plot"),n.style=t("../scatter/style").style,n.styleOnSelect=t("../scatter/style").styleOnSelect,n.hoverPoints=t("./hover"),n.selectPoints=t("../scatter/select"),n.eventData=t("./event_data"),n.moduleType="trace",n.name="scattercarpet",n.basePlotModule=t("../../plots/cartesian"),n.categories=["svg","carpet","symbols","markerColorscale","showLegend","carpetDependent","zoomScale"],n.meta={},e.exports=n},{"../../plots/cartesian":717,"../scatter/colorbar":994,"../scatter/select":1010,"../scatter/style":1011,"./attributes":1021,"./calc":1022,"./defaults":1023,"./event_data":1024,"./hover":1025,"./plot":1027}],1027:[function(t,e,r){"use strict";var n=t("../scatter/plot"),i=t("../../plots/cartesian/axes"),a=t("../../components/drawing");e.exports=function(t,e,r,o){var s,l,c,u=r[0][0].carpet,f={xaxis:i.getFromId(t,u.xaxis||"x"),yaxis:i.getFromId(t,u.yaxis||"y"),plot:e.plot};for(n(t,f,r,o),s=0;s")}(u,m,p.mockAxis,c[0].t.labels),[t]}}},{"../../components/fx":574,"../../constants/numerical":637,"../../plots/cartesian/axes":706,"../scatter/fill_hover_text":998,"../scatter/get_trace_color":1e3,"./attributes":1028}],1033:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.colorbar=t("../scatter/colorbar"),n.calc=t("./calc"),n.plot=t("./plot"),n.style=t("./style"),n.styleOnSelect=t("../scatter/style").styleOnSelect,n.hoverPoints=t("./hover"),n.eventData=t("./event_data"),n.selectPoints=t("./select"),n.moduleType="trace",n.name="scattergeo",n.basePlotModule=t("../../plots/geo"),n.categories=["geo","symbols","markerColorscale","showLegend","scatter-like"],n.meta={},e.exports=n},{"../../plots/geo":736,"../scatter/colorbar":994,"../scatter/style":1011,"./attributes":1028,"./calc":1029,"./defaults":1030,"./event_data":1031,"./hover":1032,"./plot":1034,"./select":1035,"./style":1036}],1034:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../lib"),a=t("../../constants/numerical").BADNUM,o=t("../../lib/topojson_utils").getTopojsonFeatures,s=t("../../lib/geo_location_utils").locationToFeature,l=t("../../lib/geojson_utils"),c=t("../scatter/subtypes"),u=t("./style");function f(t,e){var r=t[0].trace;if(Array.isArray(r.locations))for(var n=o(r,e),i=r.locationmode,l=0;ld.TOO_MANY_POINTS?"rect":h.hasMarkers(e)?"rect":"round";if(o&&e.connectgaps){var l=n[0],c=n[1];for(i=0;i1&&c.extendFlat(o.line,b(t,r,n)),o.errorX||o.errorY){var s=_(t,r,n,i,a);o.errorX&&c.extendFlat(o.errorX,s.x),o.errorY&&c.extendFlat(o.errorY,s.y)}return o}function A(t,e){var r=e._scene,n=t._fullLayout,i={count:0,dirty:!0,lineOptions:[],fillOptions:[],markerOptions:[],selectedOptions:[],unselectedOptions:[],errorXOptions:[],errorYOptions:[]};return e._scene||((r=e._scene=c.extendFlat({},i,{selectBatch:null,unselectBatch:null,fill2d:!1,scatter2d:!1,error2d:!1,line2d:!1,select2d:null})).update=function(t){for(var e=new Array(r.count),n=0;n=k&&(T.marker.cluster=m.tree),S.lineOptions.push(T.line),S.errorXOptions.push(T.errorX),S.errorYOptions.push(T.errorY),S.fillOptions.push(T.fill),S.markerOptions.push(T.marker),S.selectedOptions.push(T.selected),S.unselectedOptions.push(T.unselected),S.count++,m._scene=S,m.index=S.count-1,m.x=v,m.y=y,m.positions=x,m.count=u,t.firstscatter=!1,[{x:!1,y:!1,t:m,trace:e}]},plot:function(t,e,r){if(r.length){var o=t._fullLayout,s=r[0][0].t._scene,l=o.dragmode;if(s){var h=o._size,p=o.width,d=o.height;u(t,["ANGLE_instanced_arrays","OES_element_index_uint"]);var g=o._glcanvas.data()[0].regl;if(m(t,e,r),s.dirty){if(!0===s.error2d&&(s.error2d=a(g)),!0===s.line2d&&(s.line2d=i(g)),!0===s.scatter2d&&(s.scatter2d=n(g)),!0===s.fill2d&&(s.fill2d=i(g)),s.line2d&&s.line2d.update(s.lineOptions),s.error2d){var v=(s.errorXOptions||[]).concat(s.errorYOptions||[]);s.error2d.update(v)}s.scatter2d&&s.scatter2d.update(s.markerOptions),s.fill2d&&(s.fillOptions=s.fillOptions.map(function(t,e){var n=r[e];if(!(t&&n&&n[0]&&n[0].trace))return null;var i,a,o=n[0],l=o.trace,c=o.t,u=s.lineOptions[e],f=[],h=u&&u.positions||c.positions;if("tozeroy"===l.fill)(f=(f=[h[0],0]).concat(h)).push(h[h.length-2]),f.push(0);else if("tozerox"===l.fill)(f=(f=[0,h[1]]).concat(h)).push(0),f.push(h[h.length-1]);else if("toself"===l.fill||"tonext"===l.fill){for(f=[],i=0,a=0;a=0?Math.floor((e+180)/360):Math.ceil((e-180)/360)),d=e-p;if(n.getClosest(l,function(t){var e=t.lonlat;if(e[0]===s)return 1/0;var n=i.wrap180(e[0]),a=e[1],o=h.project([n,a]),l=o.x-u.c2p([d,a]),c=o.y-f.c2p([n,r]),p=Math.max(3,t.mrc||0);return Math.max(Math.sqrt(l*l+c*c)-p,1-3/p)},t),!1!==t.index){var g=l[t.index],m=g.lonlat,v=[i.wrap180(m[0])+p,m[1]],y=u.c2p(v),x=f.c2p(v),b=g.mrc||1;return t.x0=y-b,t.x1=y+b,t.y0=x-b,t.y1=x+b,t.color=a(c,g),t.extraText=function(t,e,r){var n=(e.hi||t.hoverinfo).split("+"),i=-1!==n.indexOf("all"),a=-1!==n.indexOf("lon"),s=-1!==n.indexOf("lat"),l=e.lonlat,c=[];function u(t){return t+"\xb0"}i||a&&s?c.push("("+u(l[0])+", "+u(l[1])+")"):a?c.push(r.lon+u(l[0])):s&&c.push(r.lat+u(l[1]));(i||-1!==n.indexOf("text"))&&o(e,t,c);return c.join("
")}(c,g,l[0].t.labels),[t]}}},{"../../components/fx":574,"../../constants/numerical":637,"../../lib":660,"../scatter/fill_hover_text":998,"../scatter/get_trace_color":1e3}],1047:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.colorbar=t("../scatter/colorbar"),n.calc=t("../scattergeo/calc"),n.plot=t("./plot"),n.hoverPoints=t("./hover"),n.eventData=t("./event_data"),n.selectPoints=t("./select"),n.style=function(t,e){e&&e[0].trace._glTrace.update(e)},n.moduleType="trace",n.name="scattermapbox",n.basePlotModule=t("../../plots/mapbox"),n.categories=["mapbox","gl","symbols","markerColorscale","showLegend","scatterlike"],n.meta={},e.exports=n},{"../../plots/mapbox":762,"../scatter/colorbar":994,"../scattergeo/calc":1029,"./attributes":1042,"./defaults":1044,"./event_data":1045,"./hover":1046,"./plot":1048,"./select":1049}],1048:[function(t,e,r){"use strict";var n=t("./convert");function i(t,e){this.subplot=t,this.uid=e,this.sourceIds={fill:e+"-source-fill",line:e+"-source-line",circle:e+"-source-circle",symbol:e+"-source-symbol"},this.layerIds={fill:e+"-layer-fill",line:e+"-layer-line",circle:e+"-layer-circle",symbol:e+"-layer-symbol"},this.order=["fill","line","circle","symbol"]}var a=i.prototype;a.addSource=function(t,e){this.subplot.map.addSource(this.sourceIds[t],{type:"geojson",data:e.geojson})},a.setSourceData=function(t,e){this.subplot.map.getSource(this.sourceIds[t]).setData(e.geojson)},a.addLayer=function(t,e){this.subplot.map.addLayer({type:t,id:this.layerIds[t],source:this.sourceIds[t],layout:e.layout,paint:e.paint})},a.update=function(t){for(var e=this.subplot,r=n(t),i=0;i")}e.exports={hoverPoints:function(t,e,r,i){var a=n(t,e,r,i);if(a&&!1!==a[0].index){var s=a[0];if(void 0===s.index)return a;var l=t.subplot,c=s.cd[s.index],u=s.trace;if(l.isPtWithinSector(c))return s.xLabelVal=void 0,s.yLabelVal=void 0,s.extraText=o(c,u,l),a}},makeHoverPointText:o}},{"../../lib":660,"../../plots/cartesian/axes":706,"../scatter/hover":1001}],1054:[function(t,e,r){"use strict";e.exports={moduleType:"trace",name:"scatterpolar",basePlotModule:t("../../plots/polar"),categories:["polar","symbols","markerColorscale","showLegend","scatter-like"],attributes:t("./attributes"),supplyDefaults:t("./defaults"),calc:t("./calc"),plot:t("./plot"),style:t("../scatter/style").style,hoverPoints:t("./hover").hoverPoints,selectPoints:t("../scatter/select"),meta:{}}},{"../../plots/polar":771,"../scatter/select":1010,"../scatter/style":1011,"./attributes":1050,"./calc":1051,"./defaults":1052,"./hover":1053,"./plot":1055}],1055:[function(t,e,r){"use strict";var n=t("../scatter/plot"),i=t("../../constants/numerical").BADNUM;e.exports=function(t,e,r){var a,o,s,l={xaxis:e.xaxis,yaxis:e.yaxis,plot:e.framework,layerClipId:e._hasClipOnAxisFalse?e.clipIds.circle:null},c=e.radialAxis,u=c.range;for(s=u[0]>u[1]?function(t){return t<=0}:function(t){return t>=0},a=0;a=0?(m=o.c2r(g)-l[0],T=v,y=s.c2rad(T,b.thetaunit),E[d]=C[2*d]=m*Math.cos(y),L[d]=C[2*d+1]=m*Math.sin(y)):E[d]=L[d]=C[2*d]=C[2*d+1]=NaN;var z=a.sceneOptions(t,e,b,C);z.fill&&!f.fill2d&&(f.fill2d=!0),z.marker&&!f.scatter2d&&(f.scatter2d=!0),z.line&&!f.line2d&&(f.line2d=!0),!z.errorX&&!z.errorY||f.error2d||(f.error2d=!0),_.tree=n(C),z.marker&&S>=u&&(z.marker.cluster=_.tree),c.hasMarkers(b)&&(z.selected.positions=z.unselected.positions=z.marker.positions),f.lineOptions.push(z.line),f.errorXOptions.push(z.errorX),f.errorYOptions.push(z.errorY),f.fillOptions.push(z.fill),f.markerOptions.push(z.marker),f.selectedOptions.push(z.selected),f.unselectedOptions.push(z.unselected),f.count=r.length,_._scene=f,_.index=p,_.x=E,_.y=L,_.rawx=E,_.rawy=L,_.r=w,_.theta=k,_.positions=C,_.count=S}}),a.plot(t,e,r)},hoverPoints:function(t,e,r,n){var i=t.cd[0].t,o=i.r,s=i.theta,c=a.hoverPoints(t,e,r,n);if(c&&!1!==c[0].index){var u=c[0];if(void 0===u.index)return c;var f=t.subplot,h=f.angularAxis,p=u.cd[u.index],d=u.trace;if(p.r=o[u.index],p.theta=s[u.index],p.rad=h.c2rad(p.theta,d.thetaunit),f.isPtWithinSector(p))return u.xLabelVal=void 0,u.yLabelVal=void 0,u.extraText=l(p,d,f),c}},style:a.style,selectPoints:a.selectPoints,meta:{}}},{"../../plots/cartesian/axes":706,"../../plots/polar":771,"../scatter/colorscale_calc":995,"../scatter/subtypes":1012,"../scattergl":1041,"../scattergl/constants":1038,"../scatterpolar/hover":1053,"./attributes":1056,"./defaults":1057,"fast-isnumeric":197,"point-cluster":411}],1059:[function(t,e,r){"use strict";var n=t("../scatter/attributes"),i=t("../../plots/attributes"),a=t("../../components/colorscale/color_attributes"),o=t("../../components/colorbar/attributes"),s=t("../../components/drawing/attributes").dash,l=t("../../lib/extend").extendFlat,c=n.marker,u=n.line,f=c.line;e.exports={a:{valType:"data_array",editType:"calc"},b:{valType:"data_array",editType:"calc"},c:{valType:"data_array",editType:"calc"},sum:{valType:"number",dflt:0,min:0,editType:"calc"},mode:l({},n.mode,{dflt:"markers"}),text:l({},n.text,{}),hovertext:l({},n.hovertext,{}),line:{color:u.color,width:u.width,dash:s,shape:l({},u.shape,{values:["linear","spline"]}),smoothing:u.smoothing,editType:"calc"},connectgaps:n.connectgaps,cliponaxis:n.cliponaxis,fill:l({},n.fill,{values:["none","toself","tonext"]}),fillcolor:n.fillcolor,marker:l({symbol:c.symbol,opacity:c.opacity,maxdisplayed:c.maxdisplayed,size:c.size,sizeref:c.sizeref,sizemin:c.sizemin,sizemode:c.sizemode,line:l({width:f.width,editType:"calc"},a("marker.line")),gradient:c.gradient,editType:"calc"},a("marker"),{showscale:c.showscale,colorbar:o}),textfont:n.textfont,textposition:n.textposition,selected:n.selected,unselected:n.unselected,hoverinfo:l({},i.hoverinfo,{flags:["a","b","c","text","name"]}),hoveron:n.hoveron}},{"../../components/colorbar/attributes":533,"../../components/colorscale/color_attributes":540,"../../components/drawing/attributes":556,"../../lib/extend":649,"../../plots/attributes":703,"../scatter/attributes":990}],1060:[function(t,e,r){"use strict";var n=t("fast-isnumeric"),i=t("../scatter/colorscale_calc"),a=t("../scatter/arrays_to_calcdata"),o=t("../scatter/calc_selection"),s=t("../scatter/calc").calcMarkerSize,l=["a","b","c"],c={a:["b","c"],b:["a","c"],c:["a","b"]};e.exports=function(t,e){var r,u,f,h,p,d,g=t._fullLayout[e.subplot].sum,m=e.sum||g,v={a:e.a,b:e.b,c:e.c};for(r=0;r"),o}function v(t,e){m.push(t._hovertitle+": "+i.tickText(t,e,"hover").text)}}},{"../../plots/cartesian/axes":706,"../scatter/hover":1001}],1064:[function(t,e,r){"use strict";var n={};n.attributes=t("./attributes"),n.supplyDefaults=t("./defaults"),n.colorbar=t("../scatter/colorbar"),n.calc=t("./calc"),n.plot=t("./plot"),n.style=t("../scatter/style").style,n.styleOnSelect=t("../scatter/style").styleOnSelect,n.hoverPoints=t("./hover"),n.selectPoints=t("../scatter/select"),n.eventData=t("./event_data"),n.moduleType="trace",n.name="scatterternary",n.basePlotModule=t("../../plots/ternary"),n.categories=["ternary","symbols","markerColorscale","showLegend","scatter-like"],n.meta={},e.exports=n},{"../../plots/ternary":783,"../scatter/colorbar":994,"../scatter/select":1010,"../scatter/style":1011,"./attributes":1059,"./calc":1060,"./defaults":1061,"./event_data":1062,"./hover":1063,"./plot":1065}],1065:[function(t,e,r){"use strict";var n=t("../scatter/plot");e.exports=function(t,e,r){var i=e.plotContainer;i.select(".scatterlayer").selectAll("*").remove();var a={xaxis:e.xaxis,yaxis:e.yaxis,plot:i,layerClipId:e._hasClipOnAxisFalse?e.clipIdRelative:null},o=e.layers.frontplot.select("g.scatterlayer");n(t,a,r,o)}},{"../scatter/plot":1009}],1066:[function(t,e,r){"use strict";var n=t("../scattergl/attributes"),i=t("../../plots/cartesian/constants").idRegex;function a(t){return{valType:"info_array",freeLength:!0,editType:"calc",items:{valType:"subplotid",regex:i[t],editType:"plot"}}}e.exports={dimensions:{_isLinkedToArray:"dimension",visible:{valType:"boolean",dflt:!0,editType:"calc"},label:{valType:"string",editType:"calc"},values:{valType:"data_array",editType:"calc+clearAxisTypes"},editType:"calc+clearAxisTypes"},text:n.text,marker:n.marker,xaxes:a("x"),yaxes:a("y"),diagonal:{visible:{valType:"boolean",dflt:!0,editType:"calc"},editType:"calc"},showupperhalf:{valType:"boolean",dflt:!0,editType:"calc"},showlowerhalf:{valType:"boolean",dflt:!0,editType:"calc"},selected:{marker:n.selected.marker,editType:"calc"},unselected:{marker:n.unselected.marker,editType:"calc"},opacity:n.opacity}},{"../../plots/cartesian/constants":711,"../scattergl/attributes":1037}],1067:[function(t,e,r){"use strict";var n=t("regl-line2d"),i=t("../../registry"),a=t("../../lib"),o=t("../../lib/prepare_regl"),s=t("../../plots/get_data").getModuleCalcData,l=t("../../plots/cartesian"),c=t("../../plots/cartesian/axis_ids"),u="splom";function f(t,e,r){for(var n=e.dimensions,i=r.matrixOptions.data.length,a=new Array(i),o=0,s=0;o1&&ra&&f?r._splomSubplots[y]=1:iv;for(r=0,n=0;r",maxDimensionCount:60,overdrag:45,releaseTransitionDuration:120,releaseTransitionEase:"cubic-out",scrollbarCaptureWidth:18,scrollbarHideDelay:1e3,scrollbarHideDuration:1e3,scrollbarOffset:5,scrollbarWidth:8,transitionDuration:100,transitionEase:"cubic-out",uplift:5,wrapSpacer:" ",wrapSplitCharacter:" ",cn:{table:"table",tableControlView:"table-control-view",scrollBackground:"scroll-background",yColumn:"y-column",columnBlock:"column-block",scrollAreaClip:"scroll-area-clip",scrollAreaClipRect:"scroll-area-clip-rect",columnBoundary:"column-boundary",columnBoundaryClippath:"column-boundary-clippath",columnBoundaryRect:"column-boundary-rect",columnCells:"column-cells",columnCell:"column-cell",cellRect:"cell-rect",cellText:"cell-text",cellTextHolder:"cell-text-holder",scrollbarKit:"scrollbar-kit",scrollbar:"scrollbar",scrollbarSlider:"scrollbar-slider",scrollbarGlyph:"scrollbar-glyph",scrollbarCaptureZone:"scrollbar-capture-zone"}}},{}],1080:[function(t,e,r){"use strict";var n=t("./constants"),i=t("../../lib/extend").extendFlat,a=t("fast-isnumeric");function o(t){if(Array.isArray(t)){for(var e=0,r=0;r=e||c===t.length-1)&&(n[i]=o,o.key=l++,o.firstRowIndex=s,o.lastRowIndex=c,o={firstRowIndex:null,lastRowIndex:null,rows:[]},i+=a,s=c+1,a=0);return n}e.exports=function(t,e){var r=l(e.cells.values),p=function(t){return t.slice(e.header.values.length,t.length)},d=l(e.header.values);d.length&&!d[0].length&&(d[0]=[""],d=l(d));var g=d.concat(p(r).map(function(){return c((d[0]||[""]).length)})),m=e.domain,v=Math.floor(t._fullLayout._size.w*(m.x[1]-m.x[0])),y=Math.floor(t._fullLayout._size.h*(m.y[1]-m.y[0])),x=e.header.values.length?g[0].map(function(){return e.header.height}):[n.emptyHeaderHeight],b=r.length?r[0].map(function(){return e.cells.height}):[],_=x.reduce(s,0),w=h(b,y-_+n.uplift),k=f(h(x,_),[]),M=f(w,k),A={},T=e._fullInput.columnorder.concat(p(r.map(function(t,e){return e}))),S=g.map(function(t,r){var n=Array.isArray(e.columnwidth)?e.columnwidth[Math.min(r,e.columnwidth.length-1)]:e.columnwidth;return a(n)?Number(n):1}),C=S.reduce(s,0);S=S.map(function(t){return t/C*v});var E=Math.max(o(e.header.line.width),o(e.cells.line.width)),L={key:e.index,translateX:m.x[0]*t._fullLayout._size.w,translateY:t._fullLayout._size.h*(1-m.y[1]),size:t._fullLayout._size,width:v,maxLineWidth:E,height:y,columnOrder:T,groupHeight:y,rowBlocks:M,headerRowBlocks:k,scrollY:0,cells:i({},e.cells,{values:r}),headerCells:i({},e.header,{values:g}),gdColumns:g.map(function(t){return t[0]}),gdColumnsOriginalOrder:g.map(function(t){return t[0]}),prevPages:[0,0],scrollbarState:{scrollbarScrollInProgress:!1},columns:g.map(function(t,e){var r=A[t];return A[t]=(r||0)+1,{key:t+"__"+A[t],label:t,specIndex:e,xIndex:T[e],xScale:u,x:void 0,calcdata:void 0,columnWidth:S[e]}})};return L.columns.forEach(function(t){t.calcdata=L,t.x=u(t)}),L}},{"../../lib/extend":649,"./constants":1079,"fast-isnumeric":197}],1081:[function(t,e,r){"use strict";var n=t("../../lib/extend").extendFlat;r.splitToPanels=function(t){var e=[0,0],r=n({},t,{key:"header",type:"header",page:0,prevPages:e,currentRepaint:[null,null],dragHandle:!0,values:t.calcdata.headerCells.values[t.specIndex],rowBlocks:t.calcdata.headerRowBlocks,calcdata:n({},t.calcdata,{cells:t.calcdata.headerCells})});return[n({},t,{key:"cells1",type:"cells",page:0,prevPages:e,currentRepaint:[null,null],dragHandle:!1,values:t.calcdata.cells.values[t.specIndex],rowBlocks:t.calcdata.rowBlocks}),n({},t,{key:"cells2",type:"cells",page:1,prevPages:e,currentRepaint:[null,null],dragHandle:!1,values:t.calcdata.cells.values[t.specIndex],rowBlocks:t.calcdata.rowBlocks}),r]},r.splitToCells=function(t){var e=function(t){var e=t.rowBlocks[t.page],r=e?e.rows[0].rowIndex:0,n=e?r+e.rows.length:0;return[r,n]}(t);return(t.values||[]).slice(e[0],e[1]).map(function(r,n){return{keyWithinBlock:n+("string"==typeof r&&r.match(/[<$&> ]/)?"_keybuster_"+Math.random():""),key:e[0]+n,column:t,calcdata:t.calcdata,page:t.page,rowBlocks:t.rowBlocks,value:r}})}},{"../../lib/extend":649}],1082:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("./attributes"),a=t("../../plots/domain").defaults;e.exports=function(t,e,r,o){function s(r,a){return n.coerce(t,e,i,r,a)}a(e,o,s),s("columnwidth"),s("header.values"),s("header.format"),s("header.align"),s("header.prefix"),s("header.suffix"),s("header.height"),s("header.line.width"),s("header.line.color"),s("header.fill.color"),n.coerceFont(s,"header.font",n.extendFlat({},o.font)),function(t,e){for(var r=t.columnorder||[],n=t.header.values.length,i=r.slice(0,n),a=i.slice().sort(function(t,e){return t-e}),o=i.map(function(t){return a.indexOf(t)}),s=o.length;s/i),l=!o||s;t.mayHaveMarkup=o&&a.match(/[<&>]/);var c,u="string"==typeof(c=a)&&c.match(n.latexCheck);t.latex=u;var f,h,p=u?"":_(t.calcdata.cells.prefix,e,r)||"",d=u?"":_(t.calcdata.cells.suffix,e,r)||"",g=u?null:_(t.calcdata.cells.format,e,r)||null,m=p+(g?i.format(g)(t.value):t.value)+d;if(t.wrappingNeeded=!t.wrapped&&!l&&!u&&(f=b(m)),t.cellHeightMayIncrease=s||u||t.mayHaveMarkup||(void 0===f?b(m):f),t.needsConvertToTspans=t.mayHaveMarkup||t.wrappingNeeded||t.latex,t.wrappingNeeded){var v=(" "===n.wrapSplitCharacter?m.replace(/i&&n.push(a),i+=l}return n}(i,l,s);1===c.length&&(c[0]===i.length-1?c.unshift(c[0]-1):c.push(c[0]+1)),c[0]%2&&c.reverse(),e.each(function(t,e){t.page=c[e],t.scrollY=l}),e.attr("transform",function(t){return"translate(0 "+(D(t.rowBlocks,t.page)-t.scrollY)+")"}),t&&(C(t,r,e,c,n.prevPages,n,0),C(t,r,e,c,n.prevPages,n,1),v(r,t))}}function S(t,e,r,a){return function(o){var s=o.calcdata?o.calcdata:o,l=e.filter(function(t){return s.key===t.key}),c=r||s.scrollbarState.dragMultiplier;s.scrollY=void 0===a?s.scrollY+c*i.event.dy:a;var u=l.selectAll("."+n.cn.yColumn).selectAll("."+n.cn.columnBlock).filter(k);T(t,u,l)}}function C(t,e,r,n,i,a,o){n[o]!==i[o]&&(clearTimeout(a.currentRepaint[o]),a.currentRepaint[o]=setTimeout(function(){var a=r.filter(function(t,e){return e===o&&n[e]!==i[e]});y(t,e,a,r),i[o]=n[o]}))}function E(t,e,r){return function(){var a=i.select(e.parentNode);a.each(function(t){var e=t.fragments;a.selectAll("tspan.line").each(function(t,r){e[r].width=this.getComputedTextLength()});var r,i,o=e[e.length-1].width,s=e.slice(0,-1),l=[],c=0,u=t.column.columnWidth-2*n.cellPad;for(t.value="";s.length;)c+(i=(r=s.shift()).width+o)>u&&(t.value+=l.join(n.wrapSpacer)+n.lineBreaker,l=[],c=0),l.push(r.text),c+=i;c&&(t.value+=l.join(n.wrapSpacer)),t.wrapped=!0}),a.selectAll("tspan.line").remove(),x(a.select("."+n.cn.cellText),r,t),i.select(e.parentNode.parentNode).call(P)}}function L(t,e,r,a,o){return function(){if(!o.settledY){var s=i.select(e.parentNode),l=R(o),c=o.key-l.firstRowIndex,u=l.rows[c].rowHeight,f=o.cellHeightMayIncrease?e.parentNode.getBoundingClientRect().height+2*n.cellPad:u,h=Math.max(f,u);h-l.rows[c].rowHeight&&(l.rows[c].rowHeight=h,t.selectAll("."+n.cn.columnCell).call(P),T(null,t.filter(k),0),v(r,a,!0)),s.attr("transform",function(){var t=this.parentNode.getBoundingClientRect(),e=i.select(this.parentNode).select("."+n.cn.cellRect).node().getBoundingClientRect(),r=this.transform.baseVal.consolidate(),a=e.top-t.top+(r?r.matrix.f:n.cellPad);return"translate("+z(o,i.select(this.parentNode).select("."+n.cn.cellTextHolder).node().getBoundingClientRect().width)+" "+a+")"}),o.settledY=!0}}}function z(t,e){switch(t.align){case"left":return n.cellPad;case"right":return t.column.columnWidth-(e||0)-n.cellPad;case"center":return(t.column.columnWidth-(e||0))/2;default:return n.cellPad}}function P(t){t.attr("transform",function(t){var e=t.rowBlocks[0].auxiliaryBlocks.reduce(function(t,e){return t+O(e,1/0)},0);return"translate(0 "+(O(R(t),t.key)+e)+")"}).selectAll("."+n.cn.cellRect).attr("height",function(t){return(e=R(t),r=t.key,e.rows[r-e.firstRowIndex]).rowHeight;var e,r})}function D(t,e){for(var r=0,n=e-1;n>=0;n--)r+=I(t[n]);return r}function O(t,e){for(var r=0,n=0;n0){var y,x,b,_,w,k=t.xa,M=t.ya;"h"===h.orientation?(w=e,y="y",b=M,x="x",_=k):(w=r,y="x",b=k,x="y",_=M);var A=f[t.index];if(w>=A.span[0]&&w<=A.span[1]){var T=n.extendFlat({},t),S=_.c2p(w,!0),C=o.getKdeValue(A,h,w),E=o.getPositionOnKdePath(A,h,S),L=b._offset,z=b._length;T[y+"0"]=E[0],T[y+"1"]=E[1],T[x+"0"]=T[x+"1"]=S,T[x+"Label"]=x+": "+i.hoverLabelText(_,w)+", "+f[0].t.labels.kde+" "+C.toFixed(3),T.spikeDistance=v[0].spikeDistance;var P=y+"Spike";T[P]=v[0][P],v[0].spikeDistance=void 0,v[0][P]=void 0,m.push(T),(u={stroke:t.color})[y+"1"]=n.constrain(L+E[0],L,L+z),u[y+"2"]=n.constrain(L+E[1],L,L+z),u[x+"1"]=u[x+"2"]=_._offset+S}}}-1!==p.indexOf("points")&&(c=a.hoverOnPoints(t,e,r));var D=l.selectAll(".violinline-"+h.uid).data(u?[0]:[]);return D.enter().append("line").classed("violinline-"+h.uid,!0).attr("stroke-width",1.5),D.exit().remove(),D.attr(u),"closest"===s?c?[c]:m:c?(m.push(c),m):m}},{"../../lib":660,"../../plots/cartesian/axes":706,"../box/hover":816,"./helpers":1088}],1090:[function(t,e,r){"use strict";e.exports={attributes:t("./attributes"),layoutAttributes:t("./layout_attributes"),supplyDefaults:t("./defaults"),supplyLayoutDefaults:t("./layout_defaults"),calc:t("./calc"),setPositions:t("./set_positions"),plot:t("./plot"),style:t("./style"),styleOnSelect:t("../scatter/style").styleOnSelect,hoverPoints:t("./hover"),selectPoints:t("../box/select"),moduleType:"trace",name:"violin",basePlotModule:t("../../plots/cartesian"),categories:["cartesian","svg","symbols","oriented","box-violin","showLegend","violinLayout","zoomScale"],meta:{}}},{"../../plots/cartesian":717,"../box/select":821,"../scatter/style":1011,"./attributes":1085,"./calc":1086,"./defaults":1087,"./hover":1089,"./layout_attributes":1091,"./layout_defaults":1092,"./plot":1093,"./set_positions":1094,"./style":1095}],1091:[function(t,e,r){"use strict";var n=t("../box/layout_attributes"),i=t("../../lib").extendFlat;e.exports={violinmode:i({},n.boxmode,{}),violingap:i({},n.boxgap,{}),violingroupgap:i({},n.boxgroupgap,{})}},{"../../lib":660,"../box/layout_attributes":818}],1092:[function(t,e,r){"use strict";var n=t("../../lib"),i=t("./layout_attributes"),a=t("../box/layout_defaults");e.exports=function(t,e,r){a._supply(t,e,r,function(r,a){return n.coerce(t,e,i,r,a)},"violin")}},{"../../lib":660,"../box/layout_defaults":819,"./layout_attributes":1091}],1093:[function(t,e,r){"use strict";var n=t("d3"),i=t("../../lib"),a=t("../../components/drawing"),o=t("../box/plot"),s=t("../scatter/line_points"),l=t("./helpers");e.exports=function(t,e,r,c){var u=t._fullLayout,f=e.xaxis,h=e.yaxis;function p(t){var e=s(t,{xaxis:f,yaxis:h,connectGaps:!0,baseTolerance:.75,shape:"spline",simplify:!0});return a.smoothopen(e[0],1)}var d=c.selectAll("g.trace.violins").data(r,function(t){return t[0].trace.uid});d.enter().append("g").attr("class","trace violins"),d.exit().remove(),d.order(),d.each(function(t){var r=t[0],a=r.t,s=r.trace,c=n.select(this);e.isRangePlot||(r.node3=c);var d=u._numViolins,g="group"===u.violinmode&&d>1,m=1-u.violingap,v=a.bdPos=a.dPos*m*(1-u.violingroupgap)/(g?d:1),y=a.bPos=g?2*a.dPos*((a.num+.5)/d-.5)*m:0;if(a.wHover=a.dPos*(g?m/d:1),!0!==s.visible||a.empty)n.select(this).remove();else{var x=e[a.valLetter+"axis"],b=e[a.posLetter+"axis"],_="both"===s.side,w=_||"positive"===s.side,k=_||"negative"===s.side,M=s.box&&s.box.visible,A=s.meanline&&s.meanline.visible,T=u._violinScaleGroupStats[s.scalegroup],S=c.selectAll("path.violin").data(i.identity);if(S.enter().append("path").style("vector-effect","non-scaling-stroke").attr("class","violin"),S.exit().remove(),S.each(function(t){var e,r,i,o,l,c,u,f,h=n.select(this),d=t.density,g=d.length,m=t.pos+y,M=b.c2p(m);switch(s.scalemode){case"width":e=T.maxWidth/v;break;case"count":e=T.maxWidth/v*(T.maxCount/t.pts.length)}if(w){for(u=new Array(g),l=0;la&&(a=u,o=c)}}return a?i(o):s};case"rms":return function(t,e){for(var r=0,a=0,o=0;o":return function(t){return h(t)>s};case">=":return function(t){return h(t)>=s};case"[]":return function(t){var e=h(t);return e>=s[0]&&e<=s[1]};case"()":return function(t){var e=h(t);return e>s[0]&&e=s[0]&&es[0]&&e<=s[1]};case"][":return function(t){var e=h(t);return e<=s[0]||e>=s[1]};case")(":return function(t){var e=h(t);return es[1]};case"](":return function(t){var e=h(t);return e<=s[0]||e>s[1]};case")[":return function(t){var e=h(t);return e=s[1]};case"{}":return function(t){return-1!==s.indexOf(h(t))};case"}{":return function(t){return-1===s.indexOf(h(t))}}}(r,a.getDataToCoordFunc(t,e,s,i),h),x={},b={},_=0;d?(m=function(t){x[t.astr]=n.extendDeep([],t.get()),t.set(new Array(f))},v=function(t,e){var r=x[t.astr][e];t.get()[e]=r}):(m=function(t){x[t.astr]=n.extendDeep([],t.get()),t.set([])},v=function(t,e){var r=x[t.astr][e];t.get().push(r)}),M(m);for(var w=o(e.transforms,r),k=0;k1?"%{group} (%{trace})":"%{group}");var l=t.styles,c=o.styles=[];if(l)for(a=0;a (GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,description:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,description:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",description:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,description:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,description:"Open all links in new windows",type:"boolean"},backslashEscapesHTMLTags:{defaultValue:!1,description:"Support for HTML Tag escaping. ex:
foo
",type:"boolean"},emoji:{defaultValue:!1,description:"Enable emoji support. Ex: `this is a :smile: emoji`",type:"boolean"},underline:{defaultValue:!1,description:"Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `` and ``",type:"boolean"},completeHTMLDocument:{defaultValue:!1,description:"Outputs a complete html document, including ``, `` and `` tags",type:"boolean"},metadata:{defaultValue:!1,description:"Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).",type:"boolean"},splitAdjacentBlockquotes:{defaultValue:!1,description:"Split adjacent blockquote blocks",type:"boolean"}};if(!1===e)return JSON.parse(JSON.stringify(r));var t={};for(var a in r)r.hasOwnProperty(a)&&(t[a]=r[a].defaultValue);return t}function r(e,r){"use strict";var t=r?"Error in "+r+" extension->":"Error in unnamed extension",n={valid:!0,error:""};a.helper.isArray(e)||(e=[e]);for(var s=0;s-1,p=new RegExp(r+"|"+t,"g"+c.replace(/g/g,"")),d=new RegExp(r,c.replace(/g/g,"")),h=[];do{for(n=0;o=p.exec(e);)if(d.test(o[0]))n++||(i=(s=p.lastIndex)-o[0].length);else if(n&&!--n){l=o.index+o[0].length;var _={left:{start:i,end:s},match:{start:s,end:o.index},right:{start:o.index,end:l},wholeMatch:{start:i,end:l}};if(h.push(_),!u)return h}}while(n&&(p.lastIndex=s));return h};a.helper.matchRecursiveRegExp=function(e,r,t,a){"use strict";for(var n=c(e,r,t,a),s=[],o=0;o0){var p=[];0!==i[0].wholeMatch.start&&p.push(e.slice(0,i[0].wholeMatch.start));for(var d=0;d=0?n+(t||0):n},a.helper.splitAtIndex=function(e,r){"use strict";if(!a.helper.isString(e))throw"InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string";return[e.substring(0,r),e.substring(r)]},a.helper.encodeEmailAddress=function(e){"use strict";var r=[function(e){return"&#"+e.charCodeAt(0)+";"},function(e){return"&#x"+e.charCodeAt(0).toString(16)+";"},function(e){return e}];return e=e.replace(/./g,function(e){if("@"===e)e=r[Math.floor(2*Math.random())](e);else{var t=Math.random();e=t>.9?r[2](e):t>.45?r[1](e):r[0](e)}return e})},"undefined"==typeof console&&(console={warn:function(e){"use strict";alert(e)},log:function(e){"use strict";alert(e)},error:function(e){"use strict";throw e}}),a.helper.regexes={asteriskDashAndColon:/([*_:~])/g},a.helper.emojis={"+1":"ðŸ‘","-1":"👎",100:"💯",1234:"🔢","1st_place_medal":"🥇","2nd_place_medal":"🥈","3rd_place_medal":"🥉","8ball":"🎱",a:"🅰ï¸",ab:"🆎",abc:"🔤",abcd:"🔡",accept:"🉑",aerial_tramway:"🚡",airplane:"✈ï¸",alarm_clock:"â°",alembic:"âš—ï¸",alien:"👽",ambulance:"🚑",amphora:"ðŸº",anchor:"âš“ï¸",angel:"👼",anger:"💢",angry:"😠",anguished:"😧",ant:"ðŸœ",apple:"ðŸŽ",aquarius:"â™’ï¸",aries:"♈ï¸",arrow_backward:"â—€ï¸",arrow_double_down:"â¬",arrow_double_up:"â«",arrow_down:"⬇ï¸",arrow_down_small:"🔽",arrow_forward:"â–¶ï¸",arrow_heading_down:"⤵ï¸",arrow_heading_up:"⤴ï¸",arrow_left:"⬅ï¸",arrow_lower_left:"↙ï¸",arrow_lower_right:"↘ï¸",arrow_right:"âž¡ï¸",arrow_right_hook:"↪ï¸",arrow_up:"⬆ï¸",arrow_up_down:"↕ï¸",arrow_up_small:"🔼",arrow_upper_left:"↖ï¸",arrow_upper_right:"↗ï¸",arrows_clockwise:"🔃",arrows_counterclockwise:"🔄",art:"🎨",articulated_lorry:"🚛",artificial_satellite:"🛰",astonished:"😲",athletic_shoe:"👟",atm:"ðŸ§",atom_symbol:"âš›ï¸",avocado:"🥑",b:"🅱ï¸",baby:"👶",baby_bottle:"ðŸ¼",baby_chick:"ðŸ¤",baby_symbol:"🚼",back:"🔙",bacon:"🥓",badminton:"ðŸ¸",baggage_claim:"🛄",baguette_bread:"🥖",balance_scale:"âš–ï¸",balloon:"🎈",ballot_box:"🗳",ballot_box_with_check:"☑ï¸",bamboo:"ðŸŽ",banana:"ðŸŒ",bangbang:"‼ï¸",bank:"ðŸ¦",bar_chart:"📊",barber:"💈",baseball:"âš¾ï¸",basketball:"ðŸ€",basketball_man:"⛹ï¸",basketball_woman:"⛹ï¸‍♀ï¸",bat:"🦇",bath:"🛀",bathtub:"ðŸ›",battery:"🔋",beach_umbrella:"ðŸ–",bear:"ðŸ»",bed:"ðŸ›",bee:"ðŸ",beer:"ðŸº",beers:"ðŸ»",beetle:"ðŸž",beginner:"🔰",bell:"🔔",bellhop_bell:"🛎",bento:"ðŸ±",biking_man:"🚴",bike:"🚲",biking_woman:"🚴‍♀ï¸",bikini:"👙",biohazard:"☣ï¸",bird:"ðŸ¦",birthday:"🎂",black_circle:"âš«ï¸",black_flag:"ðŸ´",black_heart:"🖤",black_joker:"ðŸƒ",black_large_square:"⬛ï¸",black_medium_small_square:"â—¾ï¸",black_medium_square:"â—¼ï¸",black_nib:"✒ï¸",black_small_square:"â–ªï¸",black_square_button:"🔲",blonde_man:"👱",blonde_woman:"👱‍♀ï¸",blossom:"🌼",blowfish:"ðŸ¡",blue_book:"📘",blue_car:"🚙",blue_heart:"💙",blush:"😊",boar:"ðŸ—",boat:"⛵ï¸",bomb:"💣",book:"📖",bookmark:"🔖",bookmark_tabs:"📑",books:"📚",boom:"💥",boot:"👢",bouquet:"ðŸ’",bowing_man:"🙇",bow_and_arrow:"ðŸ¹",bowing_woman:"🙇‍♀ï¸",bowling:"🎳",boxing_glove:"🥊",boy:"👦",bread:"ðŸž",bride_with_veil:"👰",bridge_at_night:"🌉",briefcase:"💼",broken_heart:"💔",bug:"ðŸ›",building_construction:"ðŸ—",bulb:"💡",bullettrain_front:"🚅",bullettrain_side:"🚄",burrito:"🌯",bus:"🚌",business_suit_levitating:"🕴",busstop:"ðŸš",bust_in_silhouette:"👤",busts_in_silhouette:"👥",butterfly:"🦋",cactus:"🌵",cake:"ðŸ°",calendar:"📆",call_me_hand:"🤙",calling:"📲",camel:"ðŸ«",camera:"📷",camera_flash:"📸",camping:"ðŸ•",cancer:"♋ï¸",candle:"🕯",candy:"ðŸ¬",canoe:"🛶",capital_abcd:"🔠",capricorn:"♑ï¸",car:"🚗",card_file_box:"🗃",card_index:"📇",card_index_dividers:"🗂",carousel_horse:"🎠",carrot:"🥕",cat:"ðŸ±",cat2:"ðŸˆ",cd:"💿",chains:"⛓",champagne:"ðŸ¾",chart:"💹",chart_with_downwards_trend:"📉",chart_with_upwards_trend:"📈",checkered_flag:"ðŸ",cheese:"🧀",cherries:"ðŸ’",cherry_blossom:"🌸",chestnut:"🌰",chicken:"ðŸ”",children_crossing:"🚸",chipmunk:"ðŸ¿",chocolate_bar:"ðŸ«",christmas_tree:"🎄",church:"⛪ï¸",cinema:"🎦",circus_tent:"🎪",city_sunrise:"🌇",city_sunset:"🌆",cityscape:"ðŸ™",cl:"🆑",clamp:"🗜",clap:"ðŸ‘",clapper:"🎬",classical_building:"ðŸ›",clinking_glasses:"🥂",clipboard:"📋",clock1:"ðŸ•",clock10:"🕙",clock1030:"🕥",clock11:"🕚",clock1130:"🕦",clock12:"🕛",clock1230:"🕧",clock130:"🕜",clock2:"🕑",clock230:"ðŸ•",clock3:"🕒",clock330:"🕞",clock4:"🕓",clock430:"🕟",clock5:"🕔",clock530:"🕠",clock6:"🕕",clock630:"🕡",clock7:"🕖",clock730:"🕢",clock8:"🕗",clock830:"🕣",clock9:"🕘",clock930:"🕤",closed_book:"📕",closed_lock_with_key:"ðŸ”",closed_umbrella:"🌂",cloud:"â˜ï¸",cloud_with_lightning:"🌩",cloud_with_lightning_and_rain:"⛈",cloud_with_rain:"🌧",cloud_with_snow:"🌨",clown_face:"🤡",clubs:"♣ï¸",cocktail:"ðŸ¸",coffee:"☕ï¸",coffin:"âš°ï¸",cold_sweat:"😰",comet:"☄ï¸",computer:"💻",computer_mouse:"🖱",confetti_ball:"🎊",confounded:"😖",confused:"😕",congratulations:"㊗ï¸",construction:"🚧",construction_worker_man:"👷",construction_worker_woman:"👷‍♀ï¸",control_knobs:"🎛",convenience_store:"ðŸª",cookie:"ðŸª",cool:"🆒",policeman:"👮",copyright:"©ï¸",corn:"🌽",couch_and_lamp:"🛋",couple:"👫",couple_with_heart_woman_man:"💑",couple_with_heart_man_man:"👨‍â¤ï¸‍👨",couple_with_heart_woman_woman:"👩‍â¤ï¸‍👩",couplekiss_man_man:"👨‍â¤ï¸‍💋‍👨",couplekiss_man_woman:"ðŸ’",couplekiss_woman_woman:"👩‍â¤ï¸‍💋‍👩",cow:"ðŸ®",cow2:"ðŸ„",cowboy_hat_face:"🤠",crab:"🦀",crayon:"ðŸ–",credit_card:"💳",crescent_moon:"🌙",cricket:"ðŸ",crocodile:"ðŸŠ",croissant:"ðŸ¥",crossed_fingers:"🤞",crossed_flags:"🎌",crossed_swords:"âš”ï¸",crown:"👑",cry:"😢",crying_cat_face:"😿",crystal_ball:"🔮",cucumber:"🥒",cupid:"💘",curly_loop:"âž°",currency_exchange:"💱",curry:"ðŸ›",custard:"ðŸ®",customs:"🛃",cyclone:"🌀",dagger:"🗡",dancer:"💃",dancing_women:"👯",dancing_men:"👯‍♂ï¸",dango:"ðŸ¡",dark_sunglasses:"🕶",dart:"🎯",dash:"💨",date:"📅",deciduous_tree:"🌳",deer:"🦌",department_store:"ðŸ¬",derelict_house:"ðŸš",desert:"ðŸœ",desert_island:"ðŸ",desktop_computer:"🖥",male_detective:"🕵ï¸",diamond_shape_with_a_dot_inside:"💠",diamonds:"♦ï¸",disappointed:"😞",disappointed_relieved:"😥",dizzy:"💫",dizzy_face:"😵",do_not_litter:"🚯",dog:"ðŸ¶",dog2:"ðŸ•",dollar:"💵",dolls:"🎎",dolphin:"ðŸ¬",door:"🚪",doughnut:"ðŸ©",dove:"🕊",dragon:"ðŸ‰",dragon_face:"ðŸ²",dress:"👗",dromedary_camel:"ðŸª",drooling_face:"🤤",droplet:"💧",drum:"ðŸ¥",duck:"🦆",dvd:"📀","e-mail":"📧",eagle:"🦅",ear:"👂",ear_of_rice:"🌾",earth_africa:"ðŸŒ",earth_americas:"🌎",earth_asia:"ðŸŒ",egg:"🥚",eggplant:"ðŸ†",eight_pointed_black_star:"✴ï¸",eight_spoked_asterisk:"✳ï¸",electric_plug:"🔌",elephant:"ðŸ˜",email:"✉ï¸",end:"🔚",envelope_with_arrow:"📩",euro:"💶",european_castle:"ðŸ°",european_post_office:"ðŸ¤",evergreen_tree:"🌲",exclamation:"â—ï¸",expressionless:"😑",eye:"ðŸ‘",eye_speech_bubble:"ðŸ‘‍🗨",eyeglasses:"👓",eyes:"👀",face_with_head_bandage:"🤕",face_with_thermometer:"🤒",fist_oncoming:"👊",factory:"ðŸ­",fallen_leaf:"ðŸ‚",family_man_woman_boy:"👪",family_man_boy:"👨‍👦",family_man_boy_boy:"👨‍👦‍👦",family_man_girl:"👨‍👧",family_man_girl_boy:"👨‍👧‍👦",family_man_girl_girl:"👨‍👧‍👧",family_man_man_boy:"👨‍👨‍👦",family_man_man_boy_boy:"👨‍👨‍👦‍👦",family_man_man_girl:"👨‍👨‍👧",family_man_man_girl_boy:"👨‍👨‍👧‍👦",family_man_man_girl_girl:"👨‍👨‍👧‍👧",family_man_woman_boy_boy:"👨‍👩‍👦‍👦",family_man_woman_girl:"👨‍👩‍👧",family_man_woman_girl_boy:"👨‍👩‍👧‍👦",family_man_woman_girl_girl:"👨‍👩‍👧‍👧",family_woman_boy:"👩‍👦",family_woman_boy_boy:"👩‍👦‍👦",family_woman_girl:"👩‍👧",family_woman_girl_boy:"👩‍👧‍👦",family_woman_girl_girl:"👩‍👧‍👧",family_woman_woman_boy:"👩‍👩‍👦",family_woman_woman_boy_boy:"👩‍👩‍👦‍👦",family_woman_woman_girl:"👩‍👩‍👧",family_woman_woman_girl_boy:"👩‍👩‍👧‍👦",family_woman_woman_girl_girl:"👩‍👩‍👧‍👧",fast_forward:"â©",fax:"📠",fearful:"😨",feet:"ðŸ¾",female_detective:"🕵ï¸‍♀ï¸",ferris_wheel:"🎡",ferry:"â›´",field_hockey:"ðŸ‘",file_cabinet:"🗄",file_folder:"ðŸ“",film_projector:"📽",film_strip:"🎞",fire:"🔥",fire_engine:"🚒",fireworks:"🎆",first_quarter_moon:"🌓",first_quarter_moon_with_face:"🌛",fish:"ðŸŸ",fish_cake:"ðŸ¥",fishing_pole_and_fish:"🎣",fist_raised:"✊",fist_left:"🤛",fist_right:"🤜",flags:"ðŸŽ",flashlight:"🔦",fleur_de_lis:"âšœï¸",flight_arrival:"🛬",flight_departure:"🛫",floppy_disk:"💾",flower_playing_cards:"🎴",flushed:"😳",fog:"🌫",foggy:"ðŸŒ",football:"ðŸˆ",footprints:"👣",fork_and_knife:"ðŸ´",fountain:"⛲ï¸",fountain_pen:"🖋",four_leaf_clover:"ðŸ€",fox_face:"🦊",framed_picture:"🖼",free:"🆓",fried_egg:"ðŸ³",fried_shrimp:"ðŸ¤",fries:"ðŸŸ",frog:"ðŸ¸",frowning:"😦",frowning_face:"☹ï¸",frowning_man:"ðŸ™‍♂ï¸",frowning_woman:"ðŸ™",middle_finger:"🖕",fuelpump:"⛽ï¸",full_moon:"🌕",full_moon_with_face:"ðŸŒ",funeral_urn:"âš±ï¸",game_die:"🎲",gear:"âš™ï¸",gem:"💎",gemini:"♊ï¸",ghost:"👻",gift:"ðŸŽ",gift_heart:"ðŸ’",girl:"👧",globe_with_meridians:"ðŸŒ",goal_net:"🥅",goat:"ðŸ",golf:"⛳ï¸",golfing_man:"ðŸŒï¸",golfing_woman:"ðŸŒï¸‍♀ï¸",gorilla:"ðŸ¦",grapes:"ðŸ‡",green_apple:"ðŸ",green_book:"📗",green_heart:"💚",green_salad:"🥗",grey_exclamation:"â•",grey_question:"â”",grimacing:"😬",grin:"ðŸ˜",grinning:"😀",guardsman:"💂",guardswoman:"💂‍♀ï¸",guitar:"🎸",gun:"🔫",haircut_woman:"💇",haircut_man:"💇‍♂ï¸",hamburger:"ðŸ”",hammer:"🔨",hammer_and_pick:"âš’",hammer_and_wrench:"🛠",hamster:"ðŸ¹",hand:"✋",handbag:"👜",handshake:"ðŸ¤",hankey:"💩",hatched_chick:"ðŸ¥",hatching_chick:"ðŸ£",headphones:"🎧",hear_no_evil:"🙉",heart:"â¤ï¸",heart_decoration:"💟",heart_eyes:"ðŸ˜",heart_eyes_cat:"😻",heartbeat:"💓",heartpulse:"💗",hearts:"♥ï¸",heavy_check_mark:"✔ï¸",heavy_division_sign:"âž—",heavy_dollar_sign:"💲",heavy_heart_exclamation:"â£ï¸",heavy_minus_sign:"âž–",heavy_multiplication_x:"✖ï¸",heavy_plus_sign:"âž•",helicopter:"ðŸš",herb:"🌿",hibiscus:"🌺",high_brightness:"🔆",high_heel:"👠",hocho:"🔪",hole:"🕳",honey_pot:"ðŸ¯",horse:"ðŸ´",horse_racing:"ðŸ‡",hospital:"ðŸ¥",hot_pepper:"🌶",hotdog:"🌭",hotel:"ðŸ¨",hotsprings:"♨ï¸",hourglass:"⌛ï¸",hourglass_flowing_sand:"â³",house:"ðŸ ",house_with_garden:"ðŸ¡",houses:"ðŸ˜",hugs:"🤗",hushed:"😯",ice_cream:"ðŸ¨",ice_hockey:"ðŸ’",ice_skate:"⛸",icecream:"ðŸ¦",id:"🆔",ideograph_advantage:"ðŸ‰",imp:"👿",inbox_tray:"📥",incoming_envelope:"📨",tipping_hand_woman:"ðŸ’",information_source:"ℹï¸",innocent:"😇",interrobang:"â‰ï¸",iphone:"📱",izakaya_lantern:"ðŸ®",jack_o_lantern:"🎃",japan:"🗾",japanese_castle:"ðŸ¯",japanese_goblin:"👺",japanese_ogre:"👹",jeans:"👖",joy:"😂",joy_cat:"😹",joystick:"🕹",kaaba:"🕋",key:"🔑",keyboard:"⌨ï¸",keycap_ten:"🔟",kick_scooter:"🛴",kimono:"👘",kiss:"💋",kissing:"😗",kissing_cat:"😽",kissing_closed_eyes:"😚",kissing_heart:"😘",kissing_smiling_eyes:"😙",kiwi_fruit:"ðŸ¥",koala:"ðŸ¨",koko:"ðŸˆ",label:"ðŸ·",large_blue_circle:"🔵",large_blue_diamond:"🔷",large_orange_diamond:"🔶",last_quarter_moon:"🌗",last_quarter_moon_with_face:"🌜",latin_cross:"âœï¸",laughing:"😆",leaves:"ðŸƒ",ledger:"📒",left_luggage:"🛅",left_right_arrow:"↔ï¸",leftwards_arrow_with_hook:"↩ï¸",lemon:"ðŸ‹",leo:"♌ï¸",leopard:"ðŸ†",level_slider:"🎚",libra:"♎ï¸",light_rail:"🚈",link:"🔗",lion:"ðŸ¦",lips:"👄",lipstick:"💄",lizard:"🦎",lock:"🔒",lock_with_ink_pen:"ðŸ”",lollipop:"ðŸ­",loop:"âž¿",loud_sound:"🔊",loudspeaker:"📢",love_hotel:"ðŸ©",love_letter:"💌",low_brightness:"🔅",lying_face:"🤥",m:"â“‚ï¸",mag:"ðŸ”",mag_right:"🔎",mahjong:"🀄ï¸",mailbox:"📫",mailbox_closed:"📪",mailbox_with_mail:"📬",mailbox_with_no_mail:"📭",man:"👨",man_artist:"👨‍🎨",man_astronaut:"👨‍🚀",man_cartwheeling:"🤸‍♂ï¸",man_cook:"👨‍ðŸ³",man_dancing:"🕺",man_facepalming:"🤦‍♂ï¸",man_factory_worker:"👨‍ðŸ­",man_farmer:"👨‍🌾",man_firefighter:"👨‍🚒",man_health_worker:"👨‍âš•ï¸",man_in_tuxedo:"🤵",man_judge:"👨‍âš–ï¸",man_juggling:"🤹‍♂ï¸",man_mechanic:"👨‍🔧",man_office_worker:"👨‍💼",man_pilot:"👨‍✈ï¸",man_playing_handball:"🤾‍♂ï¸",man_playing_water_polo:"🤽‍♂ï¸",man_scientist:"👨‍🔬",man_shrugging:"🤷‍♂ï¸",man_singer:"👨‍🎤",man_student:"👨‍🎓",man_teacher:"👨‍ðŸ«",man_technologist:"👨‍💻",man_with_gua_pi_mao:"👲",man_with_turban:"👳",tangerine:"ðŸŠ",mans_shoe:"👞",mantelpiece_clock:"🕰",maple_leaf:"ðŸ",martial_arts_uniform:"🥋",mask:"😷",massage_woman:"💆",massage_man:"💆‍♂ï¸",meat_on_bone:"ðŸ–",medal_military:"🎖",medal_sports:"ðŸ…",mega:"📣",melon:"ðŸˆ",memo:"ðŸ“",men_wrestling:"🤼‍♂ï¸",menorah:"🕎",mens:"🚹",metal:"🤘",metro:"🚇",microphone:"🎤",microscope:"🔬",milk_glass:"🥛",milky_way:"🌌",minibus:"ðŸš",minidisc:"💽",mobile_phone_off:"📴",money_mouth_face:"🤑",money_with_wings:"💸",moneybag:"💰",monkey:"ðŸ’",monkey_face:"ðŸµ",monorail:"ðŸš",moon:"🌔",mortar_board:"🎓",mosque:"🕌",motor_boat:"🛥",motor_scooter:"🛵",motorcycle:"ðŸ",motorway:"🛣",mount_fuji:"🗻",mountain:"â›°",mountain_biking_man:"🚵",mountain_biking_woman:"🚵‍♀ï¸",mountain_cableway:"🚠",mountain_railway:"🚞",mountain_snow:"ðŸ”",mouse:"ðŸ­",mouse2:"ðŸ",movie_camera:"🎥",moyai:"🗿",mrs_claus:"🤶",muscle:"💪",mushroom:"ðŸ„",musical_keyboard:"🎹",musical_note:"🎵",musical_score:"🎼",mute:"🔇",nail_care:"💅",name_badge:"📛",national_park:"ðŸž",nauseated_face:"🤢",necktie:"👔",negative_squared_cross_mark:"âŽ",nerd_face:"🤓",neutral_face:"ðŸ˜",new:"🆕",new_moon:"🌑",new_moon_with_face:"🌚",newspaper:"📰",newspaper_roll:"🗞",next_track_button:"â­",ng:"🆖",no_good_man:"🙅‍♂ï¸",no_good_woman:"🙅",night_with_stars:"🌃",no_bell:"🔕",no_bicycles:"🚳",no_entry:"â›”ï¸",no_entry_sign:"🚫",no_mobile_phones:"📵",no_mouth:"😶",no_pedestrians:"🚷",no_smoking:"🚭","non-potable_water":"🚱",nose:"👃",notebook:"📓",notebook_with_decorative_cover:"📔",notes:"🎶",nut_and_bolt:"🔩",o:"â­•ï¸",o2:"🅾ï¸",ocean:"🌊",octopus:"ðŸ™",oden:"ðŸ¢",office:"ðŸ¢",oil_drum:"🛢",ok:"🆗",ok_hand:"👌",ok_man:"🙆‍♂ï¸",ok_woman:"🙆",old_key:"ðŸ—",older_man:"👴",older_woman:"👵",om:"🕉",on:"🔛",oncoming_automobile:"🚘",oncoming_bus:"ðŸš",oncoming_police_car:"🚔",oncoming_taxi:"🚖",open_file_folder:"📂",open_hands:"ðŸ‘",open_mouth:"😮",open_umbrella:"☂ï¸",ophiuchus:"⛎",orange_book:"📙",orthodox_cross:"☦ï¸",outbox_tray:"📤",owl:"🦉",ox:"ðŸ‚",package:"📦",page_facing_up:"📄",page_with_curl:"📃",pager:"📟",paintbrush:"🖌",palm_tree:"🌴",pancakes:"🥞",panda_face:"ðŸ¼",paperclip:"📎",paperclips:"🖇",parasol_on_ground:"â›±",parking:"🅿ï¸",part_alternation_mark:"〽ï¸",partly_sunny:"â›…ï¸",passenger_ship:"🛳",passport_control:"🛂",pause_button:"â¸",peace_symbol:"☮ï¸",peach:"ðŸ‘",peanuts:"🥜",pear:"ðŸ",pen:"🖊",pencil2:"âœï¸",penguin:"ðŸ§",pensive:"😔",performing_arts:"🎭",persevere:"😣",person_fencing:"🤺",pouting_woman:"🙎",phone:"☎ï¸",pick:"â›",pig:"ðŸ·",pig2:"ðŸ–",pig_nose:"ðŸ½",pill:"💊",pineapple:"ðŸ",ping_pong:"ðŸ“",pisces:"♓ï¸",pizza:"ðŸ•",place_of_worship:"ðŸ›",plate_with_cutlery:"ðŸ½",play_or_pause_button:"â¯",point_down:"👇",point_left:"👈",point_right:"👉",point_up:"â˜ï¸",point_up_2:"👆",police_car:"🚓",policewoman:"👮‍♀ï¸",poodle:"ðŸ©",popcorn:"ðŸ¿",post_office:"ðŸ£",postal_horn:"📯",postbox:"📮",potable_water:"🚰",potato:"🥔",pouch:"ðŸ‘",poultry_leg:"ðŸ—",pound:"💷",rage:"😡",pouting_cat:"😾",pouting_man:"🙎‍♂ï¸",pray:"ðŸ™",prayer_beads:"📿",pregnant_woman:"🤰",previous_track_button:"â®",prince:"🤴",princess:"👸",printer:"🖨",purple_heart:"💜",purse:"👛",pushpin:"📌",put_litter_in_its_place:"🚮",question:"â“",rabbit:"ðŸ°",rabbit2:"ðŸ‡",racehorse:"ðŸŽ",racing_car:"ðŸŽ",radio:"📻",radio_button:"🔘",radioactive:"☢ï¸",railway_car:"🚃",railway_track:"🛤",rainbow:"🌈",rainbow_flag:"ðŸ³ï¸‍🌈",raised_back_of_hand:"🤚",raised_hand_with_fingers_splayed:"ðŸ–",raised_hands:"🙌",raising_hand_woman:"🙋",raising_hand_man:"🙋‍♂ï¸",ram:"ðŸ",ramen:"ðŸœ",rat:"ðŸ€",record_button:"âº",recycle:"â™»ï¸",red_circle:"🔴",registered:"®ï¸",relaxed:"☺ï¸",relieved:"😌",reminder_ribbon:"🎗",repeat:"ðŸ”",repeat_one:"🔂",rescue_worker_helmet:"⛑",restroom:"🚻",revolving_hearts:"💞",rewind:"âª",rhinoceros:"ðŸ¦",ribbon:"🎀",rice:"ðŸš",rice_ball:"ðŸ™",rice_cracker:"ðŸ˜",rice_scene:"🎑",right_anger_bubble:"🗯",ring:"ðŸ’",robot:"🤖",rocket:"🚀",rofl:"🤣",roll_eyes:"🙄",roller_coaster:"🎢",rooster:"ðŸ“",rose:"🌹",rosette:"ðŸµ",rotating_light:"🚨",round_pushpin:"ðŸ“",rowing_man:"🚣",rowing_woman:"🚣‍♀ï¸",rugby_football:"ðŸ‰",running_man:"ðŸƒ",running_shirt_with_sash:"🎽",running_woman:"ðŸƒ‍♀ï¸",sa:"🈂ï¸",sagittarius:"â™ï¸",sake:"ðŸ¶",sandal:"👡",santa:"🎅",satellite:"📡",saxophone:"🎷",school:"ðŸ«",school_satchel:"🎒",scissors:"✂ï¸",scorpion:"🦂",scorpius:"â™ï¸",scream:"😱",scream_cat:"🙀",scroll:"📜",seat:"💺",secret:"㊙ï¸",see_no_evil:"🙈",seedling:"🌱",selfie:"🤳",shallow_pan_of_food:"🥘",shamrock:"☘ï¸",shark:"🦈",shaved_ice:"ðŸ§",sheep:"ðŸ‘",shell:"ðŸš",shield:"🛡",shinto_shrine:"⛩",ship:"🚢",shirt:"👕",shopping:"ðŸ›",shopping_cart:"🛒",shower:"🚿",shrimp:"ðŸ¦",signal_strength:"📶",six_pointed_star:"🔯",ski:"🎿",skier:"â›·",skull:"💀",skull_and_crossbones:"☠ï¸",sleeping:"😴",sleeping_bed:"🛌",sleepy:"😪",slightly_frowning_face:"ðŸ™",slightly_smiling_face:"🙂",slot_machine:"🎰",small_airplane:"🛩",small_blue_diamond:"🔹",small_orange_diamond:"🔸",small_red_triangle:"🔺",small_red_triangle_down:"🔻",smile:"😄",smile_cat:"😸",smiley:"😃",smiley_cat:"😺",smiling_imp:"😈",smirk:"ðŸ˜",smirk_cat:"😼",smoking:"🚬",snail:"ðŸŒ",snake:"ðŸ",sneezing_face:"🤧",snowboarder:"ðŸ‚",snowflake:"â„ï¸",snowman:"⛄ï¸",snowman_with_snow:"☃ï¸",sob:"😭",soccer:"âš½ï¸",soon:"🔜",sos:"🆘",sound:"🔉",space_invader:"👾",spades:"â™ ï¸",spaghetti:"ðŸ",sparkle:"â‡ï¸",sparkler:"🎇",sparkles:"✨",sparkling_heart:"💖",speak_no_evil:"🙊",speaker:"🔈",speaking_head:"🗣",speech_balloon:"💬",speedboat:"🚤",spider:"🕷",spider_web:"🕸",spiral_calendar:"🗓",spiral_notepad:"🗒",spoon:"🥄",squid:"🦑",stadium:"ðŸŸ",star:"â­ï¸",star2:"🌟",star_and_crescent:"☪ï¸",star_of_david:"✡ï¸",stars:"🌠",station:"🚉",statue_of_liberty:"🗽",steam_locomotive:"🚂",stew:"ðŸ²",stop_button:"â¹",stop_sign:"🛑",stopwatch:"â±",straight_ruler:"ðŸ“",strawberry:"ðŸ“",stuck_out_tongue:"😛",stuck_out_tongue_closed_eyes:"ðŸ˜",stuck_out_tongue_winking_eye:"😜",studio_microphone:"🎙",stuffed_flatbread:"🥙",sun_behind_large_cloud:"🌥",sun_behind_rain_cloud:"🌦",sun_behind_small_cloud:"🌤",sun_with_face:"🌞",sunflower:"🌻",sunglasses:"😎",sunny:"☀ï¸",sunrise:"🌅",sunrise_over_mountains:"🌄",surfing_man:"ðŸ„",surfing_woman:"ðŸ„‍♀ï¸",sushi:"ðŸ£",suspension_railway:"🚟",sweat:"😓",sweat_drops:"💦",sweat_smile:"😅",sweet_potato:"ðŸ ",swimming_man:"ðŸŠ",swimming_woman:"ðŸŠ‍♀ï¸",symbols:"🔣",synagogue:"ðŸ•",syringe:"💉",taco:"🌮",tada:"🎉",tanabata_tree:"🎋",taurus:"♉ï¸",taxi:"🚕",tea:"ðŸµ",telephone_receiver:"📞",telescope:"🔭",tennis:"🎾",tent:"⛺ï¸",thermometer:"🌡",thinking:"🤔",thought_balloon:"💭",ticket:"🎫",tickets:"🎟",tiger:"ðŸ¯",tiger2:"ðŸ…",timer_clock:"â²",tipping_hand_man:"ðŸ’‍♂ï¸",tired_face:"😫",tm:"â„¢ï¸",toilet:"🚽",tokyo_tower:"🗼",tomato:"ðŸ…",tongue:"👅",top:"ðŸ”",tophat:"🎩",tornado:"🌪",trackball:"🖲",tractor:"🚜",traffic_light:"🚥",train:"🚋",train2:"🚆",tram:"🚊",triangular_flag_on_post:"🚩",triangular_ruler:"ðŸ“",trident:"🔱",triumph:"😤",trolleybus:"🚎",trophy:"ðŸ†",tropical_drink:"ðŸ¹",tropical_fish:"ðŸ ",truck:"🚚",trumpet:"🎺",tulip:"🌷",tumbler_glass:"🥃",turkey:"🦃",turtle:"ðŸ¢",tv:"📺",twisted_rightwards_arrows:"🔀",two_hearts:"💕",two_men_holding_hands:"👬",two_women_holding_hands:"👭",u5272:"🈹",u5408:"🈴",u55b6:"🈺",u6307:"🈯ï¸",u6708:"🈷ï¸",u6709:"🈶",u6e80:"🈵",u7121:"🈚ï¸",u7533:"🈸",u7981:"🈲",u7a7a:"🈳",umbrella:"☔ï¸",unamused:"😒",underage:"🔞",unicorn:"🦄",unlock:"🔓",up:"🆙",upside_down_face:"🙃",v:"✌ï¸",vertical_traffic_light:"🚦",vhs:"📼",vibration_mode:"📳",video_camera:"📹",video_game:"🎮",violin:"🎻",virgo:"â™ï¸",volcano:"🌋",volleyball:"ðŸ",vs:"🆚",vulcan_salute:"🖖",walking_man:"🚶",walking_woman:"🚶‍♀ï¸",waning_crescent_moon:"🌘",waning_gibbous_moon:"🌖",warning:"âš ï¸",wastebasket:"🗑",watch:"⌚ï¸",water_buffalo:"ðŸƒ",watermelon:"ðŸ‰",wave:"👋",wavy_dash:"〰ï¸",waxing_crescent_moon:"🌒",wc:"🚾",weary:"😩",wedding:"💒",weight_lifting_man:"ðŸ‹ï¸",weight_lifting_woman:"ðŸ‹ï¸‍♀ï¸",whale:"ðŸ³",whale2:"ðŸ‹",wheel_of_dharma:"☸ï¸",wheelchair:"♿ï¸",white_check_mark:"✅",white_circle:"⚪ï¸",white_flag:"ðŸ³ï¸",white_flower:"💮",white_large_square:"⬜ï¸",white_medium_small_square:"â—½ï¸",white_medium_square:"â—»ï¸",white_small_square:"â–«ï¸",white_square_button:"🔳",wilted_flower:"🥀",wind_chime:"ðŸŽ",wind_face:"🌬",wine_glass:"ðŸ·",wink:"😉",wolf:"ðŸº",woman:"👩",woman_artist:"👩‍🎨",woman_astronaut:"👩‍🚀",woman_cartwheeling:"🤸‍♀ï¸",woman_cook:"👩‍ðŸ³",woman_facepalming:"🤦‍♀ï¸",woman_factory_worker:"👩‍ðŸ­",woman_farmer:"👩‍🌾",woman_firefighter:"👩‍🚒",woman_health_worker:"👩‍âš•ï¸",woman_judge:"👩‍âš–ï¸",woman_juggling:"🤹‍♀ï¸",woman_mechanic:"👩‍🔧",woman_office_worker:"👩‍💼",woman_pilot:"👩‍✈ï¸",woman_playing_handball:"🤾‍♀ï¸",woman_playing_water_polo:"🤽‍♀ï¸",woman_scientist:"👩‍🔬",woman_shrugging:"🤷‍♀ï¸",woman_singer:"👩‍🎤",woman_student:"👩‍🎓",woman_teacher:"👩‍ðŸ«",woman_technologist:"👩‍💻",woman_with_turban:"👳‍♀ï¸",womans_clothes:"👚",womans_hat:"👒",women_wrestling:"🤼‍♀ï¸",womens:"🚺",world_map:"🗺",worried:"😟",wrench:"🔧",writing_hand:"âœï¸",x:"âŒ",yellow_heart:"💛",yen:"💴",yin_yang:"☯ï¸",yum:"😋",zap:"âš¡ï¸",zipper_mouth_face:"ðŸ¤",zzz:"💤",octocat:':octocat:',showdown:"S"},a.Converter=function(e){"use strict";function t(e,t){if(t=t||null,a.helper.isString(e)){if(e=a.helper.stdExtName(e),t=e,a.extensions[e])return console.warn("DEPRECATION WARNING: "+e+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!"),void function(e,t){"function"==typeof e&&(e=e(new a.Converter));a.helper.isArray(e)||(e=[e]);var n=r(e,t);if(!n.valid)throw Error(n.error);for(var s=0;s? ?(['"].*['"])?\)$/m)>-1)o="";else if(!o){if(s||(s=n.toLowerCase().replace(/ ?\n/g," ")),o="#"+s,a.helper.isUndefined(t.gUrls[s]))return e;o=t.gUrls[s],a.helper.isUndefined(t.gTitles[s])||(c=t.gTitles[s])}var u='
"};return e=(e=t.converter._dispatch("anchors.before",e,r,t)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g,n),e=e.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,n),e=e.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]??(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,n),e=e.replace(/\[([^\[\]]+)]()()()()()/g,n),r.ghMentions&&(e=e.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d.-]+?[a-z\d]+)*))/gim,function(e,t,n,s,o){if("\\"===n)return t+s;if(!a.helper.isString(r.ghMentionsLink))throw new Error("ghMentionsLink option must be a string");var i=r.ghMentionsLink.replace(/\{u}/g,o),l="";return r.openLinksInNewWindow&&(l=' target="¨E95Eblank"'),t+'"+s+""})),e=t.converter._dispatch("anchors.after",e,r,t)});var u=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+?\.[^'">\s]+?)()(\1)?(?=\s|$)(?!["<>])/gi,p=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi,d=/()<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>()/gi,h=/(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gim,_=/<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,g=function(e){"use strict";return function(r,t,n,s,o,i,l){var c=n=n.replace(a.helper.regexes.asteriskDashAndColon,a.helper.escapeCharactersCallback),u="",p="",d=t||"",h=l||"";return/^www\./i.test(n)&&(n=n.replace(/^www\./i,"http://www.")),e.excludeTrailingPunctuationFromURLs&&i&&(u=i),e.openLinksInNewWindow&&(p=' target="¨E95Eblank"'),d+'"+c+""+u+h}},m=function(e,r){"use strict";return function(t,n,s){var o="mailto:";return n=n||"",s=a.subParser("unescapeSpecialChars")(s,e,r),e.encodeEmails?(o=a.helper.encodeEmailAddress(o+s),s=a.helper.encodeEmailAddress(s)):o+=s,n+''+s+""}};a.subParser("autoLinks",function(e,r,t){"use strict";return e=t.converter._dispatch("autoLinks.before",e,r,t),e=e.replace(d,g(r)),e=e.replace(_,m(r,t)),e=t.converter._dispatch("autoLinks.after",e,r,t)}),a.subParser("simplifiedAutoLinks",function(e,r,t){"use strict";return r.simplifiedAutoLink?(e=t.converter._dispatch("simplifiedAutoLinks.before",e,r,t),e=r.excludeTrailingPunctuationFromURLs?e.replace(p,g(r)):e.replace(u,g(r)),e=e.replace(h,m(r,t)),e=t.converter._dispatch("simplifiedAutoLinks.after",e,r,t)):e}),a.subParser("blockGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("blockGamut.before",e,r,t),e=a.subParser("blockQuotes")(e,r,t),e=a.subParser("headers")(e,r,t),e=a.subParser("horizontalRule")(e,r,t),e=a.subParser("lists")(e,r,t),e=a.subParser("codeBlocks")(e,r,t),e=a.subParser("tables")(e,r,t),e=a.subParser("hashHTMLBlocks")(e,r,t),e=a.subParser("paragraphs")(e,r,t),e=t.converter._dispatch("blockGamut.after",e,r,t)}),a.subParser("blockQuotes",function(e,r,t){"use strict";e=t.converter._dispatch("blockQuotes.before",e,r,t),e+="\n\n";var n=/(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm;return r.splitAdjacentBlockquotes&&(n=/^ {0,3}>[\s\S]*?(?:\n\n)/gm),e=e.replace(n,function(e){return e=e.replace(/^[ \t]*>[ \t]?/gm,""),e=e.replace(/¨0/g,""),e=e.replace(/^[ \t]+$/gm,""),e=a.subParser("githubCodeBlocks")(e,r,t),e=a.subParser("blockGamut")(e,r,t),e=e.replace(/(^|\n)/g,"$1 "),e=e.replace(/(\s*
[^\r]+?<\/pre>)/gm,function(e,r){var t=r;return t=t.replace(/^  /gm,"¨0"),t=t.replace(/¨0/g,"")}),a.subParser("hashBlock")("
\n"+e+"\n
",r,t)}),e=t.converter._dispatch("blockQuotes.after",e,r,t)}),a.subParser("codeBlocks",function(e,r,t){"use strict";e=t.converter._dispatch("codeBlocks.before",e,r,t);return e=(e+="¨0").replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g,function(e,n,s){var o=n,i=s,l="\n";return o=a.subParser("outdent")(o,r,t),o=a.subParser("encodeCode")(o,r,t),o=a.subParser("detab")(o,r,t),o=o.replace(/^\n+/g,""),o=o.replace(/\n+$/g,""),r.omitExtraWLInCodeBlocks&&(l=""),o="
"+o+l+"
",a.subParser("hashBlock")(o,r,t)+i}),e=e.replace(/¨0/,""),e=t.converter._dispatch("codeBlocks.after",e,r,t)}),a.subParser("codeSpans",function(e,r,t){"use strict";return void 0===(e=t.converter._dispatch("codeSpans.before",e,r,t))&&(e=""),e=e.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(e,n,s,o){var i=o;return i=i.replace(/^([ \t]*)/g,""),i=i.replace(/[ \t]*$/g,""),i=a.subParser("encodeCode")(i,r,t),i=n+""+i+"",i=a.subParser("hashHTMLSpans")(i,r,t)}),e=t.converter._dispatch("codeSpans.after",e,r,t)}),a.subParser("completeHTMLDocument",function(e,r,t){"use strict";if(!r.completeHTMLDocument)return e;e=t.converter._dispatch("completeHTMLDocument.before",e,r,t);var a="html",n="\n",s="",o='\n',i="",l="";void 0!==t.metadata.parsed.doctype&&(n="\n","html"!==(a=t.metadata.parsed.doctype.toString().toLowerCase())&&"html5"!==a||(o=''));for(var c in t.metadata.parsed)if(t.metadata.parsed.hasOwnProperty(c))switch(c.toLowerCase()){case"doctype":break;case"title":s=""+t.metadata.parsed.title+"\n";break;case"charset":o="html"===a||"html5"===a?'\n':'\n';break;case"language":case"lang":i=' lang="'+t.metadata.parsed[c]+'"',l+='\n';break;default:l+='\n'}return e=n+"\n\n"+s+o+l+"\n\n"+e.trim()+"\n\n",e=t.converter._dispatch("completeHTMLDocument.after",e,r,t)}),a.subParser("detab",function(e,r,t){"use strict";return e=t.converter._dispatch("detab.before",e,r,t),e=e.replace(/\t(?=\t)/g," "),e=e.replace(/\t/g,"¨A¨B"),e=e.replace(/¨B(.+?)¨A/g,function(e,r){for(var t=r,a=4-t.length%4,n=0;n/g,">"),e=t.converter._dispatch("encodeAmpsAndAngles.after",e,r,t)}),a.subParser("encodeBackslashEscapes",function(e,r,t){"use strict";return e=t.converter._dispatch("encodeBackslashEscapes.before",e,r,t),e=e.replace(/\\(\\)/g,a.helper.escapeCharactersCallback),e=e.replace(/\\([`*_{}\[\]()>#+.!~=|-])/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeBackslashEscapes.after",e,r,t)}),a.subParser("encodeCode",function(e,r,t){"use strict";return e=t.converter._dispatch("encodeCode.before",e,r,t),e=e.replace(/&/g,"&").replace(//g,">").replace(/([*_{}\[\]\\=~-])/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeCode.after",e,r,t)}),a.subParser("escapeSpecialCharsWithinTagAttributes",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.before",e,r,t)).replace(/<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,function(e){return e.replace(/(.)<\/?code>(?=.)/g,"$1`").replace(/([\\`*_~=|])/g,a.helper.escapeCharactersCallback)}),e=e.replace(/-]|-[^>])(?:[^-]|-[^-])*)--)>/gi,function(e){return e.replace(/([\\`*_~=|])/g,a.helper.escapeCharactersCallback)}),e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.after",e,r,t)}),a.subParser("githubCodeBlocks",function(e,r,t){"use strict";return r.ghCodeBlocks?(e=t.converter._dispatch("githubCodeBlocks.before",e,r,t),e+="¨0",e=e.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g,function(e,n,s,o){var i=r.omitExtraWLInCodeBlocks?"":"\n";return o=a.subParser("encodeCode")(o,r,t),o=a.subParser("detab")(o,r,t),o=o.replace(/^\n+/g,""),o=o.replace(/\n+$/g,""),o="
"+o+i+"
",o=a.subParser("hashBlock")(o,r,t),"\n\n¨G"+(t.ghCodeBlocks.push({text:e,codeblock:o})-1)+"G\n\n"}),e=e.replace(/¨0/,""),t.converter._dispatch("githubCodeBlocks.after",e,r,t)):e}),a.subParser("hashBlock",function(e,r,t){"use strict";return e=t.converter._dispatch("hashBlock.before",e,r,t),e=e.replace(/(^\n+|\n+$)/g,""),e="\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n",e=t.converter._dispatch("hashBlock.after",e,r,t)}),a.subParser("hashCodeTags",function(e,r,t){"use strict";e=t.converter._dispatch("hashCodeTags.before",e,r,t);return e=a.helper.replaceRecursiveRegExp(e,function(e,n,s,o){var i=s+a.subParser("encodeCode")(n,r,t)+o;return"¨C"+(t.gHtmlSpans.push(i)-1)+"C"},"]*>","","gim"),e=t.converter._dispatch("hashCodeTags.after",e,r,t)}),a.subParser("hashElement",function(e,r,t){"use strict";return function(e,r){var a=r;return a=a.replace(/\n\n/g,"\n"),a=a.replace(/^\n/,""),a=a.replace(/\n+$/g,""),a="\n\n¨K"+(t.gHtmlBlocks.push(a)-1)+"K\n\n"}}),a.subParser("hashHTMLBlocks",function(e,r,t){"use strict";e=t.converter._dispatch("hashHTMLBlocks.before",e,r,t);var n=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"],s=function(e,r,a,n){var s=e;return-1!==a.search(/\bmarkdown\b/)&&(s=a+t.converter.makeHtml(r)+n),"\n\n¨K"+(t.gHtmlBlocks.push(s)-1)+"K\n\n"};r.backslashEscapesHTMLTags&&(e=e.replace(/\\<(\/?[^>]+?)>/g,function(e,r){return"<"+r+">"}));for(var o=0;o]*>)","im"),c="<"+n[o]+"\\b[^>]*>",u="";-1!==(i=a.helper.regexIndexOf(e,l));){var p=a.helper.splitAtIndex(e,i),d=a.helper.replaceRecursiveRegExp(p[1],s,c,u,"im");if(d===p[1])break;e=p[0].concat(d)}return e=e.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,a.subParser("hashElement")(e,r,t)),e=a.helper.replaceRecursiveRegExp(e,function(e){return"\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n"},"^ {0,3}\x3c!--","--\x3e","gm"),e=e.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,a.subParser("hashElement")(e,r,t)),e=t.converter._dispatch("hashHTMLBlocks.after",e,r,t)}),a.subParser("hashHTMLSpans",function(e,r,t){"use strict";function a(e){return"¨C"+(t.gHtmlSpans.push(e)-1)+"C"}return e=t.converter._dispatch("hashHTMLSpans.before",e,r,t),e=e.replace(/<[^>]+?\/>/gi,function(e){return a(e)}),e=e.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g,function(e){return a(e)}),e=e.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g,function(e){return a(e)}),e=e.replace(/<[^>]+?>/gi,function(e){return a(e)}),e=t.converter._dispatch("hashHTMLSpans.after",e,r,t)}),a.subParser("unhashHTMLSpans",function(e,r,t){"use strict";e=t.converter._dispatch("unhashHTMLSpans.before",e,r,t);for(var a=0;a]*>\\s*]*>","^ {0,3}\\s*
","gim"),e=t.converter._dispatch("hashPreCodeTags.after",e,r,t)}),a.subParser("headers",function(e,r,t){"use strict";function n(e){var n,s;if(r.customizedHeaderId){var o=e.match(/\{([^{]+?)}\s*$/);o&&o[1]&&(e=o[1])}return n=e,s=a.helper.isString(r.prefixHeaderId)?r.prefixHeaderId:!0===r.prefixHeaderId?"section-":"",r.rawPrefixHeaderId||(n=s+n),n=r.ghCompatibleHeaderId?n.replace(/ /g,"-").replace(/&/g,"").replace(/¨T/g,"").replace(/¨D/g,"").replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g,"").toLowerCase():r.rawHeaderId?n.replace(/ /g,"-").replace(/&/g,"&").replace(/¨T/g,"¨").replace(/¨D/g,"$").replace(/["']/g,"-").toLowerCase():n.replace(/[^\w]/g,"").toLowerCase(),r.rawPrefixHeaderId&&(n=s+n),t.hashLinkCounts[n]?n=n+"-"+t.hashLinkCounts[n]++:t.hashLinkCounts[n]=1,n}e=t.converter._dispatch("headers.before",e,r,t);var s=isNaN(parseInt(r.headerLevelStart))?1:parseInt(r.headerLevelStart),o=r.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,i=r.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm;e=(e=e.replace(o,function(e,o){var i=a.subParser("spanGamut")(o,r,t),l=r.noHeaderId?"":' id="'+n(o)+'"',c=""+i+"";return a.subParser("hashBlock")(c,r,t)})).replace(i,function(e,o){var i=a.subParser("spanGamut")(o,r,t),l=r.noHeaderId?"":' id="'+n(o)+'"',c=s+1,u=""+i+"";return a.subParser("hashBlock")(u,r,t)});var l=r.requireSpaceBeforeHeadingText?/^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm:/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;return e=e.replace(l,function(e,o,i){var l=i;r.customizedHeaderId&&(l=i.replace(/\s?\{([^{]+?)}\s*$/,""));var c=a.subParser("spanGamut")(l,r,t),u=r.noHeaderId?"":' id="'+n(i)+'"',p=s-1+o.length,d=""+c+"";return a.subParser("hashBlock")(d,r,t)}),e=t.converter._dispatch("headers.after",e,r,t)}),a.subParser("horizontalRule",function(e,r,t){"use strict";e=t.converter._dispatch("horizontalRule.before",e,r,t);var n=a.subParser("hashBlock")("
",r,t);return e=e.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,n),e=e.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,n),e=e.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,n),e=t.converter._dispatch("horizontalRule.after",e,r,t)}),a.subParser("images",function(e,r,t){"use strict";function n(e,r,n,s,o,i,l,c){var u=t.gUrls,p=t.gTitles,d=t.gDimensions;if(n=n.toLowerCase(),c||(c=""),e.search(/\(? ?(['"].*['"])?\)$/m)>-1)s="";else if(""===s||null===s){if(""!==n&&null!==n||(n=r.toLowerCase().replace(/ ?\n/g," ")),s="#"+n,a.helper.isUndefined(u[n]))return e;s=u[n],a.helper.isUndefined(p[n])||(c=p[n]),a.helper.isUndefined(d[n])||(o=d[n].width,i=d[n].height)}r=r.replace(/"/g,""").replace(a.helper.regexes.asteriskDashAndColon,a.helper.escapeCharactersCallback);var h=''+r+'"}return e=(e=t.converter._dispatch("images.before",e,r,t)).replace(/!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,n),e=e.replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,function(e,r,t,a,s,o,i,l){return a=a.replace(/\s/g,""),n(e,r,t,a,s,o,0,l)}),e=e.replace(/!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,n),e=e.replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,n),e=e.replace(/!\[([^\[\]]+)]()()()()()/g,n),e=t.converter._dispatch("images.after",e,r,t)}),a.subParser("italicsAndBold",function(e,r,t){"use strict";function a(e,r,t){return r+e+t}return e=t.converter._dispatch("italicsAndBold.before",e,r,t),e=r.literalMidWordUnderscores?(e=(e=e.replace(/\b___(\S[\s\S]*)___\b/g,function(e,r){return a(r,"","")})).replace(/\b__(\S[\s\S]*)__\b/g,function(e,r){return a(r,"","")})).replace(/\b_(\S[\s\S]*?)_\b/g,function(e,r){return a(r,"","")}):(e=(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/_([^\s_][\s\S]*?)_/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e}),e=r.literalMidWordAsterisks?(e=(e=e.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g,function(e,r,t){return a(t,r+"","")})).replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g,function(e,r,t){return a(t,r+"","")})).replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g,function(e,r,t){return a(t,r+"","")}):(e=(e=e.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/\*\*(\S[\s\S]*?)\*\*/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/\*([^\s*][\s\S]*?)\*/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e}),e=t.converter._dispatch("italicsAndBold.after",e,r,t)}),a.subParser("lists",function(e,r,t){"use strict";function n(e,n){t.gListLevel++,e=e.replace(/\n{2,}$/,"\n");var s=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,o=/\n[ \t]*\n(?!¨0)/.test(e+="¨0");return r.disableForced4SpacesIndentedSublists&&(s=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm),e=e.replace(s,function(e,n,s,i,l,c,u){u=u&&""!==u.trim();var p=a.subParser("outdent")(l,r,t),d="";return c&&r.tasklists&&(d=' class="task-list-item" style="list-style-type: none;"',p=p.replace(/^[ \t]*\[(x|X| )?]/m,function(){var e='-1?(p=a.subParser("githubCodeBlocks")(p,r,t),p=a.subParser("blockGamut")(p,r,t)):(p=(p=a.subParser("lists")(p,r,t)).replace(/\n$/,""),p=(p=a.subParser("hashHTMLBlocks")(p,r,t)).replace(/\n\n+/g,"\n\n"),p=o?a.subParser("paragraphs")(p,r,t):a.subParser("spanGamut")(p,r,t)),p=p.replace("¨A",""),p=""+p+"\n"}),e=e.replace(/¨0/g,""),t.gListLevel--,n&&(e=e.replace(/\s+$/,"")),e}function s(e,r){if("ol"===r){var t=e.match(/^ *(\d+)\./);if(t&&"1"!==t[1])return' start="'+t[1]+'"'}return""}function o(e,t,a){var o=r.disableForced4SpacesIndentedSublists?/^ ?\d+\.[ \t]/gm:/^ {0,3}\d+\.[ \t]/gm,i=r.disableForced4SpacesIndentedSublists?/^ ?[*+-][ \t]/gm:/^ {0,3}[*+-][ \t]/gm,l="ul"===t?o:i,c="";if(-1!==e.search(l))!function r(u){var p=u.search(l),d=s(e,t);-1!==p?(c+="\n\n<"+t+d+">\n"+n(u.slice(0,p),!!a)+"\n",l="ul"===(t="ul"===t?"ol":"ul")?o:i,r(u.slice(p))):c+="\n\n<"+t+d+">\n"+n(u,!!a)+"\n"}(e);else{var u=s(e,t);c="\n\n<"+t+u+">\n"+n(e,!!a)+"\n"}return c}return e=t.converter._dispatch("lists.before",e,r,t),e+="¨0",e=t.gListLevel?e.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t){return o(r,t.search(/[*+-]/g)>-1?"ul":"ol",!0)}):e.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t,a){return o(t,a.search(/[*+-]/g)>-1?"ul":"ol",!1)}),e=e.replace(/¨0/,""),e=t.converter._dispatch("lists.after",e,r,t)}),a.subParser("metadata",function(e,r,t){"use strict";function a(e){t.metadata.raw=e,(e=(e=e.replace(/&/g,"&").replace(/"/g,""")).replace(/\n {4}/g," ")).replace(/^([\S ]+): +([\s\S]+?)$/gm,function(e,r,a){return t.metadata.parsed[r]=a,""})}return r.metadata?(e=t.converter._dispatch("metadata.before",e,r,t),e=e.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/,function(e,r,t){return a(t),"¨M"}),e=e.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/,function(e,r,n){return r&&(t.metadata.format=r),a(n),"¨M"}),e=e.replace(/¨M/g,""),e=t.converter._dispatch("metadata.after",e,r,t)):e}),a.subParser("outdent",function(e,r,t){"use strict";return e=t.converter._dispatch("outdent.before",e,r,t),e=e.replace(/^(\t|[ ]{1,4})/gm,"¨0"),e=e.replace(/¨0/g,""),e=t.converter._dispatch("outdent.after",e,r,t)}),a.subParser("paragraphs",function(e,r,t){"use strict";for(var n=(e=(e=(e=t.converter._dispatch("paragraphs.before",e,r,t)).replace(/^\n+/g,"")).replace(/\n+$/g,"")).split(/\n{2,}/g),s=[],o=n.length,i=0;i=0?s.push(l):l.search(/\S/)>=0&&(l=(l=a.subParser("spanGamut")(l,r,t)).replace(/^([ \t]*)/g,"

"),l+="

",s.push(l))}for(o=s.length,i=0;i]*>\s*]*>/.test(u)&&(p=!0)}s[i]=u}return e=s.join("\n"),e=e.replace(/^\n+/g,""),e=e.replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),a.subParser("runExtension",function(e,r,t,a){"use strict";if(e.filter)r=e.filter(r,a.converter,t);else if(e.regex){var n=e.regex;n instanceof RegExp||(n=new RegExp(n,"g")),r=r.replace(n,e.replace)}return r}),a.subParser("spanGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("spanGamut.before",e,r,t),e=a.subParser("codeSpans")(e,r,t),e=a.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=a.subParser("encodeBackslashEscapes")(e,r,t),e=a.subParser("images")(e,r,t),e=a.subParser("anchors")(e,r,t),e=a.subParser("autoLinks")(e,r,t),e=a.subParser("simplifiedAutoLinks")(e,r,t),e=a.subParser("emoji")(e,r,t),e=a.subParser("underline")(e,r,t),e=a.subParser("italicsAndBold")(e,r,t),e=a.subParser("strikethrough")(e,r,t),e=a.subParser("ellipsis")(e,r,t),e=a.subParser("hashHTMLSpans")(e,r,t),e=a.subParser("encodeAmpsAndAngles")(e,r,t),r.simpleLineBreaks?/\n\n¨K/.test(e)||(e=e.replace(/\n+/g,"
\n")):e=e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),a.subParser("strikethrough",function(e,r,t){"use strict";return r.strikethrough&&(e=(e=t.converter._dispatch("strikethrough.before",e,r,t)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,n){return function(e){return r.simplifiedAutoLink&&(e=a.subParser("simplifiedAutoLinks")(e,r,t)),""+e+""}(n)}),e=t.converter._dispatch("strikethrough.after",e,r,t)),e}),a.subParser("stripLinkDefinitions",function(e,r,t){"use strict";var n=function(e,n,s,o,i,l,c){return n=n.toLowerCase(),s.match(/^data:.+?\/.+?;base64,/)?t.gUrls[n]=s.replace(/\s/g,""):t.gUrls[n]=a.subParser("encodeAmpsAndAngles")(s,r,t),l?l+c:(c&&(t.gTitles[n]=c.replace(/"|'/g,""")),r.parseImgDimensions&&o&&i&&(t.gDimensions[n]={width:o,height:i}),"")};return e=(e+="¨0").replace(/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm,n),e=e.replace(/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,n),e=e.replace(/¨0/,"")}),a.subParser("tables",function(e,r,t){"use strict";function n(e){return/^:[ \t]*--*$/.test(e)?' style="text-align:left;"':/^--*[ \t]*:[ \t]*$/.test(e)?' style="text-align:right;"':/^:[ \t]*--*[ \t]*:$/.test(e)?' style="text-align:center;"':""}function s(e,n){var s="";return e=e.trim(),(r.tablesHeaderId||r.tableHeaderId)&&(s=' id="'+e.replace(/ /g,"_").toLowerCase()+'"'),e=a.subParser("spanGamut")(e,r,t),""+e+"\n"}function o(e,n){return""+a.subParser("spanGamut")(e,r,t)+"\n"}function i(e){var i,l=e.split("\n");for(i=0;i\n\n\n",n=0;n\n";for(var s=0;s\n"}return t+="\n\n"}(d,_)}if(!r.tables)return e;return e=t.converter._dispatch("tables.before",e,r,t),e=e.replace(/\\(\|)/g,a.helper.escapeCharactersCallback),e=e.replace(/^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,i),e=e.replace(/^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm,i),e=t.converter._dispatch("tables.after",e,r,t)}),a.subParser("underline",function(e,r,t){"use strict";return r.underline?(e=t.converter._dispatch("underline.before",e,r,t),e=r.literalMidWordUnderscores?e.replace(/\b_?__(\S[\s\S]*)___?\b/g,function(e,r){return""+r+""}):e.replace(/_?__(\S[\s\S]*?)___?/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=e.replace(/(_)/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("underline.after",e,r,t)):e}),a.subParser("unescapeSpecialChars",function(e,r,t){"use strict";return e=t.converter._dispatch("unescapeSpecialChars.before",e,r,t),e=e.replace(/¨E(\d+)E/g,function(e,r){var t=parseInt(r);return String.fromCharCode(t)}),e=t.converter._dispatch("unescapeSpecialChars.after",e,r,t)});"function"==typeof define&&define.amd?define(function(){"use strict";return a}):"undefined"!=typeof module&&module.exports?module.exports=a:this.showdown=a}).call(this); +//# sourceMappingURL=showdown.min.js.map diff --git a/help/plot.html b/help/plot.html new file mode 100644 index 00000000..6edb50b2 --- /dev/null +++ b/help/plot.html @@ -0,0 +1,44 @@ + + + + + SPM Plot + + + +
+ + + diff --git a/man/Makefile b/man/Makefile index 8bb5cff7..b8f77f4b 100644 --- a/man/Makefile +++ b/man/Makefile @@ -1,9 +1,9 @@ #!/usr/bin/env make -f # Manual Makefile called by {SPM}/src/Makefile # -# Copyright (C) 2016-2017 Wellcome Trust Centre for Neuroimaging +# Copyright (C) 2016-2018 Wellcome Trust Centre for Neuroimaging # -# $Id: Makefile 7019 2017-02-16 10:49:48Z guillaume $ +# $Id: Makefile 7485 2018-11-14 10:31:57Z guillaume $ include ../src/Makefile.var @@ -37,7 +37,7 @@ manual.pdf: manual.tex spm_manual.tex spm_manual.tex: $(call verb, "Creating TEX files") - @ ${SPMEXE} eval "spm_jobman('initcfg');spm_make_manual;" + @ ${SPMEXE} eval "spm_jobman initcfg;spm_make_manual;" $(call verb, "TEX files: done") define makepdf diff --git a/man/dartelguide/dartelguide.tex b/man/dartelguide/dartelguide.tex index 3959101d..916da707 100644 --- a/man/dartelguide/dartelguide.tex +++ b/man/dartelguide/dartelguide.tex @@ -1,4 +1,7 @@ \chapter{Using Dartel \label{Chap:dartelguide}} +\minitoc + +\vskip 1.5cm Dartel\footnote{Dartel stands for ``Diffeomorphic Anatomical Registration Through Exponentiated Lie algebra''. It may not use a true Lie Algebra, but the acronym is a nice one.} is a suite of tools for achieving more accurate inter-subject registration of brain images. @@ -196,99 +199,99 @@ \section{Spatially normalising functional data to MNI space} } \end{itemize} -An alternative approach is now presented, which does not attempt to make optimal use of the available signal. - -\subsection{An alternative approach for using Dartel to spatially normalise to MNI Space} -During spatial normalisation of a brain image, some regions need to expanded and other regions need to contract in order to match the template. -If some structure is excessively shrunk by Dartel (because it has the freedom to estimate quite large deformations), then this will lead to a systematic reduction in the amount of BOLD signal being detected from that brain region. -For this reason, the normalise to MNI space option would generally be preferred when working with functional data that is to be smoothed. - -\subsubsection{Affine transform of Dartel template to MNI space} -Dartel works with images that are of average size. -When Dartel is used to generate an average shaped template (represented by a series of tissue probability maps) from a group of scans of various individuals, the result is of average size. -Brains normalised to MNI space are slightly larger than average. -In order to spatially normalise to MNI space, the deformation that maps from MNI space to the space of the group average is required. -Because the MNI space was derived by affine registration of a number of subjects to a common coordinate system, in most cases it should be possible to achieve a reasonable match of the template generated by Dartel using only an affine spatial normalisation. -This can be achieved by matching the grey matter component of the template with a grey matter tissue probability map in MNI space. -The spatial normalisation routine in SPM can be used to achieve this. - -\begin{itemize} -\item{{\bf Normalise: Estimate} - \begin{itemize} - \item{{\bf Data} - \begin{itemize} - \item{{\bf Subject} - \begin{itemize} - \item{{\bf Source Image}: Template\_6.nii,1 is usually the grey matter component of the final template of the series.} - \item{{\bf Source Weighting Image}: $<$None$>$} - \end{itemize} - } - \end{itemize} - } - \item{{\bf Estimation Options} - \begin{itemize} - \item{{\bf Template Image}: Should be the apriori/grey.nii file distributed in SPM.} - \item{{\bf Template Weighting Image}: $<$None$>$} - \item{{\bf Source Image Smoothing}: 8mm (the same as the apriori/grey.nii file has been smoothed).} - \item{{\bf Template Image Smoothing}: 0mm (because the data in the apriori folder are already smoothed by 8mm.)} - \item{{\bf Affine Regularisation}: Usually, you would specify ``ICBM space template''.} - \item{{\bf Nonlinear Frequency Cutoff}: Set this to infinity (enter ``Inf'') for affine registration.} - \item{{\bf Nonlinear Iterations}: Setting this to zero will also result in affine-only spatial normalisation.} - \item{{\bf Nonlinear Regularisation}: Setting this to infinity is another way of doing affine-only spatial normalisation.} - \end{itemize} - } - \end{itemize} -} -\end{itemize} - -For some populations of subjects, an affine transform may not be adequate for achieving good registration of the average shape to MNI space. -Nonlinear spatial normalisation may be more appropriate for these cases. -As ever, determining which procedure is better would involve a degree of empirical exploration. - -\subsubsection{Combining deformations} -Once you have the spatial transformation that maps from MNI space to the space of the Dartel template, it is possible to combine this with the DEFORMATIONS estimated by Dartel. -Rather than warping the image data twice (introducing interpolation artifacts each time), the two spatial transforms can be combined by composing them together. -The required deformation, for spatially normalising an individual to MNI space, is a mapping from MNI space to the individual image. -This is because the spatially normalised images are generated by scanning through the (initially empty) voxels in the spatially normalised image, and figuring out which voxels in the original image to sample from (as opposed to scanning through the original image and putting the values into the right places in the spatially normalised version). - -The desired mapping is from MNI space to Dartel template to individual scan. -If \emph{A} is the mapping from MNI to template, and \emph{B} is the mapping from template to individual, then this mapping is $B \circ A$, where ``$\circ$'' denotes the composition operation. -Spatially normalising via the composed deformations can be achieved through the \emph{Deformations} utility. - -\begin{itemize} -\item {\bf Deformations} - \begin{itemize} - \item {\bf Composition} - \begin{itemize} - \item {\bf Dartel flow} - \begin{itemize} - \item {\bf Flow field}: Specify the u\_rc1*.nii flow field for that subject. - \item {\bf Forward/Backwards}: This should be set to ``Backward'' to indicate a mapping from template to individual. - \item {\bf Time Steps}: This is the number of time steps used by the final iterations of the Dartel registration (usually 64). - \item {\bf Dartel template}: leave this field empty. - \end{itemize} - \item {\bf Imported \_sn.mat} - \begin{itemize} - \item {\bf Parameter File}: Select the spatial normalisation parameters that would spatially normalise the Template\_6.nii file. - \item {\bf Voxel sizes}: These are set to ``NaN'' (not a number) by default, which would take the voxel sizes for the apriori/grey.nii file. Alternatively, you could specify your favourite voxel sizes for spatially normalised images. - \item {\bf Bounding box}: Again, these are set to non-finite values by default, which results in the same bounding box as the apriori/grey.nii file. To specify your favourite bounding box, enter $[x_{min}, y_{min}, z_{min}; x_{max}, y_{max}, z_{max}]$ (in units of mm, relative to the AC). - \end{itemize} - \end{itemize} - \item {\bf Output} - \begin{itemize} - \item {\bf Pullback} - \begin{itemize} - \item {\bf Apply to}: Specify the images for that subject that you would like spatially normalised. - \item {\bf Output destination}: Specify where you want to write the images. - \item {\bf Interpolation}: Specify the form of interpolation. - \item {\bf Mask images}: Say whether you want to mask the images (see the Chapter on Realignment for more information here). - \item {\bf Gaussian FWHM}: The images can be smoothed when they are written. If you do not want this, then enter 0 0 0. - \end{itemize} - \end{itemize} - \end{itemize} -\end{itemize} - -The above procedure would be repeated for each subject in the study. +%An alternative approach is now presented, which does not attempt to make optimal use of the available signal. +% +%\subsection{An alternative approach for using Dartel to spatially normalise to MNI Space} +%During spatial normalisation of a brain image, some regions need to expanded and other regions need to contract in order to match the template. +%If some structure is excessively shrunk by Dartel (because it has the freedom to estimate quite large deformations), then this will lead to a systematic reduction in the amount of BOLD signal being detected from that brain region. +%For this reason, the normalise to MNI space option would generally be preferred when working with functional data that is to be smoothed. +% +%\subsubsection{Affine transform of Dartel template to MNI space} +%Dartel works with images that are of average size. +%When Dartel is used to generate an average shaped template (represented by a series of tissue probability maps) from a group of scans of various individuals, the result is of average size. +%Brains normalised to MNI space are slightly larger than average. +%In order to spatially normalise to MNI space, the deformation that maps from MNI space to the space of the group average is required. +%Because the MNI space was derived by affine registration of a number of subjects to a common coordinate system, in most cases it should be possible to achieve a reasonable match of the template generated by Dartel using only an affine spatial normalisation. +%This can be achieved by matching the grey matter component of the template with a grey matter tissue probability map in MNI space. +%The spatial normalisation routine in SPM can be used to achieve this. + +%\begin{itemize} +%\item{{\bf Normalise: Estimate} +% \begin{itemize} +% \item{{\bf Data} +% \begin{itemize} +% \item{{\bf Subject} +% \begin{itemize} +% \item{{\bf Source Image}: Template\_6.nii,1 is usually the grey matter component of the final template of the series.} +% \item{{\bf Source Weighting Image}: $<$None$>$} +% \end{itemize} +% } +% \end{itemize} +% } +% \item{{\bf Estimation Options} +% \begin{itemize} +% \item{{\bf Template Image}: Should be the apriori/grey.nii file distributed in SPM.} +% \item{{\bf Template Weighting Image}: $<$None$>$} +% \item{{\bf Source Image Smoothing}: 8mm (the same as the apriori/grey.nii file has been smoothed).} +% \item{{\bf Template Image Smoothing}: 0mm (because the data in the apriori folder are already smoothed by 8mm.)} +% \item{{\bf Affine Regularisation}: Usually, you would specify ``ICBM space template''.} +% \item{{\bf Nonlinear Frequency Cutoff}: Set this to infinity (enter ``Inf'') for affine registration.} +% \item{{\bf Nonlinear Iterations}: Setting this to zero will also result in affine-only spatial normalisation.} +% \item{{\bf Nonlinear Regularisation}: Setting this to infinity is another way of doing affine-only spatial normalisation.} +% \end{itemize} +% } +% \end{itemize} +%} +%\end{itemize} + +%For some populations of subjects, an affine transform may not be adequate for achieving good registration of the average shape to MNI space. +%Nonlinear spatial normalisation may be more appropriate for these cases. +%As ever, determining which procedure is better would involve a degree of empirical exploration. +% +%\subsubsection{Combining deformations} +%Once you have the spatial transformation that maps from MNI space to the space of the Dartel template, it is possible to combine this with the DEFORMATIONS estimated by Dartel. +%Rather than warping the image data twice (introducing interpolation artifacts each time), the two spatial transforms can be combined by composing them together. +%The required deformation, for spatially normalising an individual to MNI space, is a mapping from MNI space to the individual image. +%This is because the spatially normalised images are generated by scanning through the (initially empty) voxels in the spatially normalised image, and figuring out which voxels in the original image to sample from (as opposed to scanning through the original image and putting the values into the right places in the spatially normalised version). +% +%The desired mapping is from MNI space to Dartel template to individual scan. +%If \emph{A} is the mapping from MNI to template, and \emph{B} is the mapping from template to individual, then this mapping is $B \circ A$, where ``$\circ$'' denotes the composition operation. +%Spatially normalising via the composed deformations can be achieved through the \emph{Deformations} utility. +% +%\begin{itemize} +%\item {\bf Deformations} +% \begin{itemize} +% \item {\bf Composition} +% \begin{itemize} +% \item {\bf Dartel flow} +% \begin{itemize} +% \item {\bf Flow field}: Specify the u\_rc1*.nii flow field for that subject. +% \item {\bf Forward/Backwards}: This should be set to ``Backward'' to indicate a mapping from template to individual. +% \item {\bf Time Steps}: This is the number of time steps used by the final iterations of the Dartel registration (usually 64). +% \item {\bf Dartel template}: leave this field empty. +% \end{itemize} +% \item {\bf Imported \_sn.mat} +% \begin{itemize} +% \item {\bf Parameter File}: Select the spatial normalisation parameters that would spatially normalise the Template\_6.nii file. +% \item {\bf Voxel sizes}: These are set to ``NaN'' (not a number) by default, which would take the voxel sizes for the apriori/grey.nii file. Alternatively, you could specify your favourite voxel sizes for spatially normalised images. +% \item {\bf Bounding box}: Again, these are set to non-finite values by default, which results in the same bounding box as the apriori/grey.nii file. To specify your favourite bounding box, enter $[x_{min}, y_{min}, z_{min}; x_{max}, y_{max}, z_{max}]$ (in units of mm, relative to the AC). +% \end{itemize} +% \end{itemize} +% \item {\bf Output} +% \begin{itemize} +% \item {\bf Pullback} +% \begin{itemize} +% \item {\bf Apply to}: Specify the images for that subject that you would like spatially normalised. +% \item {\bf Output destination}: Specify where you want to write the images. +% \item {\bf Interpolation}: Specify the form of interpolation. +% \item {\bf Mask images}: Say whether you want to mask the images (see the Chapter on Realignment for more information here). +% \item {\bf Gaussian FWHM}: The images can be smoothed when they are written. If you do not want this, then enter 0 0 0. +% \end{itemize} +% \end{itemize} +% \end{itemize} +%\end{itemize} + +%The above procedure would be repeated for each subject in the study. \section{Warping Images to Existing Templates} If templates have already been created using Dartel, then it is possible to align other images with such templates. diff --git a/man/example_scripts/DCM_example_MMC_BGC.m b/man/example_scripts/DCM_example_MMC_BGT.m similarity index 50% rename from man/example_scripts/DCM_example_MMC_BGC.m rename to man/example_scripts/DCM_example_MMC_BGT.m index 11b80b43..8f233464 100644 --- a/man/example_scripts/DCM_example_MMC_BGC.m +++ b/man/example_scripts/DCM_example_MMC_BGT.m @@ -1,10 +1,11 @@ -% example script of an inversion of a MMC-BGC combined DCM with the generic DCM implementation +% example script for using the generic DCM routine +% based on an inversion of a MMC-BGT DCM as in van Wijk et al. 2018 Neuroimage % using spectral densities as data feature % 2 data channels: 1 beamformed MEG source, 1 LFP channel from STN % 2 conditions: OFF and ON medication % condition-related modulations allowed in all synaptic connections -% taking posteriors from grand average inversion as mean prior values for G, T, and A -% van Wijk et al. (submitted) +% optional: taking posteriors from grand average inversion as mean prior values for G, T, and A +% van Wijk et al. 2018 Neuroimage clear all, close all @@ -16,9 +17,9 @@ DCM.options.model(1).source = 'MMC'; % specify name of the first model (in same order as data) DCM.options.model(1).B = 1:14; % all intrinsic synaptic modulations can be modulated between conditions -DCM.options.model(2).source = 'BGC'; % specify name of the first model (in same order as data) +DCM.options.model(2).source = 'BGT'; % specify name of the first model (in same order as data) DCM.options.model(2).J = 5; % specify state that contributes to observed data: 1=Striatum, 3=GPe; 5=STN; 7=GPi; 9=Thalamus -DCM.options.model(2).B = [1:9]; % all intrinsic synaptic modulations can be modulated between conditions +DCM.options.model(2).B = 1:9; % all intrinsic synaptic modulations can be modulated between conditions DCM.xY.modality = 'LFP'; DCM.xY.Ic = 1:2; @@ -29,11 +30,6 @@ DCM.options.D = 1; DCM.options.spatial = 'LFP'; -%modulation of individual extrinsic connections - only works if corresponding DCM.Bs are set -DCM.options.model(1).Zf{1,1} = [0 1;0 0]; %single forward modulation -DCM.options.model(1).Zb{1,1} = [0 0;1 0]; %split backward modulation: (in)direct connection -DCM.options.model(1).Zb{2,1} = [0 0;1 0]; %split backward modulation: hyperdirect connection - DCM.A{1} = zeros(2,2); DCM.A{1}(1,2) = 1; %thalamus -> cortex modeled as forward connection @@ -49,32 +45,42 @@ DCM.xU.X = [0; 1]; %(0 = condition 1, 1 = condition 2) DCM.xU.name = {'ONvsOFF'}; -DCM.M.nodelay = 2; %eliminates all delay self-connections - -%%%%%%%%% +%%%%%%%% +%%% OPTIONAL: remove delays on self-connections +% DCM.M.nodelay = 2; %eliminates all delay self-connections +%%%%%%%% + +%%%%%%%% +%%% OPTIONAL: modulation of individual extrinsic connections +%%% By default, for models where a forward or backward connection contains two synaptic targets (like the CMC), DCM applies the same extrinsic modulation +%%% to both synaptic targets. The functionality here allows for separation into two distinct modulatory effects. +%%% only works if corresponding DCM.Bs are set +%%% NOTE: for this to work one needs a modified version of spm_gen_Q.m - please contact Bernadette +% DCM.options.model(1).Zf{1,1} = [0 1;0 0]; %single forward modulation +% DCM.options.model(1).Zb{1,1} = [0 0;1 0]; %split backward modulation: (in)direct connection +% DCM.options.model(1).Zb{2,1} = [0 0;1 0]; %split backward modulation: hyperdirect connection +%%%%%%%% + +%%%%%%%% %%% OPTIONAL: take priors means from posteriors of inversion grand average %%% (omit to use defaults prior means) -load('priors_grandaverage_OFF.mat'); - -DCM.M.MMC_T = MMC_T; -DCM.M.MMC_G = MMC_G; -DCM.M.BGC_T = BGC_T; -DCM.M.BGC_G = BGC_G; -DCM.M.BGC_E = BGC_E; -DCM.M.MMC_E = MMC_E; - -%%%%%%%%%% +% load('priors_grandaverage_OFF.mat'); +% DCM.M.MMC_T = MMC_T; +% DCM.M.MMC_G = MMC_G; +% DCM.M.BGT_T = BGT_T; +% DCM.M.BGT_G = BGT_G; +% DCM.M.BGT_E = BGT_E; +% DCM.M.MMT_E = MMT_E; +%%%%%%%%% DCM = spm_dcm_csd(DCM); %%%%%%%%% %%% OPTIONAL: use multiple runs to get out of early convergence / local minima -cnt=1; -while cnt<=10; - - DCM.M.P=DCM.Ep; - DCM = spm_dcm_csd(DCM); - - cnt=cnt+1; - disp([fnames{fn},' rep ',num2str(cnt)]); -end \ No newline at end of file +% cnt=1; +% while cnt<=8; +% DCM.M.P=DCM.Ep; +% DCM = spm_dcm_csd(DCM); +% cnt=cnt+1; +% disp([fnames{fn},' rep ',num2str(cnt)]); +% end \ No newline at end of file diff --git a/man/manual.pdf b/man/manual.pdf index bfe0bea7..50ff7b33 100644 Binary files a/man/manual.pdf and b/man/manual.pdf differ diff --git a/man/ppi/ppi.tex b/man/ppi/ppi.tex index db17dbc5..44c6b80a 100644 --- a/man/ppi/ppi.tex +++ b/man/ppi/ppi.tex @@ -233,16 +233,16 @@ \subsection{GLM analysis - Design setup and estimation} \item Under \textsc{Contrast Manager} click \textsc{Select \texttt{SPM.mat}} then click the \textsc{Dependency} button and choose \textsc{Model estimation: SPM.mat File} \item Click \textsc{Contrast Sessions} then click \textsc{New: F-contrast} once, and \textsc{New: T-contrast} twice from the \textsc{Current Item} box. -\item Click \textsc{Contrast vectors} and then \textsc{New: F contrast}. -\item The F contrast vector (named ``effects of interest'') can be entered as [eye(3), zeros(3,4)], which will produce: +\item Select \textsc{Weights matrix} underneath \textsc{F-contrast}. +\item The F weights matrix (named ``effects of interest'') can be entered as [eye(3), zeros(3,4)], which will produce: \begin{verbatim} 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 \end{verbatim} -\item For the first T-contrast, \textsc{Name} is \texttt{Attention}, and the \textsc{T contrast vector} is \texttt{~0~-1~1~0~0~0~0} (Note the order of the conditions in the design matrix is: Stationary, NoAttMot and AttMot). -\item For the second T-contrast \textsc{Name} is \texttt{Motion}, and the \textsc{T contrast vector} is: \texttt{-2~1~1~0~0~0~0}. +\item For the first T-contrast, \textsc{Name} is \texttt{Attention}, and the \textsc{T weights vector} is \texttt{~0~-1~1~0~0~0~0} (Note the order of the conditions in the design matrix is: Stationary, NoAttMot and AttMot). +\item For the second T-contrast \textsc{Name} is \texttt{Motion}, and the \textsc{T weights vector} is: \texttt{-2~1~1~0~0~0~0}. \item Click the \textsc{Save} icon on the toolbar and save the batch file.\\ diff --git a/matlabbatch/@cfg_entry/cfg_entry.m b/matlabbatch/@cfg_entry/cfg_entry.m index cdcdb78c..15423100 100644 --- a/matlabbatch/@cfg_entry/cfg_entry.m +++ b/matlabbatch/@cfg_entry/cfg_entry.m @@ -30,6 +30,9 @@ % workaround - in future versions num may be changed to a % 2-by-ndims array encoding min/max values for each % dimension. +% If strtype is 's+' and num has 2 elements, these 2 +% elements code the min/max number of lines in the cell +% array. The length of each line is not checked. % * def % * extras - Extra information used for content checks of item.val{1}. The % following strtypes can use this extra information: @@ -56,6 +59,7 @@ % The type of values that are entered by typing. e.g. 'e' for evaluated. % The valid value types are: % 's' string +% 's+' multi-line string, returned as cellstr % 'e' evaluated expression - this can be any expression, even a struct % or cell % 'f' function or function handle @@ -95,9 +99,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_entry.m 5678 2013-10-11 14:58:04Z volkmar $ +% $Id: cfg_entry.m 7335 2018-06-15 12:44:38Z volkmar $ -rev = '$Rev: 5678 $'; %#ok +rev = '$Rev: 7335 $'; %#ok myclass = mfilename; % Get local fields and defaults from private/mysubs_fields diff --git a/matlabbatch/@cfg_entry/match.m b/matlabbatch/@cfg_entry/match.m index cf8b6bd9..635f0960 100644 --- a/matlabbatch/@cfg_entry/match.m +++ b/matlabbatch/@cfg_entry/match.m @@ -18,6 +18,7 @@ % 'i' - matches strtype 'n', 'w', 'i' % 'r' - matches strtype 'n', 'w', 'i', 'r' % Any other strtype matches only on equality. +% An item with strtype 's+' also matches file lists. % % This code is part of a batch job configuration system for MATLAB. See % help matlabbatch @@ -26,9 +27,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: match.m 2675 2009-01-30 14:58:07Z volkmar $ +% $Id: match.m 7335 2018-06-15 12:44:38Z volkmar $ -rev = '$Rev: 2675 $'; %#ok +rev = '$Rev: 7335 $'; %#ok % match an empty spec sts = true; @@ -56,7 +57,11 @@ case 'num', sts = true; case 'class' - sts = strcmpi(spec{k}(l).value,class(item)); + if strcmp(item.strtype, 's+') + sts = any(strcmpi(spec{k}(l).value,{class(item),'cfg_files'})); + else + sts = strcmpi(spec{k}(l).value,class(item)); + end otherwise spec1{1}(1) = spec{k}(l); sts = match(item.cfg_item, spec1); diff --git a/matlabbatch/@cfg_entry/showdoc.m b/matlabbatch/@cfg_entry/showdoc.m index c693410e..c28a8def 100644 --- a/matlabbatch/@cfg_entry/showdoc.m +++ b/matlabbatch/@cfg_entry/showdoc.m @@ -10,9 +10,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: showdoc.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: showdoc.m 7335 2018-06-15 12:44:38Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 7335 $'; %#ok str = showdoc(item.cfg_item, indent); switch item.strtype @@ -29,7 +29,7 @@ str{end+1} = 'Whole numbers are entered.'; str{end+1} = shownum(item.num); case {'s'}, - str{end+1} = 'A String is entered.'; + str{end+1} = 'A string is entered.'; if isempty(item.num) str{end+1} = 'The character array may have arbitrary size.'; elseif isfinite(item.num(2)) @@ -40,6 +40,18 @@ str{end+1} = sprintf(['The string must have at least %d ' ... 'characters.'], item.num(1)); end; + case {'s+'}, + str{end+1} = 'A multi-line string is entered as cellstr.'; + if isempty(item.num) + str{end+1} = 'The string array may have arbitrary size.'; + elseif isfinite(item.num(2)) + str{end+1} = sprintf(['The string array must have between %d and %d ' ... + 'lines.'], item.num(1), ... + item.num(2)); + else + str{end+1} = sprintf(['The string array must have at least %d ' ... + 'lines.'], item.num(1)); + end; end; function numstr = shownum(num) diff --git a/matlabbatch/@cfg_entry/subsasgn_check.m b/matlabbatch/@cfg_entry/subsasgn_check.m index 1b76879f..269ead13 100644 --- a/matlabbatch/@cfg_entry/subsasgn_check.m +++ b/matlabbatch/@cfg_entry/subsasgn_check.m @@ -11,9 +11,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: subsasgn_check.m 5946 2014-04-10 13:09:34Z volkmar $ +% $Id: subsasgn_check.m 7339 2018-06-15 12:44:42Z volkmar $ -rev = '$Rev: 5946 $'; %#ok +rev = '$Rev: 7339 $'; %#ok sts = true; switch subs(1).subs @@ -43,7 +43,7 @@ val = {vtmp}; end case {'strtype'} - strtypes = {'s','e','f','n','w','i','r','c','x','p'}; + strtypes = {'s','s+','e','f','n','w','i','r','c','x','p'}; sts = isempty(val) || (ischar(val) && ... any(strcmp(val, strtypes))); if ~sts @@ -59,7 +59,7 @@ % check for reserved words if ischar(val) && size(val, 1) == 1 && any(strcmp(val, {'',''})) cfg_message('matlabbatch:checkval', ... - ['%s: Item must not be one of the reserved words '''' ' ... + ['%s: Value must not be one of the reserved words '''' ' ... 'or ''''.'], subsasgn_checkstr(item,substruct('.','val'))); sts = false; return; @@ -94,8 +94,14 @@ end end case {'s+'} - cfg_message('matlabbatch:checkval:strtype', ... - '%s: FAILURE: Cant do s+ yet', subsasgn_checkstr(item,substruct('.','val'))); + if ~iscellstr(val) + cfg_message('matlabbatch:checkval:strtype', ... + '%s: Item must be a cell array of strings.', subsasgn_checkstr(item,substruct('.','val'))); + sts = false; + else + [sts, val] = numcheck(item,val); + val = val(:); + end case {'f'} % test whether val is a function handle or a name of an % existing function @@ -160,7 +166,7 @@ sts = true; csz = size(val); if ~isempty(item.num) - if item.strtype == 's' && numel(item.num) == 2 + if any(strcmp(item.strtype, {'s','s+'})) && numel(item.num) == 2 % interpret num field as [min max] # elements sts = item.num(1) <= numel(val) && numel(val) <= item.num(2); if ~sts diff --git a/matlabbatch/@cfg_item/gencode_item.m b/matlabbatch/@cfg_item/gencode_item.m index 9dff5fd2..73cfbc33 100644 --- a/matlabbatch/@cfg_item/gencode_item.m +++ b/matlabbatch/@cfg_item/gencode_item.m @@ -29,9 +29,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode_item.m 5688 2013-10-11 14:58:28Z volkmar $ +% $Id: gencode_item.m 7473 2018-11-06 10:26:44Z guillaume $ -rev = '$Rev: 5688 $'; %#ok +rev = '$Rev: 7473 $'; %#ok %% Class of item % if there are function handles in .check or .def, add their names to @@ -52,7 +52,7 @@ tag = genvarname(sprintf('%s%s', stoptag, item.tag), tagctx); else tag = genvarname(sprintf('%s%s', stoptag, tag), tagctx); - end; + end str = {}; cind = []; ccnt = 0; @@ -64,8 +64,8 @@ % only modify tag if there seems to be something wrong % could be more specific (parse struct,cell,array subscripts) tag = genvarname(tag, tagctx); - end; -end; + end +end tagctx = [tagctx {tag}]; % Item count ccnt = 1; @@ -108,8 +108,8 @@ ccnt = ccnt + cccnt; ctropts.cnt = ctropts.cnt + cccnt; tagctx = [tagctx ctag(k)]; - end; - end; + end + end % Update position of class definition cind = cind+numel(cstr); % Prepend code of children @@ -118,11 +118,16 @@ elseif numel(item.val) > 0 && ~isa(item.val{1}, 'cfg_item') % Check .def field. Generate code for .val only, if no defaults % defined or value is different from defaults. - if isempty(item.def) || ~isequalwithequalnans(feval(item.def), item.val{1}) + if exist('isequalwithequalnans','builtin') + iseqn = isequalwithequalnans(feval(item.def), item.val{1}); + else + iseqn = isequaln(feval(item.def), item.val{1}); + end + if isempty(item.def) || ~iseqn str1 = gencode(item.val, sprintf('%s.val', tag), tagctx); str = [str(:)' str1(:)']; - end; -end; + end +end %% Check % Generate check field if ~isempty(item.check) @@ -145,7 +150,7 @@ % Works only because gencode does not produce subscripts for cellstrings str1 = gencode(item.help, sprintf('%s.help ', tag), tagctx); str = [str(:)' str1(:)']; -end; +end %% Def % Generate def field if ~isempty(item.def) @@ -154,4 +159,4 @@ % strings str1 = gencode(item.def, sprintf('%s.def ', tag), tagctx); str = [str(:)' str1(:)']; -end; +end diff --git a/matlabbatch/@cfg_mchoice/setval.m b/matlabbatch/@cfg_mchoice/setval.m index 8f145e48..d5aea5a0 100644 --- a/matlabbatch/@cfg_mchoice/setval.m +++ b/matlabbatch/@cfg_mchoice/setval.m @@ -3,7 +3,8 @@ % function item = setval(item, val, dflag) % If isempty(val), set item.val to {}. Otherwise, if item.values{val(1)} % is not already in item.val, set item.val{end+1} to item.values{val(1)}. -% This method does not yet delete individual val items. +% If val(1) is not finite, then the entry val(2) is deleted from item.val. +% dflag is ignored for cfg_repeat items. % dflag is ignored for cfg_mchoice items. % % This code is part of a batch job configuration system for MATLAB. See @@ -13,15 +14,19 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: setval.m 5678 2013-10-11 14:58:04Z volkmar $ +% $Id: setval.m 7336 2018-06-15 12:44:39Z volkmar $ -rev = '$Rev: 5678 $'; %#ok +rev = '$Rev: 7336 $'; %#ok if isempty(val) item = subsasgn(item, substruct('.','val'), {}); else - if ~any(strcmp(gettag(item.values{val(1)}), tagnames(item, false))) - val = item.values{val(1)}; - item = subsasgn(item, substruct('.','val', '{}',{numel(item.cfg_item.val)+1}), val); - end; + if isfinite(val(1)) + if ~any(strcmp(gettag(item.values{val(1)}), tagnames(item, false))) + val = item.values{val(1)}; + item = subsasgn(item, substruct('.','val', '{}',{numel(item.cfg_item.val)+1}), val); + end; + else + item.cfg_item.val(val(2)) = []; + end end; diff --git a/matlabbatch/@cfg_menu/setval.m b/matlabbatch/@cfg_menu/setval.m index 3ff87cd8..b796d8bc 100644 --- a/matlabbatch/@cfg_menu/setval.m +++ b/matlabbatch/@cfg_menu/setval.m @@ -14,9 +14,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: setval.m 5678 2013-10-11 14:58:04Z volkmar $ +% $Id: setval.m 7342 2018-06-15 12:44:44Z volkmar $ -rev = '$Rev: 5678 $'; %#ok +rev = '$Rev: 7342 $'; %#ok if iscell(val) && isempty(val) if dflag @@ -35,7 +35,16 @@ item = subsasgn(item, substruct('.','val'), {}); end else - val = item.values{val}; + if ~isa(val, 'cfg_dep') + if isnumeric(val) && val >= 1 && val <= numel(item.values) + val = item.values{val}; + else + cfg_message('matlabbatch:setval', ... + '%: unable to set value. Index must be an integer between 1 and %d.', ... + subsasgn_checkstr(item, substruct('.','val')), numel(item.values)); + return + end + end if dflag [sts, val1] = subsasgn_check(item, substruct('.','val'), {val}); if sts diff --git a/matlabbatch/cfg_confgui/cfg_confgui.m b/matlabbatch/cfg_confgui/cfg_confgui.m index bd50cd24..a92bc6a1 100644 --- a/matlabbatch/cfg_confgui/cfg_confgui.m +++ b/matlabbatch/cfg_confgui/cfg_confgui.m @@ -12,9 +12,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_confgui.m 6460 2015-05-28 08:30:28Z volkmar $ +% $Id: cfg_confgui.m 7337 2018-06-15 12:44:40Z volkmar $ -rev = '$Rev: 6460 $'; %#ok +rev = '$Rev: 7337 $'; %#ok %% Declaration of fields @@ -260,6 +260,7 @@ conf_strtype.name = 'Strtype'; conf_strtype.tag = 'strtype'; conf_strtype.labels = {'String (s)', ... + 'Multiline string (s+)', ... 'Evaluated (e)', ... 'Natural number (1..n) (n)', ... 'Whole number (0..n) (w)', ... @@ -269,7 +270,7 @@ 'Condition vector (c)', ... 'Contrast matrix (x)', ... 'Permutation (p)'}; -conf_strtype.values = {'s','e','n','w','i','r','f','c','x','p'}; +conf_strtype.values = {'s','s+','e','n','w','i','r','f','c','x','p'}; conf_strtype.help = {'Strtype field.', 'This type describes how an evaluated input should be treated. Type checking against this type will be performed during subscript assignment.'}; % Extras @@ -355,6 +356,15 @@ conf_class_files.hidden = true; conf_class_files.help = {'Hidden field that gives the hint to cfg_struct2cfg which class to create.'}; +% Mchoice +%----------------------------------------------------------------------- +conf_class_mchoice = cfg_const; +conf_class_mchoice.name = 'Mchoice'; +conf_class_mchoice.tag = 'type'; +conf_class_mchoice.val = {'cfg_mchoice'}; +conf_class_mchoice.hidden = true; +conf_class_mchoice.help = {'Hidden field that gives the hint to cfg_struct2cfg which class to create.'}; + % Menu %----------------------------------------------------------------------- conf_class_menu = cfg_const; @@ -439,6 +449,16 @@ conf_files.prog = @cfg_cfg_pass; conf_files.vout = @cfg_cfg_vout; +% Mchoice +%----------------------------------------------------------------------- +conf_mchoice = cfg_exbranch; +conf_mchoice.name = 'Mchoice'; +conf_mchoice.tag = 'conf_mchoice'; +conf_mchoice.val = {conf_class_mchoice, conf_name, conf_tag, conf_values, conf_check, conf_rewrite_job, conf_help}; +conf_mchoice.help = help2cell('cfg_mchoice'); +conf_mchoice.prog = @cfg_cfg_pass; +conf_mchoice.vout = @cfg_cfg_vout; + % Menu %----------------------------------------------------------------------- conf_menu = cfg_exbranch; @@ -565,7 +585,7 @@ menu_struct = cfg_choice; menu_struct.name = 'Tree structuring items'; menu_struct.tag = 'menu_struct'; -menu_struct.values = {conf_branch, conf_exbranch, conf_choice, conf_repeat}; +menu_struct.values = {conf_branch, conf_exbranch, conf_choice, conf_mchoice, conf_repeat}; menu_struct.help = {'These items collect data entry items and build a menu structure.'}; % Root node diff --git a/matlabbatch/cfg_struct2cfg.m b/matlabbatch/cfg_struct2cfg.m index 15cd8106..75d83c4a 100644 --- a/matlabbatch/cfg_struct2cfg.m +++ b/matlabbatch/cfg_struct2cfg.m @@ -18,9 +18,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_struct2cfg.m 1862 2008-06-30 14:12:49Z volkmar $ +% $Id: cfg_struct2cfg.m 7336 2018-06-15 12:44:39Z volkmar $ -rev = '$Rev: 1862 $'; %#ok +rev = '$Rev: 7336 $'; %#ok if nargin < 2 indent = ''; @@ -61,7 +61,7 @@ val{k} = cfg_struct2cfg(co.val{k}, [indent ' ']); end; co.val = val; - case {'cfg_repeat', 'cfg_choice'} + case {'cfg_repeat', 'cfg_choice', 'cfg_mchoice'} if isfield(co, 'val') val = cell(size(co.val)); for k = 1:numel(co.val) diff --git a/matlabbatch/cfg_ui.fig b/matlabbatch/cfg_ui.fig index 3b283da7..343bc6f0 100644 Binary files a/matlabbatch/cfg_ui.fig and b/matlabbatch/cfg_ui.fig differ diff --git a/matlabbatch/cfg_ui.m b/matlabbatch/cfg_ui.m index 9f13238a..d817ffdf 100644 --- a/matlabbatch/cfg_ui.m +++ b/matlabbatch/cfg_ui.m @@ -27,9 +27,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_ui.m 6515 2015-08-06 10:07:55Z volkmar $ +% $Id: cfg_ui.m 7394 2018-08-13 16:24:53Z spm $ -rev = '$Rev: 6515 $'; %#ok +rev = '$Rev: 7394 $'; %#ok % edit the above text to modify the response to help cfg_ui @@ -186,7 +186,10 @@ function local_setfont(obj,fs) function local_pointer(ptr) shh = get(0,'showhiddenhandles'); set(0,'showhiddenhandles','on'); -set(get(0,'Children'),'Pointer',ptr); +C = get(0,'Children'); +for i = 1:numel(C) + try, set(C(i),'Pointer',ptr); end +end drawnow; set(0,'showhiddenhandles',shh); @@ -629,7 +632,9 @@ function MenuFileSave_Callback(hObject, eventdata, handles) udmodlist = get(handles.modlist, 'userdata'); opwd = pwd; if ~isempty(udmodlist.wd) - cd(udmodlist.wd); + try + cd(udmodlist.wd); + end end; [file, pth, idx] = uiputfile({'*.mat','Matlab .mat File';... '*.m','Matlab .m Script File'}, 'Save Job'); @@ -668,7 +673,9 @@ function MenuFileScript_Callback(hObject, eventdata, handles) udmodlist = get(handles.modlist, 'userdata'); opwd = pwd; if ~isempty(udmodlist.wd) - cd(udmodlist.wd); + try + cd(udmodlist.wd); + end end; [file, pth, idx] = uiputfile({'*.m','Matlab .m Script File'},... 'Script File name'); @@ -706,7 +713,9 @@ function MenuFileRun_Callback(hObject, eventdata, handles) if strcmpi(questdlg(sprintf('An error occured during job execution. Please see the MATLAB command window for details.\n\nSave error information?'),'Error in job execution', 'Yes','No','Yes'), 'yes') opwd = pwd; if ~isempty(udmodlist.wd) - cd(udmodlist.wd); + try + cd(udmodlist.wd); + end end; [file, pth, idx] = uiputfile({'*.mat','Matlab .mat File'},... 'Error .mat File name'); diff --git a/matlabbatch/cfg_ui_util.m b/matlabbatch/cfg_ui_util.m index ab2b9da7..deca3e4d 100644 --- a/matlabbatch/cfg_ui_util.m +++ b/matlabbatch/cfg_ui_util.m @@ -11,9 +11,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_ui_util.m 6136 2014-08-07 10:35:12Z volkmar $ +% $Id: cfg_ui_util.m 7473 2018-11-06 10:26:44Z guillaume $ -rev = '$Rev: 6136 $'; %#ok +rev = '$Rev: 7473 $'; %#ok switch lower(cmd) case {'preview'} @@ -41,27 +41,32 @@ datastr = sprintf('DEP %s', contents{2}{1}.sname); else datastr = sprintf('DEP (%d outputs)', numel(contents{2}{1})); - end; + end else switch contents{5} - case 'cfg_menu', + case 'cfg_menu' datastr = 'Unknown selection'; for l = 1:numel(contents{4}) - if isequalwithequalnans(contents{2}{1}, contents{4}{l}) + if exist('isequalwithequalnans','builtin') + iseqn = isequalwithequalnans(contents{2}{1}, contents{4}{l}); + else + iseqn = isequaln(contents{2}{1}, contents{4}{l}); + end + if iseqn datastr = contents{3}{l}; break; - end; - end; - case 'cfg_files', + end + end + case 'cfg_files' if numel(contents{2}{1}) == 1 if isempty(contents{2}{1}{1}) datastr = ' '; else datastr = contents{2}{1}{1}; - end; + end else datastr = sprintf('%d files', numel(contents{2}{1})); - end; + end case 'cfg_entry' csz = size(contents{2}{1}); % TODO use gencode like string formatting @@ -76,26 +81,26 @@ datastr = mat2str(contents{2}{1}(:)'); elseif any(csz == 0) switch class(contents{2}{1}) - case 'char', + case 'char' datastr = ''''''; - case 'double', + case 'double' datastr = '[]'; otherwise datastr = sprintf('%s([])', ... class(contents{2}{1})); - end; + end else szstr = sprintf('%dx', csz); datastr = sprintf('%s %s', ... szstr(1:end-1), class(contents{2}{1})); - end; + end otherwise datastr = ' '; - end; - end; + end + end else datastr = '<-X'; - end; + end namestr = sprintf('%s%s ', indent, contents{1}); varargout{1} = namestr; varargout{2} = datastr; @@ -131,7 +136,7 @@ cfg_tropts({{'hidden', true}},1,1,1,1,dflag), ... {'name','val','labels','values','class','level', ... 'all_set','all_set_item','preview'}); - end; + end namestr = cell(1,numel(id)); datastr = cell(1,numel(id)); namestr{1} = sprintf('Help on: %s',contents{1}{1}); @@ -153,7 +158,7 @@ str = {'Reference from'}; for k = 1:numel(contents{2}{1}) % we may have multiple dependencies str{k+1} = contents{2}{1}(k).sname; % return something to be printed - end; + end elseif ~isempty(contents{2}) if ndims(contents{2}{1}) <= 2 if ischar(contents{2}{1}) @@ -165,13 +170,13 @@ str = cellstr(num2str(contents{2}{1})); else str = gencode(contents{2}{1},'val'); - end; + end else str = gencode(contents{2}{1},'val'); - end; + end else str = ''; - end; + end case {'cfg_menu','cfg_choice'} if strcmp(contents{5},'cfg_menu') || ~dflag if strcmp(contents{5},'cfg_choice') @@ -179,7 +184,7 @@ cmpsubs = substruct('.','tag'); else cmpsubs = struct('type',{},'subs',{}); - end; + end valsubs = substruct('{}',{1}); nitem = numel(contents{4}); mrk = cell(1,nitem); @@ -189,19 +194,19 @@ mrk{l} = '*'; else mrk{l} = ' '; - end; - end; + end + end if strcmp(contents{5},'cfg_choice') str = cell(1,nitem); for k = 1:nitem str{k} = contents{4}{k}.name; - end; + end else str = contents{3}; - end; + end str = strcat(mrk(:), str(:)); - end; - case {'cfg_repeat'} + end + case {'cfg_repeat', 'cfg_mchoice'} if ~dflag % Already selected items ncitems = numel(contents{2}); @@ -209,10 +214,10 @@ for k = 1:ncitems str{k} = contents{2}{k}.name; end - end; + end otherwise str = {}; - end; + end varargout{1} = str; case 'showvaldeps' % List matching dependencies @@ -221,7 +226,7 @@ % loop over sout to find whether there are dependencies that match the current item for k = 1:numel(sout) smatch(k) = cfg_util('match', job_id, mod_job_id, item_mod_id, sout(k).tgt_spec); - end; + end varargout{1} = sout(smatch); case 'showvaledit' % Fill value display boxes in a GUI, set up callbacks if necessary @@ -279,8 +284,8 @@ if ~isempty(sout) set(findobj(fig,'-regexp','Tag','.*AddDep$'), ... 'Visible','on', 'Enable','on'); - end; - end; + end + end set(findobj(fig,'-regexp','Tag','.*EditVal$'), ... 'Visible','on', 'Enable','on'); set(findobj(fig,'-regexp','Tag','.*ClearVal$'), ... @@ -293,19 +298,19 @@ cmpsubs = substruct('.','tag'); else cmpsubs = struct('type',{},'subs',{}); - end; + end valsubs = substruct('{}',{1}); nitem = numel(contents{4}); for l = 1:nitem valuesubs = substruct('{}',{l}); if ~isempty(contents{2}) && isequal(subsref(contents{2},[valsubs cmpsubs]), subsref(contents{4},[valuesubs cmpsubs])) cval = l; - end; - end; + end + end udvalshow.cval = cval; if cval == -1 cval = 1; - end; + end udvalshow.cmd = num2cell(1:nitem); ltop = cfg_ui_getListboxTop(handles.valshow, cval, numel(str)); set(findobj(fig,'-regexp', 'Tag','^valshow.*'), 'Visible','on'); @@ -317,8 +322,8 @@ 'Visible','on', 'Enable','on'); set(findobj(fig,'-regexp','Tag','.*ClearVal$'), ... 'Visible','on', 'Enable','on'); - end; - case {'cfg_repeat'} + end + case {'cfg_repeat', 'cfg_mchoice'} if ~dflag udvalshow.cval = -1; % Already selected items @@ -340,36 +345,50 @@ end % Add/Replicate callbacks will be shown only if max number of % items not yet reached - if ncitems < contents{9}(2) + if (strcmp(contents{5}, 'cfg_repeat') && ncitems < contents{9}(2)) ... + || (strcmp(contents{5}, 'cfg_mchoice') && ncitems < numel(contents{4})) % Available items - naitems = numel(contents{4}); + aitems = contents{4}; + if strcmp(contents{5}, 'cfg_mchoice') + unsel = true(size(aitems)); + for k = 1:numel(contents{2}) + unsel(k) = ~any(cellfun(@(citem)isequal(contents{2}(k), citem), aitems)); + end + aitems = aitems(unsel); + end + naitems = numel(aitems); str1 = cell(naitems,1); cmd1 = cell(naitems,1); for k = 1:naitems - str1{k} = sprintf('New: %s', contents{4}{k}.name); + str1{k} = sprintf('New: %s', aitems{k}.name); cmd1{k} = [k Inf]; madd = findobj(fig,'-regexp','Tag','.*ValAddItem$'); for cm = 1:numel(madd) uimenu(madd(cm), ... - 'Label',contents{4}{k}.name, ... + 'Label',aitems{k}.name, ... 'Callback',@(ob,ev)local_setvaledit(ciid, cmd1{k}, false, updatecb, ob, ev), ... 'Tag','ValAddItemDyn'); end - end; - str2 = cell(ncitems,1); - cmd2 = cell(ncitems,1); - for k = 1:ncitems - str2{k} = sprintf('Replicate: %s (%d)',... - contents{2}{k}.name, k); - cmd2{k} = [-1 k]; - mrepl = findobj(fig,'-regexp','Tag','.*ValReplItem$'); - for cm = 1:numel(mrepl) - uimenu(mrepl(cm), ... - 'Label',sprintf('%s (%d)', ... - contents{2}{k}.name, k), ... - 'Callback',@(ob,ev)local_setvaledit(ciid, cmd2{k}, false, updatecb, ob, ev), ... - 'Tag','ValReplItemDyn'); + end + if strcmp(contents{5}, 'cfg_repeat') + str2 = cell(ncitems,1); + cmd2 = cell(ncitems,1); + for k = 1:ncitems + str2{k} = sprintf('Replicate: %s (%d)',... + contents{2}{k}.name, k); + cmd2{k} = [-1 k]; + mrepl = findobj(fig,'-regexp','Tag','.*ValReplItem$'); + for cm = 1:numel(mrepl) + uimenu(mrepl(cm), ... + 'Label',sprintf('%s (%d)', ... + contents{2}{k}.name, k), ... + 'Callback',@(ob,ev)local_setvaledit(ciid, cmd2{k}, false, updatecb, ob, ev), ... + 'Tag','ValReplItemDyn'); + end end + else + str2 = {}; + cmd2 = {}; end set(findobj(fig,'-regexp','Tag','.*AddItem$'), ... 'Visible','on', 'Enable','on'); @@ -395,11 +414,11 @@ if ncitems > 0 set(findobj(fig,'-regexp','Tag','.*DelItem$'), ... 'Visible','on', 'Enable','on'); - end; + end set(findobj(fig,'-regexp','Tag','.*ClearVal$'), ... 'Visible','on', 'Enable','on'); - end; - end; + end + end [id, stop, help] = cfg_util('listmod', ciid{:}, cfg_findspec, ... cfg_tropts(cfg_findspec,1,1,1,1,false), {'showdoc'}); set(handles.helpbox, 'Value',1, 'ListboxTop',1, 'string',cfg_justify(handles.helpbox, help{1}{1})); @@ -413,24 +432,28 @@ [unused, unused, itemclass] = cfg_util('listmod', ciid{:}, cfg_findspec, ... cfg_tropts(cfg_findspec,1,1,1,1,false), {'class'}); switch itemclass{1}{1} - case {'cfg_entry'}, + case {'cfg_entry'} [val, sts] = local_valedit_edit(ciid, itemname, val); - case { 'cfg_files'}, + case { 'cfg_files'} [val, sts] = local_valedit_files(ciid, itemname, val); - case {'cfg_choice', 'cfg_menu', 'cfg_repeat'}, + case {'cfg_choice', 'cfg_mchoice', 'cfg_menu', 'cfg_repeat'} % does not return value - use udvalshow.updatecb inside % local_valedit_repeat as callback to update ui. sts = false; local_valedit_repeat(gcbf); otherwise sts = false; - end; + end if sts - handles = guidata(gcbf); + h = gcbf; + if isempty(h) && exist('OCTAVE_VERSION', 'builtin') + h = findall(0,'tag','cfg_ui'); + end + handles = guidata(h); udvalshow = get(handles.valshow, 'Userdata'); feval(udvalshow.setvalcb, val); feval(udvalshow.updatecb); - end; + end case 'setvaledit' local_setvaledit(varargin{:}); end @@ -460,13 +483,15 @@ function local_valedit_key(hObject, data, varargin) % silently clear cfg_deps if strtype{1}{1} == 's' val = {''}; + elseif strcmp(strtype{1}{1}, 's+') + val = {{''}}; else val = {[]}; - end; -end; + end +end % If requested or we can't handle this, use expert mode expmode = strcmp(cfg_get_defaults([mfilename '.ExpertEdit']), 'on') ||... - ndims(val{1}) > 2 || ~(ischar(val{1}) || isnumeric(val{1}) || islogical(val{1})); + ndims(val{1}) > 2 || ~(ischar(val{1}) || iscellstr(val{1}) || isnumeric(val{1}) || islogical(val{1})); % Generate code for current value, if not empty % Set dialog texts if expmode @@ -481,7 +506,8 @@ function local_valedit_key(hObject, data, varargin) hlptxt = char({'Enter a valid MATLAB expression.', ... ' ', ... ['Strings must be enclosed in single quotes ' ... - '(''A''), multiline arrays in brackets ([ ]).'], ... + '(''A''), multiline string arrays in curly braces {} ' ... + 'and multiline arrays in brackets ([ ]).'], ... ' ', ... 'To clear a value, enter an empty cell ''{}''.', ... ' ', ... @@ -489,20 +515,24 @@ function local_valedit_key(hObject, data, varargin) failtxt = {'Input could not be evaluated. Possible reasons are:',... '1) Input should be a vector or matrix, but is not enclosed in ''['' and '']'' brackets.',... '2) Input should be a character or string, but is not enclosed in '' single quotes.',... + '3) Input should be a multiline string, but is not enclosed in ''{'' and ''}'' braces or strings not in '' single quotes.',... '3) Input should be a MATLAB variable, but is misspelled.',... '4) Input should be a MATLAB expression, but has syntax errors.'}; else if strtype{1}{1} == 's' instr = val; encl = {'''' ''''}; + elseif strcmp(strtype{1}{1}, 's+') + instr = {char(val{1})}; + encl = {'''' ''''}; else try instr = {num2str(val{1})}; catch instr = {''}; - end; + end encl = {'[' ']'}; - end; + end hlptxt = char({'Enter a value.', ... ' ', ... 'To clear a value, clear the input field and accept.', ... @@ -524,7 +554,7 @@ function local_valedit_key(hObject, data, varargin) if iscell(str) && isempty(str) % User has hit cancel button return; - end; + end % save instr in case of evaluation error instr = str; % str{1} is a multiline char array @@ -542,31 +572,38 @@ function local_valedit_key(hObject, data, varargin) cfg_ui_restore(hv); % for strtype 's', val must be a string sts = sts && (~strcmp(strtype{1}{1},'s') || ischar(val)); + % for strtype 's+', val must be a cellstr + sts = sts && (~strcmp(strtype{1}{1},'s+') || iscellstr(val)); if ~sts if ~expmode - % try with matching value enclosure - if strtype{1}{1} == 's' - if ishandle(val) % delete accidentally created objects - delete(val); - end - % escape single quotes and place the whole string in single quotes - str = strcat(encl(1), strrep(cstr,'''',''''''), encl(2), {char(10)}); + if strcmp(strtype{1}{1}, 's+') + val = cstr; + sts = true; else - cestr = [encl(1); cstr(:); encl(2)]'; - str = strcat(cestr, {char(10)}); - end; - str = cat(2, str{:}); - % Evaluation is encapsulated to avoid users compromising this function - % context - graphics handles are made invisible to avoid accidental - % damage - hv = cfg_ui_disable(0, 'HandleVisibility'); - [val, sts] = cfg_eval_valedit(str); - cfg_ui_restore(hv); - end; + % try with matching value enclosure + if strtype{1}{1} == 's' + if ishandle(val) % delete accidentally created objects + delete(val); + end + % escape single quotes and place the whole string in single quotes + str = strcat(encl(1), strrep(cstr,'''',''''''), encl(2), {char(10)}); + else + cestr = [encl(1); cstr(:); encl(2)]'; + str = strcat(cestr, {char(10)}); + end + str = cat(2, str{:}); + % Evaluation is encapsulated to avoid users compromising this function + % context - graphics handles are made invisible to avoid accidental + % damage + hv = cfg_ui_disable(0, 'HandleVisibility'); + [val, sts] = cfg_eval_valedit(str); + cfg_ui_restore(hv); + end + end if ~sts % (Still) no valid input uiwait(msgbox(failtxt,'Evaluation error','modal')); - end; - end; + end + end end % End of function will be reached with sts == true and new val @@ -577,7 +614,7 @@ function local_valedit_key(hObject, data, varargin) inifile = ''; else inifile = val{1}; -end; +end [val, sts] = cfg_getfile(contents{1}{1}, contents{2}{1}, itemname, inifile, contents{3}{1}, contents{4}{1}); % -------------------------------------------------------------------- @@ -593,7 +630,7 @@ function local_valedit_repeat(hObject,varargin) if ccmd ~= udvalshow.cval feval(udvalshow.setvalcb, udvalshow.cmd{ccmd}); feval(udvalshow.updatecb); - end; + end elseif (~isempty(udvalshow.key) && strcmpi(udvalshow.key.Key,'escape') && ... isequal(hObject, handles.valshow)) % callback called from handles.valshow, finish editing @@ -613,7 +650,7 @@ function local_valedit_repeat(hObject,varargin) else udvalshow.key = []; set(handles.valshow, 'Userdata',udvalshow); -end; +end % -------------------------------------------------------------------- function local_setvaledit(ciid, val, dflag, varargin) @@ -622,7 +659,7 @@ function local_setvaledit(ciid, val, dflag, varargin) else cfg_util('setval', ciid{:}, val); cfg_util('harvest', ciid{1:end-1}); -end; +end if nargin > 3 && subsasgn_check_funhandle(varargin{1}) % update GUI feval(varargin{1}); diff --git a/matlabbatch/cfg_util.m b/matlabbatch/cfg_util.m index baa06490..c03629cc 100644 --- a/matlabbatch/cfg_util.m +++ b/matlabbatch/cfg_util.m @@ -373,7 +373,7 @@ % Same as cfg_util('showdoc', but use handle or width to determine the % width of the returned strings. % -% [mod_job_idlist, str, sts, dep sout] = cfg_util('showjob', job_id[, mod_job_idlist]) +% [mod_job_idlist, str, sts, dep, sout] = cfg_util('showjob', job_id[, mod_job_idlist]) % % Return information about the current job (or the part referenced by the % input cell array mod_job_idlist). Output arguments @@ -423,9 +423,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_util.m 6942 2016-11-21 13:17:44Z guillaume $ +% $Id: cfg_util.m 7345 2018-06-15 12:44:47Z volkmar $ -rev = '$Rev: 6942 $'; +rev = '$Rev: 7345 $'; %% Initialisation of cfg variables % load persistent configuration data, initialise if necessary @@ -760,16 +760,21 @@ varargout{1} = cjob; varargout{2} = {}; else - if ischar(varargin{1}) || iscellstr(varargin{1}) + if ischar(varargin{1}) + % assume single job filename [job, jobdedup] = cfg_load_jobs(varargin{1}); - elseif iscell(varargin{1}) && iscell(varargin{1}{1}) - % try to initialise cell array of jobs - job = varargin{1}; - jobdedup = NaN; % Postpone duplicate detection - else - % try to initialise single job + elseif iscell(varargin{1}) && all(cellfun('isclass', varargin{1}, 'struct')) + % try to initialise single job variable job{1} = varargin{1}; jobdedup = 1; + else + % try to initialise cell array of jobs - a mix of file + % names and job variables is allowed + job = cell(size(varargin{1})); + jobdedup = NaN*ones(size(varargin{1})); + jfiles = cellfun(@ischar, varargin{1}); + [job(jfiles), jobdedup(jfiles)] = cfg_load_jobs(varargin{1}(jfiles)); + job(~jfiles) = varargin{1}(~jfiles); end % job should be a cell array of job structures isjob = true(size(job)); @@ -777,13 +782,17 @@ isjob(k) = iscell(job{k}) && all(cellfun('isclass', job{k}, 'struct')); end job = job(isjob); + jobdedup = jobdedup(isjob); if isempty(job) cfg_message('matlabbatch:initialise:invalid','No valid job.'); else if any(isnan(jobdedup)) % build up list of unique jobs - jobdedup = NaN*ones(size(job)); - cu = 0; + if any(~isnan(jobdedup)) + cu = max(jobdedup(~isnan(jobdedup))); + else + cu = 0; + end for k = 1:numel(job) if isnan(jobdedup(k)) % found new candidate @@ -791,7 +800,11 @@ jobdedup(k) = cu; % look for similar jobs under remaining candidates csel = find(isnan(jobdedup)); - eqind = cellfun(@(cjob)isequalwithequalnans(cjob,job{k}),job(csel)); + if exist('isequalwithequalnans','builtin') + eqind = cellfun(@(cjob)isequalwithequalnans(cjob,job{k}),job(csel)); + else + eqind = cellfun(@(cjob)isequaln(cjob,job{k}),job(csel)); + end jobdedup(csel(eqind)) = cu; end end @@ -1440,7 +1453,12 @@ function local_gencode(c0, fname, tropts, preamble) cfg_mlbatch_appcfg_master; [c0, jobs] = cfg_util_persistent; else - appcfgs = cellstr(which('cfg_mlbatch_appcfg','-all')); + if exist('OCTAVE_VERSION','builtin') + % workaround for bug #32088 + appcfgs = file_in_loadpath('cfg_mlbatch_appcfg.m', 'all'); + else + appcfgs = cellstr(which('cfg_mlbatch_appcfg','-all')); + end cwd = pwd; dirs = cell(size(appcfgs)); for k = 1:numel(appcfgs) @@ -1634,23 +1652,34 @@ function local_gencode(c0, fname, tropts, preamble) diary(tempname); dname = get(0, 'DiaryFile'); end +tdsubs = substruct('.','tdeps'); +chsubs = substruct('.','chk'); cfg_message('matlabbatch:run:jobstart', ... ['\n\n------------------------------------------------------------------------\n',... - 'Running job #%d\n', ... - '------------------------------------------------------------------------'], cjob); -if cflag && ~isempty(job.cjrun) - [u1, mlbch] = harvest(job.cjrun, job.cjrun, false, true); -else - job1 = local_compactjob(job); - job.cjid2subsrun = job1.cjid2subs; - [u1, mlbch, u3, u4, u5, job.cjrun] = harvest(job1.cj, job1.cj, false, true); + '%s - Running job #%d\n', ... + '------------------------------------------------------------------------'], datestr(now), cjob); +try + if cflag && ~isempty(job.cjrun) + [u1, mlbch] = harvest(job.cjrun, job.cjrun, false, true); + else + job1 = local_compactjob(job); + job.cjid2subsrun = job1.cjid2subs; + [u1, mlbch, u3, u4, u5, job.cjrun] = harvest(job1.cj, job1.cj, false, true); + end + % copy cjid2subs, it will be modified for each module that is run + cjid2subs = job.cjid2subsrun; + cjid2subsfailed = {}; + cjid2subsskipped = {}; +catch + cjid2subs = {}; + cjid2subsfailed = {}; + cjid2subsskipped = job.cjid2subsrun; + le = lasterror; + le.stack = le.stack(1); + str = cfg_disp_error(le); + cfg_message('matlabbatch:run:modfailed', '%s - Failed to update inputs for some modules.', datestr(now)); + cfg_message('matlabbatch:run:modfailed', '%s\n', str{:}); end -% copy cjid2subs, it will be modified for each module that is run -cjid2subs = job.cjid2subsrun; -cjid2subsfailed = {}; -cjid2subsskipped = {}; -tdsubs = substruct('.','tdeps'); -chsubs = substruct('.','chk'); while ~isempty(cjid2subs) % find mlbch that can run cand = false(size(cjid2subs)); @@ -1683,11 +1712,11 @@ function local_gencode(c0, fname, tropts, preamble) if isa(cm.jout,'cfg_inv_out') % no cached outputs (module did not run or it does not return % outputs) - run job - cfg_message('matlabbatch:run:modstart', 'Running ''%s''', cm.name); + cfg_message('matlabbatch:run:modstart', '%s - Running ''%s''', datestr(now), cm.name); try cm = cfg_run_cm(cm, subsref(mlbch, cfg2jobsubs(job.cjrun, cjid2subsrun{k}))); csdeps{k} = cm.sdeps; - cfg_message('matlabbatch:run:moddone', 'Done ''%s''', cm.name); + cfg_message('matlabbatch:run:moddone', '%s - Done ''%s''', datestr(now), cm.name); catch cjid2subsfailed = [cjid2subsfailed cjid2subsrun(k)]; le = lasterror; @@ -1697,7 +1726,7 @@ function local_gencode(c0, fname, tropts, preamble) le.stack = le.stack(1:runind-1); end str = cfg_disp_error(le); - cfg_message('matlabbatch:run:modfailed', 'Failed ''%s''', cm.name); + cfg_message('matlabbatch:run:modfailed', '%s - Failed ''%s''', datestr(now), cm.name); cfg_message('matlabbatch:run:modfailed', '%s\n', str{:}); end % save results (if any) into job tree @@ -1720,15 +1749,23 @@ function local_gencode(c0, fname, tropts, preamble) [un, ind] = unique(ctgt_exbranch_id); for k = 1:numel(ind) cm = subsref(job.cjrun, ctgt_exbranch{ind(k)}); - [u1, cmlbch, u3, u4, u5, job.cjrun] = harvest(cm, job.cjrun, false, ... - true); - mlbch = subsasgn(mlbch, cfg2jobsubs(job.cjrun, ctgt_exbranch{ind(k)}), ... - cmlbch); + try + [u1, cmlbch, u3, u4, u5, job.cjrun] = harvest(cm, job.cjrun, false, ... + true); + mlbch = subsasgn(mlbch, cfg2jobsubs(job.cjrun, ctgt_exbranch{ind(k)}), ... + cmlbch); + catch + le = lasterror; + le.stack = le.stack(1); + str = cfg_disp_error(le); + cfg_message('matlabbatch:run:modfailed', '%s - Failed to update inputs for ''%s''', datestr(now), cm.name); + cfg_message('matlabbatch:run:modfailed', '%s\n', str{:}); + end end end end if isempty(cjid2subsfailed) && isempty(cjid2subsskipped) - cfg_message('matlabbatch:run:jobdone', 'Done\n'); + cfg_message('matlabbatch:run:jobdone', '%s - Done\n', datestr(now)); err = []; else str = cell(numel(cjid2subsfailed)+numel(cjid2subsskipped)+1,1); diff --git a/matlabbatch/gencode_rvalue.m b/matlabbatch/gencode_rvalue.m index af704ff0..cdd0ba34 100644 --- a/matlabbatch/gencode_rvalue.m +++ b/matlabbatch/gencode_rvalue.m @@ -28,9 +28,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode_rvalue.m 6640 2015-12-11 10:17:09Z volkmar $ +% $Id: gencode_rvalue.m 7344 2018-06-15 12:44:46Z volkmar $ -rev = '$Rev: 6640 $'; %#ok +rev = '$Rev: 7344 $'; %#ok if nargin < 2 cflag = false; @@ -178,7 +178,8 @@ '\', '\\'; ... char(0), '\0'; ... char(9), '\t'; ... - char(10), '\n'}; + char(10), '\n'; ... + char(13), '\r'}; for cr = 1:size(replacements, 1) str{k} = strrep(str{k}, replacements{cr,:}); end diff --git a/matlabbatch/private/cfg_justify.m b/matlabbatch/private/cfg_justify.m index af05e691..6f1ce74b 100644 --- a/matlabbatch/private/cfg_justify.m +++ b/matlabbatch/private/cfg_justify.m @@ -29,37 +29,42 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: cfg_justify.m 3944 2010-06-23 08:53:40Z volkmar $ +% $Id: cfg_justify.m 7462 2018-10-31 15:43:31Z spm $ out = {}; +if exist('OCTAVE_VERSION','builtin') + out = char(varargin(2:end)); + return; +end + if nargin < 2 - cfg_message('matlabbatch:usage','Incorrect usage of cfg_justify.') + cfg_message('matlabbatch:usage','Incorrect usage of cfg_justify.'); end n = varargin{1}; if ishandle(n) % estimate extent of a space char and scrollbar width - TempObj=copyobj(n,get(n,'Parent')); + TempObj = copyobj(n,get(n,'Parent')); set(TempObj,'Visible','off','Max',100); - spext = cfg_maxextent(TempObj,{repmat(' ',1,100)})/100; + spext = cfg_maxextent(TempObj,{repmat(' ',1,100)})/100; % try to work out slider size - pos = get(TempObj,'Position'); - oldun = get(TempObj,'units'); + pos = get(TempObj,'Position'); + oldun = get(TempObj,'units'); set(TempObj,'units','points'); - ppos = get(TempObj,'Position'); + ppos = get(TempObj,'Position'); set(TempObj,'units',oldun); - sc = pos(3)/ppos(3); + sc = pos(3)/ppos(3); % assume slider width of 15 points - swidth=15*sc; + swidth = 15*sc; else % dummy constants - spext = 1; - swidth = 0; + spext = 1; + swidth = 0; TempObj = n; end -for i=2:nargin, - if iscell(varargin{i}), - for j=1:numel(varargin{i}), +for i=2:nargin + if iscell(varargin{i}) + for j=1:numel(varargin{i}) para = justify_paragraph(TempObj,spext,swidth,varargin{i}{j}); out = [out(:);para(:)]'; end @@ -71,58 +76,60 @@ if ishandle(TempObj) delete(TempObj); end + + function out = justify_paragraph(n,spext,swidth,txt) -if numel(txt)>1 && txt(1)=='%', +if numel(txt)>1 && txt(1)=='%' txt = txt(2:end); -end; +end %txt = regexprep(txt,'/\*([^(/\*)]*)\*/',''); st1 = strfind(txt,'/*'); en1 = strfind(txt,'*/'); -st = []; -en = []; -for i=1:numel(st1), +st = ''; +en = ''; +for i=1:numel(st1) en1 = en1(en1>st1(i)); - if ~isempty(en1), + if ~isempty(en1) st = [st st1(i)]; en = [en en1(1)]; en1 = en1(2:end); - end; -end; + end +end -str = []; +str = ''; pen = 1; -for i=1:numel(st), +for i=1:numel(st) str = [str txt(pen:st(i)-1)]; pen = en(i)+2; -end; +end str = [str txt(pen:numel(txt))]; txt = str; off = find((txt'>='a' & txt'<='z') | (txt'>='A' & txt'<='Z')); -if isempty(off), +if isempty(off) out{1} = txt; else off = off(1); para = justify_para(n,off,spext,swidth,txt(off:end)); out = cell(numel(para),1); - if numel(para)>1, + if numel(para)>1 out{1} = [txt(1:(off-1)) para{1}]; - for j=2:numel(para), + for j=2:numel(para) out{j} = [repmat(' ',1,off-1) para{j}]; - end; + end else out{1} = txt; - end; -end; -return; + end +end + function out = justify_para(n,off,spext,swidth,varargin) % Collect varargs into a single string str = varargin{1}; -for i=2:length(varargin), +for i=2:length(varargin) str = [str ' ' varargin{i}]; -end; -if isempty(str), out = {''}; return; end; +end +if isempty(str), out = {''}; return; end if ishandle(n) % new size: at least 20 spaces wide, max widget width less offset % space and scrollbar width @@ -136,7 +143,7 @@ % fill with spaces to produce (roughly) block output for k = 1:numel(out)-1 out{k} = justify_line(out{k}, pos(3), cext(k), spext); - end; + end % reset position set(n,'Position',opos); else @@ -144,8 +151,9 @@ out = textwrap({str},cols); for k = 1:numel(out)-1 out{k} = justify_line(out{k}, cols, length(out{k}), 1); - end; -end; + end +end + function out = justify_line(str, width, cext, spext) ind = strfind(str,' '); @@ -160,6 +168,6 @@ % insert spaces beginning at the end of the string for k = numel(ind):-1:1 str = [str(1:ind(k)) repmat(' ',1,ins(k)) str(ind(k)+1:end)]; - end; + end out = str; -end; +end diff --git a/matlabbatch/private/cfg_textfill.m b/matlabbatch/private/cfg_textfill.m index d8320b50..288463a4 100644 --- a/matlabbatch/private/cfg_textfill.m +++ b/matlabbatch/private/cfg_textfill.m @@ -12,9 +12,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_textfill.m 2138 2008-09-22 13:27:44Z volkmar $ +% $Id: cfg_textfill.m 7473 2018-11-06 10:26:44Z guillaume $ -rev = '$Rev: 2138 $'; %#ok +rev = '$Rev: 7473 $'; %#ok if ~ishandle(obj) cfg_message('matlabbatch:usage',... @@ -48,8 +48,9 @@ if numel(left) ~= numel(right) cfg_message('matlabbatch:usage',... 'Second and third input must have the same number of lines.'); -end; +end; +if exist('OCTAVE_VERSION', 'builtin'), set(0,'currentfigure',ancestor(obj,'figure')); end TempObj=copyobj(obj,get(obj,'Parent')); set(TempObj,'Visible','off','Max',100); diff --git a/matlabbatch/private/num2str.m b/matlabbatch/private/num2str.m index 7b396507..40cceee5 100644 --- a/matlabbatch/private/num2str.m +++ b/matlabbatch/private/num2str.m @@ -52,7 +52,12 @@ floatWidthOffset = 4; % Compose sprintf format string of numeric array. -if nargin < 2 && ~isempty(x) && isequalwithequalnans(x, fix(x)) +if exist('isequalwithequalnans','builtin') + iseqn = isequalwithequalnans(x, fix(x)); +else + iseqn = isequaln(x, fix(x)); +end +if nargin < 2 && ~isempty(x) && iseqn if isreal(x) % The precision is unspecified; the numeric array contains whole numbers. s = int2str(x); diff --git a/spm.m b/spm.m index 64cd244c..b67b9b49 100644 --- a/spm.m +++ b/spm.m @@ -4,7 +4,7 @@ % ___ ____ __ __ % / __)( _ \( \/ ) % \__ \ )___/ ) ( Statistical Parametric Mapping -% (___/(__) (_/\/\_) SPM - http://www.fil.ion.ucl.ac.uk/spm/ +% (___/(__) (_/\/\_) SPM - https://www.fil.ion.ucl.ac.uk/spm/ %_______________________________________________________________________ % % SPM (Statistical Parametric Mapping) is a package for the analysis @@ -19,7 +19,7 @@ % command line using the command `spm_help`. % % Details of this release are available via the "About SPM" help topic -% accessible from the SPM splash screen. See also README.txt. +% accessible from the SPM splash screen. See also README.md. % % This spm function initialises the default parameters, and displays a % splash screen with buttons leading to the PET, fMRI and M/EEG @@ -50,10 +50,10 @@ % FORMAT & help in the main body of spm.m % %_______________________________________________________________________ -% Copyright (C) 1991,1994-2016 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1991,1994-2018 Wellcome Trust Centre for Neuroimaging % Andrew Holmes -% $Id: spm.m 7134 2017-07-18 09:46:25Z guillaume $ +% $Id: spm.m 7475 2018-11-07 13:03:49Z guillaume $ %======================================================================= @@ -306,7 +306,7 @@ disp( ' ___ ____ __ __ '); disp( '/ __)( _ \( \/ ) '); disp( '\__ \ )___/ ) ( Statistical Parametric Mapping '); -disp(['(___/(__) (_/\/\_) ',spm('Ver'),' - http://www.fil.ion.ucl.ac.uk/spm/']); +disp(['(___/(__) (_/\/\_) ',spm('Ver'),' - https://www.fil.ion.ucl.ac.uk/spm/']); fprintf('\n'); @@ -348,7 +348,7 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality spm_figure('WaterMark',Finter,spm('Ver'),'',45); url = fullfile(spm('Dir'),'help','index.html'); -%url = fullfile(spm('Dir'),'README.txt'); +%url = fullfile(spm('Dir'),'README.md'); spm_help('!Disp',url,'',Fgraph); fprintf('.'); %-Setup for current modality @@ -373,7 +373,7 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality %-Sort out arguments %----------------------------------------------------------------------- if nargin<2, Modality = ''; else Modality = varargin{2}; end -[Modality,ModNum] = spm('CheckModality',Modality); +Modality = spm('CheckModality',Modality); %-Sort out global defaults %----------------------------------------------------------------------- @@ -381,25 +381,8 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality %-Sort out visibility of appropriate controls on Menu window %----------------------------------------------------------------------- -Fmenu = spm_figure('FindWin','Menu'); -if ~isempty(Fmenu) - if strcmpi(Modality,'PET') - set(findobj(Fmenu, '-regexp', 'Tag', 'FMRI'), 'Visible', 'off'); - set(findobj(Fmenu, '-regexp', 'Tag', 'EEG'), 'Visible', 'off'); - set(findobj(Fmenu, '-regexp', 'Tag', 'PET'), 'Visible', 'on' ); - elseif strcmpi(Modality,'FMRI') - set(findobj(Fmenu, '-regexp', 'Tag', 'EEG'), 'Visible', 'off'); - set(findobj(Fmenu, '-regexp', 'Tag', 'PET'), 'Visible', 'off'); - set(findobj(Fmenu, '-regexp', 'Tag', 'FMRI'), 'Visible', 'on' ); - else - set(findobj(Fmenu, '-regexp', 'Tag', 'PET'), 'Visible', 'off'); - set(findobj(Fmenu, '-regexp', 'Tag', 'FMRI'), 'Visible', 'off'); - set(findobj(Fmenu, '-regexp', 'Tag', 'EEG'), 'Visible', 'on' ); - end - set(findobj(Fmenu,'Tag','Modality'),'Value',ModNum,'UserData',ModNum); -else - warning('SPM Menu window not found'); -end +spm_Menu('Switch',Modality); + %======================================================================= case 'defaults' %-Set SPM defaults (as global variable) @@ -426,10 +409,14 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality end clear ft_defaults clear global ft_default - ft_defaults; global ft_default ft_default.trackcallinfo = 'no'; ft_default.showcallinfo = 'no'; + ft_default.trackusage = 'no'; + ft_defaults; + ft_default.trackcallinfo = 'no'; + ft_default.showcallinfo = 'no'; + ft_default.trackusage = 'no'; ft_warning('off','backtrace'); if ~isdeployed addpath(... @@ -438,6 +425,7 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality fullfile(spm('Dir'),'external','eeprobe'),... fullfile(spm('Dir'),'external','mne'),... fullfile(spm('Dir'),'external','yokogawa_meg_reader'),... + fullfile(spm('Dir'),'external','ricoh_meg_reader'),... fullfile(spm('Dir'),'toolbox', 'dcm_meeg'),... fullfile(spm('Dir'),'toolbox', 'spectral'),... fullfile(spm('Dir'),'toolbox', 'Neural_Models'),... @@ -497,66 +485,7 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality %======================================================================= % Fmenu = spm('CreateMenuWin',Vis) %----------------------------------------------------------------------- -if nargin<2, Vis='on'; else Vis=varargin{2}; end - -%-Close any existing 'Menu' 'Tag'ged windows -%----------------------------------------------------------------------- -delete(spm_figure('FindWin','Menu')) -Fmenu = openfig(fullfile(spm('Dir'),'spm_Menu.fig'),'new','invisible'); -set(Fmenu,'name',[spm('Version') ': Menu']); -S0 = spm('WinSize','0',1); -SM = spm('WinSize','M'); -set(Fmenu,'Units','pixels', 'Position',[S0(1) S0(2) 0 0] + SM); - -%-Set SPM colour -%----------------------------------------------------------------------- -set(findobj(Fmenu,'Tag', 'frame'),'backgroundColor',spm('colour')); -set(Fmenu,'Color',[1 1 1]*.8); -if ismac - set(findobj(Fmenu,'UserData','LABEL'),'Visible','off','Tag',''); -end - -%-Set Utils -%----------------------------------------------------------------------- -set(findobj(Fmenu,'Tag', 'Utils'), 'String',{'Utils...',... - 'CD',... - 'PWD',... - 'Run M-file',... - 'Load MAT-file',... - 'Save MAT-file',... - 'Delete files',... - 'Show SPM',... - 'Show MATLAB'}); -set(findobj(Fmenu,'Tag', 'Utils'), 'UserData',{... - ['spm(''FnBanner'',''CD'');' ... - 'cd(spm_select(1,''dir'',''Select new working directory''));' ... - 'spm(''alert"'',{''New working directory:'',['' '',pwd]},''CD'',1);'],... - ['spm(''FnBanner'',''PWD'');' ... - 'spm(''alert"'',{''Present working directory:'',['' '',pwd]},''PWD'',1);'],... - ['spm(''FnBanner'',''Run M-file'');' ... - 'spm(''Run'');' ... - 'fprintf(''%-40s: %30s\n'',''Completed'',spm(''time''));'],... - ['spm(''FnBanner'',''Load MAT-file'');' ... - 'load(spm_select(1,''mat'',''Select MAT-file''));'],... - ['spm(''FnBanner'',''Save MAT-file'');' ... - 'save(spm_input(''Output filename'',1,''s''), spm_get_defaults(''mat.format''));'],... - ['spm(''FnBanner'',''Delete files'');' ... - 'spm(''Delete'');'],... - ['spm(''FnBanner'',''Show SPM'');' ... - 'spm(''Show'');'],... - ['spm(''FnBanner'',''Show Command Window'');' ... - 'commandwindow;']}); - -%-Set Toolboxes -%----------------------------------------------------------------------- -xTB = spm('tbs'); -if ~isempty(xTB) - set(findobj(Fmenu,'Tag', 'Toolbox'),'String',{'Toolbox:' xTB.name }); - set(findobj(Fmenu,'Tag', 'Toolbox'),'UserData',xTB); -else - set(findobj(Fmenu,'Tag', 'Toolbox'),'Visible','off') -end -set(Fmenu,'Visible',Vis); +Fmenu = spm_Menu('Create',varargin{2:end}); varargout = {Fmenu}; diff --git a/spm_BIDS.m b/spm_BIDS.m index 82bf00ba..110dae65 100644 --- a/spm_BIDS.m +++ b/spm_BIDS.m @@ -1,24 +1,25 @@ function varargout = spm_BIDS(varargin) -% Parse directory structure formated according to the BIDS standard +% Parse and query a directory structure formated according to the BIDS standard % FORMAT BIDS = spm_BIDS(root) % root - directory formated according to BIDS [Default: pwd] % BIDS - structure containing the BIDS file layout % % FORMAT result = spm_BIDS(BIDS,query,...) -% BIDS - BIDS directory name or structure containing the BIDS file layout -% query - type of query -% result - query's result +% BIDS - BIDS directory name or BIDS structure (from spm_BIDS) +% query - type of query: {'data', 'metadata', 'sessions', 'subjects', +% 'runs', 'tasks', 'runs', 'types', 'modalities'} +% result - outcome of query %__________________________________________________________________________ % -% BIDS (Brain Imaging Data Structure): http://bids.neuroimaging.io/ +% BIDS (Brain Imaging Data Structure): https://bids.neuroimaging.io/ % The brain imaging data structure, a format for organizing and % describing outputs of neuroimaging experiments. % K. J. Gorgolewski et al, Scientific Data, 2016. %__________________________________________________________________________ -% Copyright (C) 2016-2017 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2016-2018 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_BIDS.m 7120 2017-06-20 11:30:30Z spm $ +% $Id: spm_BIDS.m 7441 2018-10-11 08:56:40Z guillaume $ %-Validate input arguments @@ -134,16 +135,17 @@ %========================================================================== function subject = parse_subject(p, subjname, sesname) -subject.name = subjname; % subject name ('sub-') -subject.path = fullfile(p,subjname,sesname); % full path to subject directory +subject.name = subjname; % subject name ('sub-') +subject.path = fullfile(p,subjname,sesname); % full path to subject directory subject.session = sesname; % session name ('' or 'ses-