@@ -1942,7 +1942,7 @@ export function createPolarAreas({ series, center, maxRadius }) {
19421942 middlePoint : { x : middleX , y : middleY }
19431943 } ;
19441944 } ) ;
1945-
1945+
19461946 return paths ;
19471947}
19481948
@@ -1963,6 +1963,137 @@ export function createShadesOfGrey(startColor, steps) {
19631963 return shades ;
19641964}
19651965
1966+ /**
1967+ * LTTB algorithm for a ds with coordinates
1968+ * @param {Array<Object> } data
1969+ * @param {number } threshold
1970+ * @returns {Array<Object> }
1971+ */
1972+ export function largestTriangleThreeBuckets ( { data, threshold } ) {
1973+ if ( threshold >= data . length || threshold < 3 ) {
1974+ return data ;
1975+ }
1976+ const sampled = [ ] ;
1977+ const bucketSize = ( data . length - 2 ) / ( threshold - 2 ) ;
1978+ let a = 0 ;
1979+ // First point as is
1980+ sampled . push ( data [ a ] ) ;
1981+ for ( let i = 0 ; i < threshold - 2 ; i += 1 ) {
1982+ const bucketStart = Math . floor ( ( i + 1 ) * bucketSize ) + 1 ;
1983+ const bucketEnd = Math . min ( Math . floor ( ( i + 2 ) * bucketSize ) + 1 , data . length ) ;
1984+ const bucket = data . slice ( bucketStart , bucketEnd ) ;
1985+ let averageX = 0 ;
1986+ let averageY = 0 ;
1987+ for ( const point of bucket ) {
1988+ averageX += point . x ;
1989+ averageY += point . y ;
1990+ }
1991+ averageX /= bucket . length ;
1992+ averageY /= bucket . length ;
1993+ let maxArea = - 1 ;
1994+ let nextA = a ;
1995+ for ( let j = bucketStart ; j < bucketEnd ; j += 1 ) {
1996+ const area = Math . abs (
1997+ ( data [ a ] . x - averageX ) * ( data [ j ] . y - data [ a ] . y ) -
1998+ ( data [ a ] . x - data [ j ] . x ) * ( averageY - data [ a ] . y )
1999+ ) ;
2000+ if ( area > maxArea ) {
2001+ maxArea = area ;
2002+ nextA = j ;
2003+ }
2004+ }
2005+ sampled . push ( data [ nextA ] ) ;
2006+ a = nextA ;
2007+ }
2008+ // Last point as is
2009+ sampled . push ( data [ data . length - 1 ] ) ;
2010+ return sampled ;
2011+ }
2012+
2013+ /**
2014+ * LTTB algorithm for an array of numbers.
2015+ * @param {Array<number> } data
2016+ * @param {number } threshold
2017+ * @returns {Array<number> }
2018+ */
2019+ export function largestTriangleThreeBucketsArray ( { data, threshold } ) {
2020+ if ( threshold >= data . length || threshold < 3 ) {
2021+ return data ;
2022+ }
2023+ const sampled = [ ] ;
2024+ const bucketSize = ( data . length - 2 ) / ( threshold - 2 ) ;
2025+ let a = 0 ;
2026+ // First point as is
2027+ sampled . push ( data [ a ] ) ;
2028+ for ( let i = 0 ; i < threshold - 2 ; i += 1 ) {
2029+ const bucketStart = Math . floor ( ( i + 1 ) * bucketSize ) + 1 ;
2030+ const bucketEnd = Math . min ( Math . floor ( ( i + 2 ) * bucketSize ) + 1 , data . length ) ;
2031+ const bucket = data . slice ( bucketStart , bucketEnd ) ;
2032+ const average = bucket . reduce ( ( a , b ) => a + b , 0 ) / bucket . length ;
2033+ let maxArea = - 1 ;
2034+ let nextA = a ;
2035+
2036+ for ( let j = bucketStart ; j < bucketEnd ; j += 1 ) {
2037+ const area = Math . abs ( ( data [ a ] - average ) * ( j - a ) ) ;
2038+ if ( area > maxArea ) {
2039+ maxArea = area ;
2040+ nextA = j ;
2041+ }
2042+ }
2043+ sampled . push ( data [ nextA ] ) ;
2044+ a = nextA ;
2045+ }
2046+ // Last point as is
2047+ sampled . push ( data [ data . length - 1 ] ) ;
2048+ return sampled ;
2049+ }
2050+
2051+ /**
2052+ * LTTB algorithm for an array of objects containing 'name' and 'value'.
2053+ * @param {Array<{ name: string, value: number }> } data
2054+ * @param {number } threshold
2055+ * @returns {Array<{ name: string, value: number }> }
2056+ */
2057+ export function largestTriangleThreeBucketsArrayObjects ( { data, threshold, key= 'value' } ) {
2058+ if ( threshold >= data . length || threshold < 3 ) {
2059+ return data ;
2060+ }
2061+
2062+ const sampled = [ ] ;
2063+ const bucketSize = ( data . length - 2 ) / ( threshold - 2 ) ;
2064+ let a = 0 ;
2065+
2066+ // First point as is
2067+ sampled . push ( data [ a ] ) ;
2068+
2069+ for ( let i = 0 ; i < threshold - 2 ; i += 1 ) {
2070+ const bucketStart = Math . floor ( ( i + 1 ) * bucketSize ) + 1 ;
2071+ const bucketEnd = Math . min ( Math . floor ( ( i + 2 ) * bucketSize ) + 1 , data . length ) ;
2072+ const bucket = data . slice ( bucketStart , bucketEnd ) ;
2073+
2074+ const average = bucket . reduce ( ( sum , point ) => sum + point [ key ] , 0 ) / bucket . length ;
2075+
2076+ let maxArea = - 1 ;
2077+ let nextA = a ;
2078+
2079+ for ( let j = bucketStart ; j < bucketEnd ; j += 1 ) {
2080+ const area = Math . abs ( ( data [ a ] [ key ] - average ) * ( j - a ) ) ;
2081+ if ( area > maxArea ) {
2082+ maxArea = area ;
2083+ nextA = j ;
2084+ }
2085+ }
2086+
2087+ sampled . push ( data [ nextA ] ) ;
2088+ a = nextA ;
2089+ }
2090+
2091+ sampled . push ( data [ data . length - 1 ] ) ;
2092+
2093+ return sampled ;
2094+ }
2095+
2096+
19662097const lib = {
19672098 XMLNS ,
19682099 abbreviate,
@@ -2016,6 +2147,8 @@ const lib = {
20162147 isFunction,
20172148 isSafeValue,
20182149 isValidUserValue,
2150+ largestTriangleThreeBucketsArray,
2151+ largestTriangleThreeBucketsArrayObjects,
20192152 lightenHexColor,
20202153 makeDonut,
20212154 makePath,
0 commit comments