diff --git a/apps/gipy/ChangeLog b/apps/gipy/ChangeLog index 5cd21bd45..d46d24c69 100644 --- a/apps/gipy/ChangeLog +++ b/apps/gipy/ChangeLog @@ -114,4 +114,17 @@ * Better position in elevation profiles * Integrating fixes in upstream rust heatshrink crate * Small path optimisations with brouter (removing looplets) - * Bugfix in nearest segment detection \ No newline at end of file + * Bugfix in nearest segment detection + +0.23: + * New display algorithm : way faster, larger roads, zooming out is now ok + * You will need to re-generate your maps because the new algorithm uses a different scale + * Better path simplification + * Removed sharp turns auto-detection + * Waypoints autodetection using the map : this will generate more points than really needed but can still be effective. + * New option: sleep between waypoints + * Interface: centered svg preview + display file sizes + * Interface: disable/enable waypoints detection and elevation + * Touching the screen when sleeping will wake up but not change screen + * Removing footways (if bicycles not allowed) to reduce resource usage + * Switching screen will clear the screen immediately so you can know your screen touch has been received \ No newline at end of file diff --git a/apps/gipy/README.md b/apps/gipy/README.md index 58040bfea..e247e954b 100644 --- a/apps/gipy/README.md +++ b/apps/gipy/README.md @@ -18,7 +18,6 @@ It provides the following features : - display the path with current position from gps - display a local map around you, downloaded from openstreetmap - detects and buzzes if you leave the path -- (optional) buzzes before sharp turns - (optional) buzzes before waypoints (for example when you need to turn in https://mapstogpx.com/) - display instant / average speed @@ -122,6 +121,7 @@ Few settings for now (feel free to suggest me more) : - power lcd off (disabled by default): turn lcd off when inactive to save power. the watch will wake up when reaching points, when you touch the screen and when speed is below 13km/h. - powersave by default: when gipy starts is powersaving activated ? (see below) +- sleep between waypoints: instead of powersaving between points save power between waypoints (crossroads). this way you can save more. waypoints autodetection is WIP. ### Powersaving @@ -142,7 +142,7 @@ The algorithm works in the following ways : Activation events are the following : -- you are near (< 100m) the next point on path +- you are near (< 100m) the next point (only waypoints of *sleep between waypoints* is on) on path - you are slow (< *wake-up speed* setting (13 km/h by default)) - you are lost - you press the button / touch the screen diff --git a/apps/gipy/TODO b/apps/gipy/TODO index 8c767c463..e5fe68240 100644 --- a/apps/gipy/TODO +++ b/apps/gipy/TODO @@ -1,3 +1,10 @@ + +urgent TODO: + +- prefetch tiles ? +- update documentation to reflect new display ? +- add an image for the arrow ? + *** thoughts on lcd power *** so, i tried experimenting with turning the lcd off in order to save power. diff --git a/apps/gipy/app.js b/apps/gipy/app.js index 8b7a12574..0a631613b 100644 --- a/apps/gipy/app.js +++ b/apps/gipy/app.js @@ -2,17 +2,16 @@ let simulated = false; let displaying = false; let in_menu = false; let go_backwards = false; -let zoomed = true; let status; let initial_options = Bangle.getOptions(); let interests_colors = [ - 0xffff, // Waypoints, white - 0xf800, // Bakery, red - 0x001f, // DrinkingWater, blue - 0x07ff, // Toilets, cyan - 0x07e0, // Artwork, green + [1, 1, 1], // Waypoints, white + [1, 0, 0], // Bakery, red + [0, 0, 1], // DrinkingWater, blue + [0, 1, 1], // Toilets, cyan + [0, 1, 0], // Artwork, green ]; let Y_OFFSET = 20; @@ -31,9 +30,10 @@ var settings = Object.assign( active_time: 10, brightness: 0.5, buzz_on_turns: false, - disable_bluetooth: true, + disable_bluetooth: false, power_lcd_off: false, powersave_by_default: false, + sleep_between_waypoints: false, }, s.readJSON("gipy.json", true) || {} ); @@ -43,13 +43,13 @@ let powersaving = settings.powersave_by_default; // let profile_start_times = []; // function start_profiling() { -// profile_start_times.push(getTime()); +// profile_start_times.push(getTime()); // } // function end_profiling(label) { -// let end_time = getTime(); -// let elapsed = end_time - profile_start_times.pop(); -// console.log("profile:", label, "took", elapsed); +// let end_time = getTime(); +// let elapsed = end_time - profile_start_times.pop(); +// console.log("profile:", label, "took", elapsed); // } // return the index of the largest element of the array which is <= x @@ -152,29 +152,8 @@ class TilesOffsets { } } -// this function is not inlined to avoid array declaration in jit -function center_points(points, scaled_current_x, scaled_current_y) { - return g.transformVertices(points, [ - 1, - 0, - 0, - 1, - -scaled_current_x, - -scaled_current_y, - ]); -} - -// this function is not inlined to avoid array declaration in jit -function rotate_points(points, c, s) { - let center_x = g.getWidth() / 2; - let center_y = g.getHeight() / 2 + Y_OFFSET; - - return g.transformVertices(points, [-c, s, s, c, center_x, center_y]); -} - class Map { constructor(buffer, offset, filename) { - this.points_cache = []; // don't refetch points all the time // header let color_array = Uint8Array(buffer, offset, 3); this.color = [ @@ -262,123 +241,16 @@ class Map { // offset += encoded_blocks_size; } - display( - displayed_x, - displayed_y, - scale_factor, - cos_direction, - sin_direction - ) { - g.setColor(this.color[0], this.color[1], this.color[2]); - let local_x = displayed_x - this.start_coordinates[0]; - let local_y = displayed_y - this.start_coordinates[1]; - let tile_x = Math.floor(local_x / this.side); - let tile_y = Math.floor(local_y / this.side); + add_to_tile_image(img, absolute_tile_x, absolute_tile_y) { + let tile_x = absolute_tile_x - this.first_tile[0]; + let tile_y = absolute_tile_y - this.first_tile[1]; + let side = img.getWidth() - 6; - let limit = 1; - if (!zoomed) { - limit = 2; - } - for (let y = tile_y - limit; y <= tile_y + limit; y++) { - if (y < 0 || y >= this.grid_size[1]) { - continue; - } - for (let x = tile_x - limit; x <= tile_x + limit; x++) { - if (x < 0 || x >= this.grid_size[0]) { - continue; - } - if ( - this.tile_is_on_screen( - x, - y, - local_x, - local_y, - scale_factor, - cos_direction, - sin_direction - ) - ) { - // let colors = [ - // [0, 0, 0], - // [0, 0, 1], - // [0, 1, 0], - // [0, 1, 1], - // [1, 0, 0], - // [1, 0, 1], - // [1, 1, 0], - // [1, 1, 0.5], - // [0.5, 0, 0.5], - // [0, 0.5, 0.5], - // ]; - if (this.color[0] == 1 && this.color[1] == 0 && this.color[2] == 0) { - this.display_thick_tile( - x, - y, - local_x, - local_y, - scale_factor, - cos_direction, - sin_direction - ); - } else { - this.display_tile( - x, - y, - local_x, - local_y, - scale_factor, - cos_direction, - sin_direction - ); - } - } - } - } - } + let thick = this.color[0] != 0; + img.setColor(this.color[0], this.color[1], this.color[2]); - tile_is_on_screen( - tile_x, - tile_y, - current_x, - current_y, - scale_factor, - cos_direction, - sin_direction - ) { - let width = g.getWidth(); - let height = g.getHeight(); - let center_x = width / 2; - let center_y = height / 2 + Y_OFFSET; - let side = this.side; - let tile_center_x = (tile_x + 0.5) * side; - let tile_center_y = (tile_y + 0.5) * side; - let scaled_center_x = (tile_center_x - current_x) * scale_factor; - let scaled_center_y = (tile_center_y - current_y) * scale_factor; - let rotated_center_x = - scaled_center_x * cos_direction - scaled_center_y * sin_direction; - let rotated_center_y = - scaled_center_x * sin_direction + scaled_center_y * cos_direction; - let on_screen_center_x = center_x - rotated_center_x; - let on_screen_center_y = center_y + rotated_center_y; + let tile_num = tile_x + tile_y * this.grid_size[0]; - let scaled_side = side * scale_factor * Math.sqrt(1 / 2); - - if (on_screen_center_x + scaled_side <= 0) { - return false; - } - if (on_screen_center_x - scaled_side >= width) { - return false; - } - if (on_screen_center_y + scaled_side <= 0) { - return false; - } - if (on_screen_center_y - scaled_side >= height) { - return false; - } - return true; - } - - tile_points(tile_num, tile_x, tile_y, scaled_side) { let line_start_offset = this.tiles_offsets.tile_start_offset( tile_y * this.grid_size[0] ); @@ -388,117 +260,37 @@ class Map { this.tiles_offsets.tile_end_offset(tile_num) - line_start_offset; let line = this.binary_lines[tile_y]; - // we need to copy both for correct results and for performances - // let's precompute also. - let cached_tile = new Float64Array(upper_limit - offset); - for (let i = offset; i < upper_limit; i += 2) { - let x = (tile_x + line.buffer[i] / 255) * scaled_side; - let y = (tile_y + line.buffer[i + 1] / 255) * scaled_side; - cached_tile[i - offset] = x; - cached_tile[i + 1 - offset] = y; - } - return cached_tile; - } + for (let i = offset; i < upper_limit; i += 4) { + let x1 = (line.buffer[i] / 255) * side + 3; + let y1 = ((255 - line.buffer[i + 1]) / 255) * side + 3; + let x2 = (line.buffer[i + 2] / 255) * side + 3; + let y2 = ((255 - line.buffer[i + 3]) / 255) * side + 3; - invalidate_caches() { - this.points_cache = []; - } - - fetch_points(tile_x, tile_y, scaled_side) { - let tile_num = tile_x + tile_y * this.grid_size[0]; - for (let i = 0; i < this.points_cache.length; i++) { - if (this.points_cache[i][0] == tile_num) { - return this.points_cache[i][1]; + let thickness = 1; + if (thick) { + thickness = 3.5; } - } - if (this.points_cache.length > 40) { - this.points_cache.shift(); - } - let points = this.tile_points(tile_num, tile_x, tile_y, scaled_side); - this.points_cache.push([tile_num, points]); - return points; - } - display_tile( - tile_x, - tile_y, - current_x, - current_y, - scale_factor, - cos_direction, - sin_direction - ) { - "jit"; - let points = this.fetch_points(tile_x, tile_y, this.side * scale_factor); - let scaled_current_x = current_x * scale_factor; - let scaled_current_y = current_y * scale_factor; - let recentered_points = center_points( - points, - scaled_current_x, - scaled_current_y - ); - let screen_points = rotate_points( - recentered_points, - cos_direction, - sin_direction - ); - - for (let i = 0; i < screen_points.length; i += 4) { - g.drawLine( - screen_points[i], - screen_points[i + 1], - screen_points[i + 2], - screen_points[i + 3] - ); - } - } - - display_thick_tile( - tile_x, - tile_y, - current_x, - current_y, - scale_factor, - cos_direction, - sin_direction - ) { - "jit"; - - let points = this.fetch_points(tile_x, tile_y, this.side * scale_factor); - let scaled_current_x = current_x * scale_factor; - let scaled_current_y = current_y * scale_factor; - let recentered_points = center_points( - points, - scaled_current_x, - scaled_current_y - ); - let screen_points = rotate_points( - recentered_points, - cos_direction, - sin_direction - ); - - for (let i = 0; i < screen_points.length; i += 4) { - let final_x = screen_points[i]; - let final_y = screen_points[i + 1]; - let new_final_x = screen_points[i + 2]; - let new_final_y = screen_points[i + 3]; - - let xdiff = new_final_x - final_x; - let ydiff = new_final_y - final_y; + let xdiff = x2 - x1; + let ydiff = y2 - y1; let d = Math.sqrt(xdiff * xdiff + ydiff * ydiff); - let ox = (-ydiff / d) * 3; - let oy = (xdiff / d) * 3; - g.fillPoly([ - final_x + ox, - final_y + oy, - new_final_x + ox, - new_final_y + oy, - new_final_x - ox, - new_final_y - oy, - final_x - ox, - final_y - oy, + let ox = (-ydiff / d) * thickness; + let oy = (xdiff / d) * thickness; + img.fillPoly([ + x1 + ox, + y1 + oy, + x2 + ox, + y2 + oy, + x2 - ox, + y2 - oy, + x1 - ox, + y1 - oy, ]); + + // } else { + // img.drawLine(x1, y1, x2, y2); + + // } } } } @@ -525,112 +317,30 @@ class Interests { this.binary_interests[i] = binary_interests[i]; } offset += end; - this.points_cache = []; return [this, offset]; } - display( - displayed_x, - displayed_y, - scale_factor, - cos_direction, - sin_direction - ) { - let local_x = displayed_x - this.start_coordinates[0]; - let local_y = displayed_y - this.start_coordinates[1]; - let tile_x = Math.floor(local_x / this.side); - let tile_y = Math.floor(local_y / this.side); - for (let y = tile_y - 1; y <= tile_y + 1; y++) { - if (y < 0 || y >= this.grid_size[1]) { - continue; - } - for (let x = tile_x - 1; x <= tile_x + 1; x++) { - if (x < 0 || x >= this.grid_size[0]) { - continue; - } - this.display_tile( - x, - y, - local_x, - local_y, - scale_factor, - cos_direction, - sin_direction - ); - } - } - } + add_to_tile_image(img, absolute_tile_x, absolute_tile_y) { + let tile_x = absolute_tile_x - this.first_tile[0]; + let tile_y = absolute_tile_y - this.first_tile[1]; + let side = img.getWidth() - 6; + + let tile_num = tile_x + tile_y * this.grid_size[0]; - tile_points(tile_num, tile_x, tile_y, scaled_side) { let offset = this.offsets.tile_start_offset(tile_num); let upper_limit = this.offsets.tile_end_offset(tile_num); - let tile_interests = []; + let buffer = this.binary_interests; for (let i = offset; i < upper_limit; i += 3) { - let interest = this.binary_interests[i]; - let x = (tile_x + this.binary_interests[i + 1] / 255) * scaled_side; - let y = (tile_y + this.binary_interests[i + 2] / 255) * scaled_side; - if (interest >= interests_colors.length) { - throw "bad interest" + interest + "at" + tile_num + "offset" + i; - } - tile_interests.push(interest); - tile_interests.push(x); - tile_interests.push(y); - } - return tile_interests; - } - fetch_points(tile_x, tile_y, scaled_side) { - //TODO: factorize with map ? - let tile_num = tile_x + tile_y * this.grid_size[0]; - for (let i = 0; i < this.points_cache.length; i++) { - if (this.points_cache[i][0] == tile_num) { - return this.points_cache[i][1]; - } - } - if (this.points_cache.length > 40) { - this.points_cache.shift(); - } - let points = this.tile_points(tile_num, tile_x, tile_y, scaled_side); - this.points_cache.push([tile_num, points]); - return points; - } - invalidate_caches() { - this.points_cache = []; - } - display_tile( - tile_x, - tile_y, - displayed_x, - displayed_y, - scale_factor, - cos_direction, - sin_direction - ) { - let width = g.getWidth(); - let half_width = width / 2; - let half_height = g.getHeight() / 2 + Y_OFFSET; - let interests = this.fetch_points(tile_x, tile_y, this.side * scale_factor); - - let scaled_current_x = displayed_x * scale_factor; - let scaled_current_y = displayed_y * scale_factor; - - for (let i = 0; i < interests.length; i += 3) { - let type = interests[i]; - let x = interests[i + 1]; - let y = interests[i + 2]; - - let scaled_x = x - scaled_current_x; - let scaled_y = y - scaled_current_y; - let rotated_x = scaled_x * cos_direction - scaled_y * sin_direction; - let rotated_y = scaled_x * sin_direction + scaled_y * cos_direction; - let final_x = half_width - rotated_x; - let final_y = half_height + rotated_y; + let type = buffer[i]; + let x = (buffer[i + 1] / 255) * side + 3; + let y = ((255 - buffer[i + 2]) / 255) * side + 3; let color = interests_colors[type]; if (type == 0) { - g.setColor(0, 0, 0).fillCircle(final_x, final_y, 6); + img.setColor(0, 0, 0).fillCircle(x, y, 6); } - g.setColor(color).fillCircle(final_x, final_y, 5); + img.setColor(color[0], color[1], color[2]).fillCircle(x, y, 5); } } } @@ -645,21 +355,23 @@ class Status { this.interests = interests; this.heights = heights; this.screen = MAP; - let half_screen_width = g.getWidth() / 2; - let half_screen_height = g.getHeight() / 2; - let half_screen_diagonal = Math.sqrt( - half_screen_width * half_screen_width + - half_screen_height * half_screen_height - ); - this.scale_factor = half_screen_diagonal / maps[0].side; // multiply geo coordinates by this to get pixels coordinates this.on_path = true; // are we on the path or lost ? this.position = null; // where we are - this.adjusted_cos_direction = 1; // cos of where we look at - this.adjusted_sin_direction = 0; // sin of where we look at + this.direction = 0; + this.adjusted_cos_direction = Math.cos(-Math.PI / 2.0); + this.adjusted_sin_direction = Math.sin(-Math.PI / 2.0); + this.zoomed_in = true; + this.current_segment = null; // which segment is closest this.reaching = null; // which waypoint are we reaching ? this.distance_to_next_point = null; // how far are we from next point ? this.projected_point = null; + this.reset_images_cache(); + + let width = g.getWidth(); + let height = g.getHeight(); + let diagonal_third = Math.sqrt(width * width + height * height) / 3; + this.scale_factor = diagonal_third / maps[0].side; // multiply geo coordinates by this to get pixels coordinates if (this.path !== null) { let r = [0]; @@ -681,6 +393,28 @@ class Status { this.old_points = []; // record previous points but only when enough distance between them this.old_times = []; // the corresponding times } + reset_images_cache() { + let tiles_per_diagonals = this.zoomed_in ? 3 : 5; + let screen_width = g.getWidth(); + let screen_height = g.getHeight(); + this.images_cache = []; + + let img_side = + Math.ceil( + Math.sqrt(screen_width * screen_width + screen_height * screen_height) / + tiles_per_diagonals + ) + 6; // three extra pixels on each side to allow thick lines + + E.defrag(); + let limit = tiles_per_diagonals * (tiles_per_diagonals + 1); + + for (let i = 0; i < limit; i++) { + let img = Graphics.createArrayBuffer(img_side, img_side, 4, { + msb: true, + }); + this.images_cache.push({ image: img, x: -1, y: -1 }); + } + } activate() { if (!powersaving) { return; @@ -709,14 +443,6 @@ class Status { } } } - invalidate_caches() { - for (let i = 0; i < this.maps.length; i++) { - this.maps[i].invalidate_caches(); - } - if (this.interests !== null) { - this.interests.invalidate_caches(); - } - } new_position_reached(position) { // we try to figure out direction by looking at previous points // instead of the gps course which is not very nice. @@ -746,11 +472,14 @@ class Status { let oldest_point = this.old_points[0]; let distance_to_oldest = oldest_point.distance(position); + let time_to_oldest = now - this.old_times[0]; // every 3 points we count the distance if (this.gps_coordinates_counter % 3 == 0) { - if (distance_to_oldest < 150.0) { - // to avoid gps glitches + if (time_to_oldest > 6 || distance_to_oldest < 150.0) { + // to avoid gps glitches (sometimes the gps signal will make you jump around) + // however if gps disconnects (time_to_oldest becomes large) we still count the distance + // when it re-activates this.advanced_distance += distance_to_oldest; } } @@ -778,6 +507,7 @@ class Status { } return; } + this.direction = direction; if (in_menu) { return; } @@ -877,14 +607,15 @@ class Status { // }, time_to_next_point); // } // } + let reaching_waypoint = this.path.is_waypoint(next_point); if (this.distance_to_next_point <= 100) { - this.activate(); - } - if (this.reaching != next_point && this.distance_to_next_point <= 100) { - this.reaching = next_point; - let reaching_waypoint = this.path.is_waypoint(next_point); - if (reaching_waypoint) { - if (settings.buzz_on_turns) { + if (reaching_waypoint || !settings.sleep_between_waypoints) { + this.activate(); + } + + if (this.reaching != next_point) { + this.reaching = next_point; + if (reaching_waypoint && settings.buzz_on_turns) { Bangle.buzz(); setTimeout(() => Bangle.buzz(), 500); setTimeout(() => Bangle.buzz(), 1000); @@ -922,21 +653,12 @@ class Status { direction = Math.atan2(diff.lat, diff.lon); let full_angle = direction - this.angle; - // let c = towards.coordinates(p, this.adjusted_cos_direction, this.adjusted_sin_direction, this.scale_factor); - // g.setColor(1,0,1).fillCircle(c[0], c[1], 5); - - let scale; - if (zoomed) { - scale = this.scale_factor; - } else { - scale = this.scale_factor / 2; - } c = this.projected_point.coordinates( this.displayed_position, this.adjusted_cos_direction, this.adjusted_sin_direction, - scale + this.scale_factor ); let cos1 = Math.cos(full_angle + 0.6 + Math.PI / 2); @@ -990,14 +712,97 @@ class Status { return false; } } + + tile_image(absolute_tile_x, absolute_tile_y) { + // in the cache old images are front and recently used ones are back + let cached_img_index = this.images_cache.findIndex((i) => { + return i.x == absolute_tile_x && i.y == absolute_tile_y; + }); + if (cached_img_index == -1) { + // console.log("loading", absolute_tile_x, absolute_tile_y); + let old_image = this.images_cache.shift(); + this.compute_tile_image(old_image.image, absolute_tile_x, absolute_tile_y); + this.images_cache.push({ + image: old_image.image, + x: absolute_tile_x, + y: absolute_tile_y, + }); + return old_image.image; + } else { + let cached_img = this.images_cache.splice(cached_img_index, 1)[0]; + this.images_cache.push(cached_img); + return cached_img.image; + } + } + + compute_tile_image(img, absolute_tile_x, absolute_tile_y) { + img.transparent = img.toColor(1, 1, 1); + img.setBgColor(1, 1, 1).clear(); + + this.maps.forEach((m) => { + m.add_to_tile_image(img, absolute_tile_x, absolute_tile_y); + }); + this.interests.add_to_tile_image(img, absolute_tile_x, absolute_tile_y); + } + + display_map() { + + // start_profiling(); + let displayed_x = this.displayed_position.lon; + let displayed_y = this.displayed_position.lat; + let tile_x_coord = displayed_x / this.maps[0].side; + let tile_y_coord = displayed_y / this.maps[0].side; + let absolute_tile_x = Math.floor(tile_x_coord); + let absolute_tile_y = Math.floor(tile_y_coord); + + let tiles_per_diagonals = this.zoomed_in ? 3 : 5; + let diagonal = Math.ceil( + Math.sqrt(g.getWidth() * g.getWidth() + g.getHeight() * g.getHeight()) / + tiles_per_diagonals + ); + let angle = this.direction - Math.PI / 2; + let cos_direction = Math.cos(angle); + let sin_direction = Math.sin(angle); + let d = Math.floor(tiles_per_diagonals / 2); + + for (let x = -d; x <= d; x++) { + for (let y = -d; y <= d; y++) { + let img = this.tile_image(absolute_tile_x + x, absolute_tile_y + y); + + let screen_x = + (absolute_tile_x + x + 0.5 - tile_x_coord) * diagonal + 3; + let screen_y = + -(absolute_tile_y + y + 0.5 - tile_y_coord) * diagonal - 3; + + let rotated_x = screen_x * cos_direction - screen_y * sin_direction; + let rotated_y = screen_x * sin_direction + screen_y * cos_direction; + let final_x = g.getWidth() / 2 + rotated_x; + let final_y = g.getHeight() / 2 + Y_OFFSET + rotated_y; + + g.drawImage(img, final_x, final_y, { rotate: angle }); + } + } + // end_profiling("map display"); + } display() { if (displaying || in_menu) { return; // don't draw on drawings } + g.reset(); displaying = true; g.clear(); if (this.screen == MAP) { this.display_map(); + if (this.position !== null) { + // start_profiling(); + this.display_path(); + // end_profiling("path display"); + } + + // start_profiling(); + this.display_direction(); + this.display_stats(); + // end_profiling("direction and stats display"); } else { let current_position = 0; if (this.current_segment !== null) { @@ -1023,6 +828,7 @@ class Status { } } Bangle.drawWidgets(); + g.flip(); displaying = false; } display_heights(display_start, current_position, displayed_length) { @@ -1175,39 +981,6 @@ class Status { widgets_height ); } - display_map() { - let scale_factor = this.scale_factor; - if (!zoomed) { - scale_factor /= 2; - } - - // start_profiling(); - for (let i = 0; i < this.maps.length; i++) { - this.maps[i].display( - this.displayed_position.lon, - this.displayed_position.lat, - scale_factor, - this.adjusted_cos_direction, - this.adjusted_sin_direction - ); - } - // end_profiling("map"); - if (this.interests !== null) { - this.interests.display( - this.displayed_position.lon, - this.displayed_position.lat, - scale_factor, - this.adjusted_cos_direction, - this.adjusted_sin_direction - ); - } - if (this.position !== null) { - this.display_path(); - } - - this.display_direction(); - this.display_stats(); - } display_stats() { let now = new Date(); let minutes = now.getMinutes().toString(); @@ -1303,9 +1076,6 @@ class Status { let half_width = width / 2; let half_height = height / 2 + Y_OFFSET; let scale_factor = this.scale_factor; - if (!zoomed) { - scale_factor /= 2; - } if (this.path !== null) { // compute coordinate for projection on path @@ -1424,8 +1194,6 @@ function load_gps(filename) { class Path { constructor(buffer, offset) { - // let p = Uint16Array(buffer, offset, 1); - // console.log(p); let points_number = Uint16Array(buffer, offset, 1)[0]; offset += 2; @@ -1640,11 +1408,11 @@ function start_gipy(path, maps, interests, heights) { }, }, Zoom: { - value: zoomed, + value: status.zoomed_in, format: (v) => (v ? "In" : "Out"), onchange: (v) => { - status.invalidate_caches(); - zoomed = v; + status.zoomed_in = v; + status.reset_images_cache(); }, }, /*LANG*/ @@ -1687,11 +1455,14 @@ function start_gipy(path, maps, interests, heights) { status.display(); Bangle.on("touch", () => { + let active = status.active; status.activate(); if (in_menu) { return; } - if (status.heights !== null) { + if (active && status.heights !== null) { + g.clear(); + g.flip(); status.screen = (status.screen + 1) % 3; status.display(); } @@ -1720,51 +1491,52 @@ function start_gipy(path, maps, interests, heights) { }); if (simulated) { - status.starting_time = getTime(); - // let's keep the screen on in simulations - Bangle.setLCDTimeout(0); - Bangle.setLCDPower(1); - Bangle.loadWidgets(); // i don't know why i cannot load them at start : they would display on splash screen + // status.starting_time = getTime(); + // // let's keep the screen on in simulations + // Bangle.setLCDTimeout(0); + // Bangle.setLCDPower(1); + // Bangle.loadWidgets(); // i don't know why i cannot load them at start : they would display on splash screen - function simulate_gps(status) { - if (status.path === null) { - let map = status.maps[0]; - let p1 = new Point(map.start_coordinates[0], map.start_coordinates[1]); - let p2 = new Point( - map.start_coordinates[0] + map.side * map.grid_size[0], - map.start_coordinates[1] + map.side * map.grid_size[1] - ); - let pos = p1.times(1 - fake_gps_point).plus(p2.times(fake_gps_point)); - if (fake_gps_point < 1) { - fake_gps_point += 0.05; - } - status.update_position(pos); - } else { - if (fake_gps_point > status.path.len - 1 || fake_gps_point < 0) { - return; - } - let point_index = Math.floor(fake_gps_point); - if (point_index >= status.path.len / 2 - 1) { - return; - } - let p1 = status.path.point(2 * point_index); // use these to approximately follow path - let p2 = status.path.point(2 * (point_index + 1)); - //let p1 = status.path.point(point_index); // use these to strictly follow path - //let p2 = status.path.point(point_index + 1); + // function simulate_gps(status) { + // if (status.path === null) { + // let map = status.maps[0]; + // let p1 = new Point(map.start_coordinates[0], map.start_coordinates[1]); + // let p2 = new Point( + // map.start_coordinates[0] + map.side * map.grid_size[0], + // map.start_coordinates[1] + map.side * map.grid_size[1] + // ); + // let pos = p1.times(1 - fake_gps_point).plus(p2.times(fake_gps_point)); + // if (fake_gps_point < 1) { + // fake_gps_point += 0.05; + // } + // status.update_position(pos); + // } else { + // if (fake_gps_point > status.path.len - 1 || fake_gps_point < 0) { + // return; + // } + // let point_index = Math.floor(fake_gps_point); + // if (point_index >= status.path.len / 2 - 1) { + // return; + // } + // let p1 = status.path.point(2 * point_index); // use these to approximately follow path + // let p2 = status.path.point(2 * (point_index + 1)); + // //let p1 = status.path.point(point_index); // use these to strictly follow path + // //let p2 = status.path.point(point_index + 1); - let alpha = fake_gps_point - point_index; - let pos = p1.times(1 - alpha).plus(p2.times(alpha)); + // let alpha = fake_gps_point - point_index; + // let pos = p1.times(1 - alpha).plus(p2.times(alpha)); - if (go_backwards) { - fake_gps_point -= 0.2; // advance simulation - } else { - fake_gps_point += 0.2; // advance simulation - } - status.update_position(pos); - } - } + // if (go_backwards) { + // fake_gps_point -= 0.2; // advance simulation + // } else { + // fake_gps_point += 0.2; // advance simulation + // } + // console.log(fake_gps_point); + // status.update_position(pos); + // } + // } - setInterval(simulate_gps, 500, status); + // setInterval(simulate_gps, 500, status); } else { status.activate(); diff --git a/apps/gipy/interface.html b/apps/gipy/interface.html index 892349df9..f8eebabe6 100644 --- a/apps/gipy/interface.html +++ b/apps/gipy/interface.html @@ -51,6 +51,10 @@ + + + +
@@ -111,10 +115,11 @@ function display_polygon(map) { } map.fitBounds(L.latLngBounds(L.latLng(min_lat, min_lng), L.latLng(max_lat, max_lng))); + document.getElementById('convert').disabled = false; } -import init, { load_gps_from_string, get_polygon, get_polyline, request_map, get_gps_content, get_gps_map_svg, gps_from_area } from "./pkg/gps.js"; +import init, { disable_elevation, load_gps_from_string, get_polygon, get_polyline, request_map, get_gps_content, get_gps_map_svg, gps_from_area } from "./pkg/gps.js"; console.log("imported wasm"); // start map @@ -187,7 +192,8 @@ document status.innerHTML = "file reading completed"; let gpx_content = reader.result; - gps_data = load_gps_from_string(gpx_content); + let autodetect_waypoints = document.getElementById("autodetect_waypoints").checked; + gps_data = load_gps_from_string(gpx_content, autodetect_waypoints); display_polygon(map); }; reader.readAsText(this.files[0]); @@ -211,8 +217,12 @@ document request_map(gps_data, key1, value1, key2, value2, key3, value3, key4, value4).then( () => { - status.innerHTML = "file converted"; + let include_elevation = document.getElementById("include_elevation").checked; + if (!include_elevation) { + disable_elevation(gps_data); + } gps_content = get_gps_content(gps_data); + status.innerHTML = "file converted (" + gps_content.length + " bytes)"; let svg_string = get_gps_map_svg(gps_data); let img = document.getElementById("svgmap"); img.innerHTML = svg_string; diff --git a/apps/gipy/metadata.json b/apps/gipy/metadata.json index 53176f61f..636bbe865 100644 --- a/apps/gipy/metadata.json +++ b/apps/gipy/metadata.json @@ -2,7 +2,7 @@ "id": "gipy", "name": "Gipy", "shortName": "Gipy", - "version": "0.22", + "version": "0.23", "description": "Follow gpx files using the gps. Don't get lost in your bike trips and hikes.", "allow_emulator":false, "icon": "gipy.png", diff --git a/apps/gipy/pkg/gps.d.ts b/apps/gipy/pkg/gps.d.ts index 8df9092a9..11cdf371b 100644 --- a/apps/gipy/pkg/gps.d.ts +++ b/apps/gipy/pkg/gps.d.ts @@ -2,6 +2,10 @@ /* eslint-disable */ /** * @param {Gps} gps +*/ +export function disable_elevation(gps: Gps): void; +/** +* @param {Gps} gps * @returns {string} */ export function get_gps_map_svg(gps: Gps): string; @@ -40,9 +44,10 @@ export function get_gps_content(gps: Gps): Uint8Array; export function request_map(gps: Gps, key1: string, value1: string, key2: string, value2: string, key3: string, value3: string, key4: string, value4: string): Promise; /** * @param {string} input +* @param {boolean} autodetect_waypoints * @returns {Gps} */ -export function load_gps_from_string(input: string): Gps; +export function load_gps_from_string(input: string, autodetect_waypoints: boolean): Gps; /** * @param {number} xmin * @param {number} ymin @@ -62,22 +67,23 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl export interface InitOutput { readonly memory: WebAssembly.Memory; readonly __wbg_gps_free: (a: number) => void; + readonly disable_elevation: (a: number) => void; readonly get_gps_map_svg: (a: number, b: number) => void; readonly get_polygon: (a: number, b: number) => void; readonly has_heights: (a: number) => number; readonly get_polyline: (a: number, b: number) => void; readonly get_gps_content: (a: number, b: number) => void; readonly request_map: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number) => number; - readonly load_gps_from_string: (a: number, b: number) => number; + readonly load_gps_from_string: (a: number, b: number, c: number) => number; readonly gps_from_area: (a: number, b: number, c: number, d: number) => number; readonly __wbindgen_malloc: (a: number) => number; readonly __wbindgen_realloc: (a: number, b: number, c: number) => number; readonly __wbindgen_export_2: WebAssembly.Table; - readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h9f56e5d7ebbfdb61: (a: number, b: number, c: number) => void; + readonly wasm_bindgen__convert__closures__invoke1_mut__hef038f7a61abd0f6: (a: number, b: number, c: number) => void; readonly __wbindgen_add_to_stack_pointer: (a: number) => number; readonly __wbindgen_free: (a: number, b: number) => void; readonly __wbindgen_exn_store: (a: number) => void; - readonly wasm_bindgen__convert__closures__invoke2_mut__h193105c6f054446a: (a: number, b: number, c: number, d: number) => void; + readonly wasm_bindgen__convert__closures__invoke2_mut__h545ed49cfafdda52: (a: number, b: number, c: number, d: number) => void; } export type SyncInitInput = BufferSource | WebAssembly.Module; diff --git a/apps/gipy/pkg/gps.js b/apps/gipy/pkg/gps.js index 70698408d..9057a5aa0 100644 --- a/apps/gipy/pkg/gps.js +++ b/apps/gipy/pkg/gps.js @@ -98,14 +98,6 @@ function takeObject(idx) { return ret; } -const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - -cachedTextDecoder.decode(); - -function getStringFromWasm0(ptr, len) { - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); -} - function addHeapObject(obj) { if (heap_next === heap.length) heap.push(heap.length + 1); const idx = heap_next; @@ -115,6 +107,14 @@ function addHeapObject(obj) { return idx; } +const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +function getStringFromWasm0(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + function debugString(val) { // primitive types const type = typeof val; @@ -205,7 +205,7 @@ function makeMutClosure(arg0, arg1, dtor, f) { return real; } function __wbg_adapter_24(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h9f56e5d7ebbfdb61(arg0, arg1, addHeapObject(arg2)); + wasm.wasm_bindgen__convert__closures__invoke1_mut__hef038f7a61abd0f6(arg0, arg1, addHeapObject(arg2)); } function _assertClass(instance, klass) { @@ -214,6 +214,14 @@ function _assertClass(instance, klass) { } return instance.ptr; } +/** +* @param {Gps} gps +*/ +export function disable_elevation(gps) { + _assertClass(gps, Gps); + wasm.disable_elevation(gps.ptr); +} + /** * @param {Gps} gps * @returns {string} @@ -350,12 +358,13 @@ export function request_map(gps, key1, value1, key2, value2, key3, value3, key4, /** * @param {string} input +* @param {boolean} autodetect_waypoints * @returns {Gps} */ -export function load_gps_from_string(input) { +export function load_gps_from_string(input, autodetect_waypoints) { const ptr0 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; - const ret = wasm.load_gps_from_string(ptr0, len0); + const ret = wasm.load_gps_from_string(ptr0, len0, autodetect_waypoints); return Gps.__wrap(ret); } @@ -378,8 +387,8 @@ function handleError(f, args) { wasm.__wbindgen_exn_store(addHeapObject(e)); } } -function __wbg_adapter_85(arg0, arg1, arg2, arg3) { - wasm.wasm_bindgen__convert__closures__invoke2_mut__h193105c6f054446a(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); +function __wbg_adapter_86(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen__convert__closures__invoke2_mut__h545ed49cfafdda52(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); } /** @@ -454,6 +463,10 @@ function getImports() { imports.wbg.__wbindgen_object_drop_ref = function(arg0) { takeObject(arg0); }; + imports.wbg.__wbindgen_object_clone_ref = function(arg0) { + const ret = getObject(arg0); + return addHeapObject(ret); + }; imports.wbg.__wbindgen_string_new = function(arg0, arg1) { const ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); @@ -462,29 +475,6 @@ function getImports() { const ret = fetch(getObject(arg0)); return addHeapObject(ret); }; - imports.wbg.__wbindgen_object_clone_ref = function(arg0) { - const ret = getObject(arg0); - return addHeapObject(ret); - }; - imports.wbg.__wbg_fetch_749a56934f95c96c = function(arg0, arg1) { - const ret = getObject(arg0).fetch(getObject(arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_newwithstrandinit_05d7180788420c40 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_signal_31753ac644b25fbb = function(arg0) { - const ret = getObject(arg0).signal; - return addHeapObject(ret); - }; - imports.wbg.__wbg_new_6396e586b56e1dff = function() { return handleError(function () { - const ret = new AbortController(); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_abort_064ae59cda5cd244 = function(arg0) { - getObject(arg0).abort(); - }; imports.wbg.__wbg_new_2d0053ee81e4dd2a = function() { return handleError(function () { const ret = new Headers(); return addHeapObject(ret); @@ -521,6 +511,25 @@ function getImports() { const ret = getObject(arg0).text(); return addHeapObject(ret); }, arguments) }; + imports.wbg.__wbg_fetch_749a56934f95c96c = function(arg0, arg1) { + const ret = getObject(arg0).fetch(getObject(arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_signal_31753ac644b25fbb = function(arg0) { + const ret = getObject(arg0).signal; + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_6396e586b56e1dff = function() { return handleError(function () { + const ret = new AbortController(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_abort_064ae59cda5cd244 = function(arg0) { + getObject(arg0).abort(); + }; + imports.wbg.__wbg_newwithstrandinit_05d7180788420c40 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); + return addHeapObject(ret); + }, arguments) }; imports.wbg.__wbg_new_abda76e883ba8a5f = function() { const ret = new Error(); return addHeapObject(ret); @@ -620,7 +629,7 @@ function getImports() { const a = state0.a; state0.a = 0; try { - return __wbg_adapter_85(a, state0.b, arg0, arg1); + return __wbg_adapter_86(a, state0.b, arg0, arg1); } finally { state0.a = a; } @@ -655,6 +664,10 @@ function getImports() { const ret = new Uint8Array(getObject(arg0)); return addHeapObject(ret); }; + imports.wbg.__wbg_stringify_d6471d300ded9b68 = function() { return handleError(function (arg0) { + const ret = JSON.stringify(getObject(arg0)); + return addHeapObject(ret); + }, arguments) }; imports.wbg.__wbg_get_765201544a2b6869 = function() { return handleError(function (arg0, arg1) { const ret = Reflect.get(getObject(arg0), getObject(arg1)); return addHeapObject(ret); @@ -667,10 +680,6 @@ function getImports() { const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2)); return ret; }, arguments) }; - imports.wbg.__wbg_stringify_d6471d300ded9b68 = function() { return handleError(function (arg0) { - const ret = JSON.stringify(getObject(arg0)); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { const ret = debugString(getObject(arg1)); const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); @@ -685,8 +694,8 @@ function getImports() { const ret = wasm.memory; return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper2230 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 264, __wbg_adapter_24); + imports.wbg.__wbindgen_closure_wrapper2354 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 299, __wbg_adapter_24); return addHeapObject(ret); }; diff --git a/apps/gipy/pkg/gps_bg.wasm b/apps/gipy/pkg/gps_bg.wasm index 16d7109a6..47d190458 100644 Binary files a/apps/gipy/pkg/gps_bg.wasm and b/apps/gipy/pkg/gps_bg.wasm differ diff --git a/apps/gipy/pkg/gps_bg.wasm.d.ts b/apps/gipy/pkg/gps_bg.wasm.d.ts index b2d08e6bb..dbf30308b 100644 --- a/apps/gipy/pkg/gps_bg.wasm.d.ts +++ b/apps/gipy/pkg/gps_bg.wasm.d.ts @@ -2,19 +2,20 @@ /* eslint-disable */ export const memory: WebAssembly.Memory; export function __wbg_gps_free(a: number): void; +export function disable_elevation(a: number): void; export function get_gps_map_svg(a: number, b: number): void; export function get_polygon(a: number, b: number): void; export function has_heights(a: number): number; export function get_polyline(a: number, b: number): void; export function get_gps_content(a: number, b: number): void; export function request_map(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number): number; -export function load_gps_from_string(a: number, b: number): number; +export function load_gps_from_string(a: number, b: number, c: number): number; export function gps_from_area(a: number, b: number, c: number, d: number): number; export function __wbindgen_malloc(a: number): number; export function __wbindgen_realloc(a: number, b: number, c: number): number; export const __wbindgen_export_2: WebAssembly.Table; -export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h9f56e5d7ebbfdb61(a: number, b: number, c: number): void; +export function wasm_bindgen__convert__closures__invoke1_mut__hef038f7a61abd0f6(a: number, b: number, c: number): void; export function __wbindgen_add_to_stack_pointer(a: number): number; export function __wbindgen_free(a: number, b: number): void; export function __wbindgen_exn_store(a: number): void; -export function wasm_bindgen__convert__closures__invoke2_mut__h193105c6f054446a(a: number, b: number, c: number, d: number): void; +export function wasm_bindgen__convert__closures__invoke2_mut__h545ed49cfafdda52(a: number, b: number, c: number, d: number): void; diff --git a/apps/gipy/settings.js b/apps/gipy/settings.js index fff1f8f79..e3b00359c 100644 --- a/apps/gipy/settings.js +++ b/apps/gipy/settings.js @@ -6,10 +6,11 @@ wake_up_speed: 13, active_time: 10, buzz_on_turns: false, - disable_bluetooth: true, + disable_bluetooth: false, brightness: 0.5, power_lcd_off: false, powersave_by_default: false, + sleep_between_waypoints: false, }, require("Storage").readJSON(FILE, true) || {} ); @@ -89,6 +90,13 @@ settings.powersave_by_default = v; writeSettings(); } + }, + "sleep between waypoints": { + value: settings.sleep_between_waypoints == true, + onchange: (v) => { + settings.sleep_between_waypoints = v; + writeSettings(); + } } }); });