-
-
Save ilblog/11863f97f25a4c2478649f5b618fcc16 to your computer and use it in GitHub Desktop.
iOS crash code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! */ | |
W.define('airgram',['imageMaker','overlays'], (iMaker,overlays) => W.ImageMaker.instance({ | |
colors: iMaker.temp.fillColors, | |
altN: ['1000h', '950h','925h','900h','850h','800h','700h','600h','500h','400h','300h','200h','150h'], | |
alt: [ 1000.0, 950.0, 925.0, 900.0, 850.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 150.0 ], | |
step: 5.0, // isotherm step; | |
K0: 273.15, // 0°C in Kelvin | |
KMin: 193, // minimum temp value for scale [K] | |
colorBg: 'rgba(255,255,255,1)', // airgram background color | |
colorWind: 'rgba(0, 0, 0, 0.90)', // wind signs color | |
colorNoWind: 'rgba(0, 0, 0, 0.20)', // NO wind signs color | |
colorTemp: 'rgba(255, 255, 255, 0.8)', // temp values color | |
colorLegend: 'rgba(32, 32, 32, 0.50)', // text on border | |
colorHLines: 'rgba(0, 0, 0, 0.20)', // text on border | |
// Mouse movement properties | |
/* mouseHook: false, | |
el: null, | |
isShown: false, | |
_init: function() { | |
this.boundedMouse = this.onMouseMove.bind( this ) | |
this.debouncedDisplay = _.debounce( this.display.bind(this), 200 ) | |
}, | |
onMouseMove: function(e) { | |
if( this.isShown ) this.hideInfo() | |
if( e && e.layerX ) { | |
this.debouncedDisplay( e.layerX, e.layerY - canvasOffset ) | |
} | |
console.log( 'offsets: ' + this.el.offsetLeft + ', ' + this.el.offsetTop, | |
'page: ' + e.pageX + ',' + e.pageY, | |
'layer: ' + e.layerX + ',' + e.layerY, | |
'client: ' + e.clientX + ',' + e.clientY, | |
'screen: ' + e.screenX + ',' + e.screenY) | |
}, | |
hideInfo: function() { | |
this.show = false | |
console.log('HIDE') | |
}, | |
display: function(x,y) { | |
this.show = true | |
var res = this.airgramPoint(x,y) | |
console.log(x,y,res) | |
}, | |
addMouseHook: function(el) { | |
if(!el) return; | |
this.mouseHook = true; | |
this.el = el; | |
this.el.addEventListener('mousemove',this.boundedMouse,true) | |
}, | |
removeMouseHook: function() { | |
this.mouseHook = false; | |
this.el.removeEventListener('mousemove',this.boundedMouse,true) | |
},*/ | |
lerpColor256( a, b, f ) { | |
var n = a.length; | |
var c = []; | |
for( var i = 0; i < n; ++i ) { | |
c.push( Math.min( Math.max( 0, Math.round( a[i] + f * (b[i] - a[i]) ) ), 255) ); | |
} | |
return c; | |
}, | |
prepareStepColors() { | |
var a = [] | |
, step = this.step | |
, fw = 0.3 | |
, n0 = 1 + Math.floor( (this.K0 - this.KMin) / step ) | |
, t = this.K0 - ( step * n0 ) // initial temperature value for table | |
, t1 = 328.0 // target temp value | |
, gradient = this.colors | |
, n = gradient.length | |
this.tmin_ = t | |
//t += 2.5 | |
t += 0.5 * step | |
while( t < t1 ) { | |
for (let j = 1; j < n; ++j) { | |
if( t < gradient[j][0] ) { | |
let f = ( t - gradient[j - 1][0] ) / ( gradient[j][0] - gradient[j - 1][0] ) | |
a.push( this.lerpColor256( this.lerpColor256( gradient[j - 1][1], gradient[j][1], f), [160, 160, 160, 255], fw ) ) | |
break | |
} | |
} | |
t += step | |
} | |
this.n0_ = n0 - 1 | |
this.n1_ = n0 | |
this.steps_ = a | |
}, | |
// @params .. { .., airSegWidth, airHeight, airBorders, ... } | |
// @json2 .. meteogram API result | |
// @heigth .. airgram height | |
// @params.tdWidth .. segment size in normal pixels (NOT scaled by pixel ratio) | |
render( json2 ) { | |
if( ! this.steps_ ) this.prepareStepColors(); | |
// var height = this.h * this.canvasRatio | |
// , segW = this.tdWidth * this.canvasRatio | |
var height = this.getPixelRatioAdjustedSize( this.h ) | |
, segW = this.getPixelRatioAdjustedSize( this.tdWidth ) | |
var data = json2.data | |
, nx = data["temp-1000h"].length | |
, ny = this.altN.length | |
, n = nx * ny | |
, tempArray = new Float32Array( n ) // 2D array of temp values | |
, p = 0 | |
for( let j = 0; j < ny; ++j ) { | |
let nameT = 'temp-' + this.altN[ j ] | |
, rowT = data[ nameT ]; | |
for( let i = 0; i < nx; ++i ) { | |
tempArray[ p++ ] = Number( rowT[i] ); | |
} | |
} | |
// render temp gradient and return height lookup table for point function | |
var hLut = this.renderTemp( nx, ny, segW, height, tempArray ); | |
// render legend before wind marks | |
this.legend( nx, ny, this.tdWidth, this.h ); | |
// WIND | |
var windArray = new Float32Array( n + n ) // array of wind [U,V] values | |
p = 0 | |
for( let j = 0; j < ny; ++j ) { | |
let name1 = 'wind_u-' + this.altN[ j ] | |
, name2 = 'wind_v-' + this.altN[ j ] | |
, row1 = data[ name1 ] | |
, row2 = data[ name2 ]; | |
for( let i = 0; i < nx; ++i ) { | |
windArray[ p++ ] = Number( row1[ i ] ); | |
windArray[ p++ ] = Number( row2[ i ] ); | |
} | |
} | |
this.renderWind( nx, ny, this.tdWidth, this.h, windArray ); | |
// store data for point function | |
this.preparePData( nx, ny, this.tdWidth, this.w, this.h, hLut, data, tempArray, windArray ); | |
return this | |
}, | |
// modify pixels on edges | |
// @a .. pixel values array | |
edge( a, d, i ) { | |
// get core | |
var m = [ a[i-1-d], a[i-d], a[i+1-d], | |
a[i-1], a[i], a[i+1], | |
a[i-1+d], a[i+d], a[i+1+d] ]; | |
var f = 0.0; | |
if( m[1] != m[7] ) f += 2.0; | |
if( m[3] != m[5] ) f += 2.0; | |
if( m[0] != m[6] ) f += 1.0; | |
if( m[2] != m[8] ) f += 1.0; | |
if( m[0] != m[2] ) f += 1.0; | |
if( m[6] != m[8] ) f += 1.0; | |
var b1 = false, b2 = false; | |
for( var j = 0; j < m.length; j++ ) { | |
if( m[j] == this.n0_ ) b1 = true; | |
if( m[j] == this.n1_ ) b2 = true; | |
} | |
if(b1 && b2) { | |
f = Math.min(2.0 + f * 48.0, 128.0); | |
} else { | |
f = Math.max(1.0 - f * 0.03, 0.05); | |
} | |
return f; | |
}, | |
renderTemp( nx, ny, sx, h, a ) { | |
var ctx = this.ctx | |
, tAdd = -this.tmin_ | |
, tMul = 1.0 / this.step | |
var w = nx * sx | |
, imageData = ctx.createImageData( w, h ) | |
, data = imageData.data | |
, size = w * h | |
, dst = new Uint8Array( size ) | |
, hAdd = -150.0 | |
, hMul = (h - 1) / ( 1000.0 - 150.0 ) | |
, y1 = h | |
, y0 | |
, dx2 = (sx + 1) >> 1 | |
, hli = h + h | |
, hLut = new Int32Array( hli ); | |
for (let j = 0; j < ny - 1; ++j) { | |
y0 = y1 | |
y1 = Math.round( (this.alt[j + 1] + hAdd) * hMul ) | |
let j0 = nx * this.clamp0X(j + 2, ny) | |
, j1 = nx * this.clamp0X(j + 1, ny) | |
, j2 = nx * this.clamp0X(j + 0, ny) | |
, j3 = nx * this.clamp0X(j - 1, ny) | |
, x0 = 0 | |
, x1 = dx2 | |
, dy = y0 - y1 | |
, fy = 1.0 / dy | |
for (let i = 0; i < nx + 1; ++i) { | |
let i0 = this.clamp0X(i - 2, nx) | |
, i1 = this.clamp0X(i - 1, nx) | |
, i2 = this.clamp0X(i + 0, nx) | |
, i3 = this.clamp0X(i + 1, nx) | |
, m = [ a[j0 + i0], a[j0 + i1], a[j0 + i2], a[j0 + i3], | |
a[j1 + i0], a[j1 + i1], a[j1 + i2], a[j1 + i3], | |
a[j2 + i0], a[j2 + i1], a[j2 + i2], a[j2 + i3], | |
a[j3 + i0], a[j3 + i1], a[j3 + i2], a[j3 + i3] | |
] | |
, dx = x1 - x0 | |
, fx = 1.0 / dx | |
, offs = (y1 * w + x0) // top-left corner of renctangle to fill | |
for (let y = 0; y < dy; ++y) { | |
if( i == 0 ) { // prepare height LUT for point data | |
hLut[ --hli ] = j; | |
hLut[ --hli ] = Math.round( y * fy * 10000 ); | |
//hLut[ --hli ] = [j, y * fy] | |
} | |
let o = offs + y * w; // * 4); | |
for (let x = 0; x < dx && o < dst.length; ++x, o++) { | |
let c =this.bicubicFiltering( m, fx * x, fy * ( y + 0 ) ) | |
dst[ o ] = Math.floor( (c + tAdd) * tMul ) | |
} | |
} | |
x0 = x1 | |
x1 += sx | |
if( x1 > w ) { | |
x1 = w | |
} | |
} | |
} | |
const len = ( data.length - 4 ) // last item | |
var p = 0 | |
// | |
// INSIDE THIS LOOPS IT CRASHES!!!!!!! ALWAYS ON DIFFERENT INDEXes | |
// | |
for( let j = 0; j < h; j++ ) { | |
if(p >= len) break | |
for( let i = 0; i < w; i++ ) { | |
if(p >= len) break | |
let o = j * w + i | |
var br = 1.0 | |
if( (j > 0) && (j < (h - 1)) && (i > 0) && (i < w - 1) ) { | |
br = this.edge( dst, w, o ) | |
} | |
var rgb = this.steps_[ dst[o] ] || [0, 0, 0] | |
if( br < 0.99 ) { | |
data[p++] = Math.round(rgb[0] * br) | |
data[p++] = Math.round(rgb[1] * br) | |
data[p++] = Math.round(rgb[2] * br) | |
} | |
else if( br > 2.0 ) { | |
data[p++] = Math.min(rgb[0] + br, 255) | |
data[p++] = Math.min(rgb[1] + br, 255) | |
data[p++] = Math.min(rgb[2] + br, 255) | |
} | |
else { | |
data[p++] = rgb[0] | |
data[p++] = rgb[1] | |
data[p++] = rgb[2] | |
} | |
data[p++] = 255; | |
} | |
} | |
// | |
// Batched version (did not helped) | |
// | |
/* const maxBatchTime = 50 // Max batch time in ms | |
const batch = () => { | |
const limit = Date.now() + maxBatchTime | |
while( j < h ) { | |
j++ | |
if(p >= len) break | |
for( let i = 0; i < w; i++ ) { | |
if(p >= len) break | |
let o = j * w + i | |
var br = 1.0 | |
if( (j > 0) && (j < (h - 1)) && (i > 0) && (i < w - 1) ) { | |
br = this.edge( dst, w, o ) | |
} | |
var rgb = this.steps_[ dst[o] ] || [0, 0, 0] | |
if( br < 0.99 ) { | |
data[p++] = Math.round(rgb[0] * br) | |
data[p++] = Math.round(rgb[1] * br) | |
data[p++] = Math.round(rgb[2] * br) | |
} | |
else if( br > 2.0 ) { | |
data[p++] = Math.min(rgb[0] + br, 255) | |
data[p++] = Math.min(rgb[1] + br, 255) | |
data[p++] = Math.min(rgb[2] + br, 255) | |
} | |
else { | |
data[p++] = rgb[0] | |
data[p++] = rgb[1] | |
data[p++] = rgb[2] | |
} | |
data[p++] = 255; | |
} | |
if( !(j % 10) && Date.now() > limit ) { | |
ctx.putImageData( imageData, 0, 0 ); | |
setTimeout( batch, 250) | |
return | |
} | |
} | |
ctx.putImageData( imageData, 0, 0 ); | |
} | |
batch()*/ | |
// temp values | |
var | |
nx0 = 2 | |
, nxStep = 4 | |
, v0, v1 | |
let x = sx * nx0; | |
let dy = 12; | |
ctx.fillStyle = this.colorTemp; | |
ctx.textAlign = 'center'; | |
ctx.font = '8px Verdana'; | |
while( nx0 < nx ) { | |
let i = x; | |
y1 = 0; | |
for( let y = 0; y < h - dy / 2; y++ ) { | |
v0 = dst[i]; | |
if( v1 >= 0 ) { | |
if( v1 != v0 ) { | |
if( y - y1 > dy ) { | |
// render temp value at edge position | |
ctx.fillText( | |
this.convertTemp( Math.round( this.tmin_ + Math.max(v0, v1) * this.step ) ) + '°', | |
x / this.canvasRatio, | |
( y + dy * 0.35 ) / this.canvasRatio | |
); | |
// ctx.fillText( Math.round( this.tmin_ + Math.max(v0, v1) * this.step - this.K0 ) + '°', | |
// x + borders[0], y + borders[2] + dy * 0.35 ); // Celsius only variant | |
y1 = y; | |
} | |
} | |
} | |
v1 = v0; | |
i += w; | |
} | |
nx0 += nxStep; | |
x += nxStep * sx; | |
} | |
return hLut; | |
}, | |
// s ..size | |
windMark( ctx, x, y, u, v, s ) { | |
u = -u; | |
var d = Math.sqrt( u * u + v * v ) | |
, knotsI = Math.round( d * 0.388768 ) // conversion from m/s to 5-knots multimple | |
, s1 = s * 0.17 | |
, s2 = s * 0.35; | |
if( knotsI > 0 ) { | |
let u0 = u / d | |
, v0 = v / d | |
, x1 = u0 * s1 * 0.5 - v0 * s2 | |
, y1 = v0 * s1 * 0.5 + u0 * s2 | |
, dx = -s1 * u0 | |
, dy = -s1 * v0; | |
ctx.strokeStyle = ctx.fillStyle = this.colorWind; | |
ctx.beginPath(); | |
ctx.moveTo(x, y); | |
x += u0 * s; | |
y += v0 * s; | |
ctx.lineTo(x, y); | |
if( knotsI == 1 ) { // shift for 5 kn mark | |
x += dx; | |
y += dy; | |
} | |
if ( knotsI >= 10 ) { | |
while( knotsI >= 10 ) { // fat mark (50 kn) | |
knotsI -= 10; | |
ctx.lineTo(x + dx + x1, y + dy + y1 ); | |
ctx.lineTo(x + dx, y + dy ); | |
ctx.fill(); | |
x += dx; | |
y += dy; | |
} | |
x += dx; | |
y += dy; | |
} | |
while( knotsI > 0 ) { // thin mark | |
ctx.moveTo(x, y) | |
let f = knotsI == 1 ? 0.5 : 1.0 // short or normal mark (5 kn or 10 kn) | |
ctx.lineTo(x + x1 * f, y + y1 * f ) | |
x += dx; y += dy | |
knotsI -= 2 | |
} | |
ctx.stroke() | |
} | |
else { | |
ctx.strokeStyle = ctx.fillStyle = this.colorNoWind | |
ctx.beginPath(); | |
ctx.arc( x, y, s * 0.07, 0, 2 * Math.PI, false); | |
ctx.stroke(); | |
ctx.beginPath(); | |
ctx.arc( x, y, s * 0.16, 0, 2 * Math.PI, false); | |
ctx.stroke(); | |
} | |
}, | |
// | |
// Render wind marks | |
// | |
renderWind( nx, ny, segW, h, a ) { | |
var ctx = this.ctx | |
, hAdd = -150.0 | |
, hMul = (h - 1) / ( 1000.0 - 150.0 ) | |
, dx2 = (segW + 1) >> 1 | |
, s1 = segW * 0.65 // normal wind mark size | |
, s2 = segW * 0.46 // smaller size for first and last column | |
, s3 = segW * 0.4 | |
, x, y, u, v, i, o | |
, altName, size | |
ctx.lineWidth = 1 | |
for( var j = 0; j < ny; ++j ) { | |
y = Math.round( ( this.alt[ j ] + hAdd ) * hMul ); | |
altName = this.altN[ j ] | |
// Skip these levels | |
if( /925h|150h/.test( altName ) ) continue; | |
for (i = 0; i < nx; ++i ) { | |
x = dx2 + segW * i | |
o = ( j * nx + i ) << 1 | |
u = a[ o ] | |
v = a[ o + 1 ] | |
if( /1000h/.test(altName) ) | |
size = s3 | |
else | |
size = ( (i === 0 && u < 0 ) || ( i === nx - 1 && u > 0 ) ) ? s2 : s1 | |
this.windMark( ctx, x, y, u, v, size ); | |
} | |
} | |
}, | |
legend( nx, ny, segW, h ) { | |
var ctx = this.ctx | |
, hAdd = -150.0 | |
, hMul = (h - 1) / ( 1000.0 - 150.0 ) | |
, y, i, j, s, x | |
, d = 2; | |
ctx.fillStyle = this.colorLegend; | |
ctx.font = '9px Verdana'; | |
ctx.setLineDash([2, 4]); | |
ctx.lineWidth = this.canvasRatio; | |
ctx.strokeStyle = this.colorHLines; | |
ctx.beginPath() | |
for( j = 0; j < 2; j++ ) { | |
ctx.textAlign = j ? 'right' : 'left'; | |
x = j === 0 ? d : this.w - d; | |
for( i = 0; i < this.altN.length - 1; i++ ) { | |
s = this.altN[i]; | |
if( s !== '925h' ) { | |
y = Math.round( (this.alt[i] + hAdd) * hMul ); | |
ctx.fillText( s, x, y + 3 ); | |
if( j && i ) { | |
ctx.moveTo( 30, y ); | |
ctx.lineTo( this.w - 30, y ); | |
} | |
} | |
} | |
} | |
ctx.stroke(); | |
ctx.setLineDash([]); | |
}, | |
// data for airgram point function | |
pdata: null, | |
// convert temperature to user units | |
convertTemp( t ) { | |
return Math.round( overlays.temp.convertNumber( t ) ) | |
}, | |
// prepare data for point function on airgram | |
preparePData( nx, ny, segmentWidth, imgWidth, imgHeight, hLut, data, tempArray, windArray ) { | |
this.nx_ = nx; | |
this.ny_ = ny; | |
this.segmentWidth_ = segmentWidth; | |
this.imgWidth_ = imgWidth; | |
this.imgHeight_ = imgHeight; | |
this.hLut_ = hLut; | |
this.tempArray_ = tempArray; | |
this.windArray_ = windArray; | |
// TODO: hr array? | |
}, | |
// @x, @y .. position inside image including borders (values scaled by canvasRatio) | |
// returns array [ temp, windU, windV ] | |
airgramPoint( x, y ) { | |
var w = this.imgWidth_ | |
, h = this.imgHeight_ | |
, nx = this.nx_ | |
, lerp = this.lerp | |
, offs | |
if( x >= 0 && x < w && y >= 0 && y < h ) { // is inside? | |
offs = y + y | |
var j = this.hLut_[ offs ] | |
, fy = 0.0001 * this.hLut_[ offs + 1 ] | |
, x1 = x - 0.5 * this.segmentWidth_ | |
, i = 0 | |
, fx = 0.0 | |
, ret = [] | |
if( x1 > 0 ) { | |
fx = x1 / this.segmentWidth_; | |
i = Math.floor( fx ); | |
if( i >= nx - 1 ) { | |
i = nx - 2; | |
fx = 1.0; | |
} | |
else { | |
fx -= i; | |
} | |
} | |
// | |
offs = j * nx + i; | |
ret.push( lerp( | |
lerp( this.tempArray_[ offs ], this.tempArray_[ offs + 1 ], fx ), | |
lerp( this.tempArray_[ offs + nx ], this.tempArray_[ offs + nx + 1 ], fx ), | |
fy | |
) ); | |
offs += offs; | |
for( var l = 0; l < 2; l++ ) { | |
ret.push( this.lerp( | |
lerp( this.windArray_[ offs + l ], this.windArray_[ offs + 2 + l ], fx ), | |
lerp( this.windArray_[ offs + nx + nx + l ], this.windArray_[ offs + nx + nx + 2 + l ], fx ), | |
fy | |
) ); | |
} | |
return ret; | |
} | |
return null // no data or border | |
} | |
})); | |
W.require(['airgram']) | |
// legend2( nx, ny, segW, h, a, borders ) { | |
// var ctx = this.ctx | |
// , b = this.canvasRatio > 1.5 | |
// , hAdd = -150.0 | |
// , hMul = (h - 1) / ( 1000.0 - 150.0 ) | |
// , h2 = 0.5 * this.h | |
// , y, i, s | |
// ctx.fillStyle = this.colorLegend; | |
// ctx.textAlign = 'left'; | |
// ctx.font = (b ? '16' : '8') + 'px Verdana'; | |
// for( i = 0; i < this.altN.length; i++ ) { | |
// s = this.altN[i]; | |
// if( s !== '925h' ) { | |
// y = Math.round( (this.alt[i] + hAdd) * hMul ) + borders[2] + ( b ? 5 : 3 ) | |
// ctx.fillText( s, this.w - borders[1], y ); | |
// } | |
// } | |
// // | |
// ctx.translate( h2, h2 ); | |
// ctx.rotate( -0.5 * Math.PI ); | |
// ctx.textAlign = 'center'; | |
// ctx.fillText( 'Temperature (°C), Wind (kt)', 0, borders[0] - h2 - ( b ? 5 : 3 ) ); | |
// ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform | |
// // | |
// ctx.font = (b ? '14' : '7') + 'px Verdana'; | |
// var hour = 0 | |
// , dh = 3 | |
// , x = borders[ 0 ] + 0.5 * segW | |
// y = h + borders[ 2 ] + (b ? 18 : 10 ); | |
// for( i = 0; i < nx; i++ ) { | |
// if( hour >= 24 || hour <= 0 ) { | |
// hour = 0; | |
// } | |
// else { | |
// ctx.fillText( ( '0' + hour ).slice( -2 ), x, y ); | |
// } | |
// hour += dh; | |
// x += segW; | |
// } | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment