gipy: fixed perf issues in new display algorithm
parent
4da1282dec
commit
bd3f4489c4
|
|
@ -117,7 +117,8 @@
|
||||||
* Bugfix in nearest segment detection
|
* Bugfix in nearest segment detection
|
||||||
|
|
||||||
0.23:
|
0.23:
|
||||||
* New display algorithm : way faster, larger roads
|
* 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
|
* Better path simplification
|
||||||
* Waypoints autodetection using the map
|
* Waypoints autodetection using the map
|
||||||
* New option: sleep between waypoints
|
* New option: sleep between waypoints
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
urgent TODO:
|
urgent TODO:
|
||||||
|
|
||||||
|
- prefetch tiles ?
|
||||||
- update documentation to reflect new display ?
|
- update documentation to reflect new display ?
|
||||||
- add an image for the arrow ?
|
- add an image for the arrow ?
|
||||||
|
|
||||||
|
|
|
||||||
272
apps/gipy/app.js
272
apps/gipy/app.js
|
|
@ -7,11 +7,11 @@ let status;
|
||||||
let initial_options = Bangle.getOptions();
|
let initial_options = Bangle.getOptions();
|
||||||
|
|
||||||
let interests_colors = [
|
let interests_colors = [
|
||||||
[1,1,1], // Waypoints, white
|
[1, 1, 1], // Waypoints, white
|
||||||
[1,0,0], // Bakery, red
|
[1, 0, 0], // Bakery, red
|
||||||
[0,0,1], // DrinkingWater, blue
|
[0, 0, 1], // DrinkingWater, blue
|
||||||
[0,1,1], // Toilets, cyan
|
[0, 1, 1], // Toilets, cyan
|
||||||
[0,1,0], // Artwork, green
|
[0, 1, 0], // Artwork, green
|
||||||
];
|
];
|
||||||
|
|
||||||
let Y_OFFSET = 20;
|
let Y_OFFSET = 20;
|
||||||
|
|
@ -43,13 +43,13 @@ let powersaving = settings.powersave_by_default;
|
||||||
// let profile_start_times = [];
|
// let profile_start_times = [];
|
||||||
|
|
||||||
// function start_profiling() {
|
// function start_profiling() {
|
||||||
// profile_start_times.push(getTime());
|
// profile_start_times.push(getTime());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// function end_profiling(label) {
|
// function end_profiling(label) {
|
||||||
// let end_time = getTime();
|
// let end_time = getTime();
|
||||||
// let elapsed = end_time - profile_start_times.pop();
|
// let elapsed = end_time - profile_start_times.pop();
|
||||||
// console.log("profile:", label, "took", elapsed);
|
// console.log("profile:", label, "took", elapsed);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// return the index of the largest element of the array which is <= x
|
// 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 {
|
class Map {
|
||||||
constructor(buffer, offset, filename) {
|
constructor(buffer, offset, filename) {
|
||||||
this.points_cache = []; // don't refetch points all the time
|
|
||||||
// header
|
// header
|
||||||
let color_array = Uint8Array(buffer, offset, 3);
|
let color_array = Uint8Array(buffer, offset, 3);
|
||||||
this.color = [
|
this.color = [
|
||||||
|
|
@ -338,7 +317,6 @@ class Interests {
|
||||||
this.binary_interests[i] = binary_interests[i];
|
this.binary_interests[i] = binary_interests[i];
|
||||||
}
|
}
|
||||||
offset += end;
|
offset += end;
|
||||||
this.points_cache = [];
|
|
||||||
return [this, offset];
|
return [this, offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,7 +366,7 @@ class Status {
|
||||||
this.reaching = null; // which waypoint are we reaching ?
|
this.reaching = null; // which waypoint are we reaching ?
|
||||||
this.distance_to_next_point = null; // how far are we from next point ?
|
this.distance_to_next_point = null; // how far are we from next point ?
|
||||||
this.projected_point = null;
|
this.projected_point = null;
|
||||||
this.images_cache = [];
|
this.reset_images_cache();
|
||||||
|
|
||||||
let width = g.getWidth();
|
let width = g.getWidth();
|
||||||
let height = g.getHeight();
|
let height = g.getHeight();
|
||||||
|
|
@ -415,6 +393,28 @@ class Status {
|
||||||
this.old_points = []; // record previous points but only when enough distance between them
|
this.old_points = []; // record previous points but only when enough distance between them
|
||||||
this.old_times = []; // the corresponding times
|
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() {
|
activate() {
|
||||||
if (!powersaving) {
|
if (!powersaving) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -472,11 +472,14 @@ class Status {
|
||||||
|
|
||||||
let oldest_point = this.old_points[0];
|
let oldest_point = this.old_points[0];
|
||||||
let distance_to_oldest = oldest_point.distance(position);
|
let distance_to_oldest = oldest_point.distance(position);
|
||||||
|
let time_to_oldest = now - this.old_times[0];
|
||||||
|
|
||||||
// every 3 points we count the distance
|
// every 3 points we count the distance
|
||||||
if (this.gps_coordinates_counter % 3 == 0) {
|
if (this.gps_coordinates_counter % 3 == 0) {
|
||||||
if (distance_to_oldest < 150.0) {
|
if (time_to_oldest > 6 || distance_to_oldest < 150.0) {
|
||||||
// to avoid gps glitches
|
// 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;
|
this.advanced_distance += distance_to_oldest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -711,44 +714,75 @@ class Status {
|
||||||
}
|
}
|
||||||
|
|
||||||
tile_image(absolute_tile_x, absolute_tile_y) {
|
tile_image(absolute_tile_x, absolute_tile_y) {
|
||||||
let cached_img = this.images_cache.find((i) => {
|
// 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;
|
return i.x == absolute_tile_x && i.y == absolute_tile_y;
|
||||||
});
|
});
|
||||||
if (cached_img === undefined) {
|
if (cached_img_index == -1) {
|
||||||
let img = this.compute_tile_image(absolute_tile_x, absolute_tile_y);
|
// console.log("loading", absolute_tile_x, absolute_tile_y);
|
||||||
let limit = this.zoomed_in ? 12 : 30;
|
let old_image = this.images_cache.shift();
|
||||||
if (this.images_cache.length > limit) {
|
this.compute_tile_image(old_image.image, absolute_tile_x, absolute_tile_y);
|
||||||
this.images_cache.shift();
|
|
||||||
}
|
|
||||||
this.images_cache.push({
|
this.images_cache.push({
|
||||||
image: img,
|
image: old_image.image,
|
||||||
x: absolute_tile_x,
|
x: absolute_tile_x,
|
||||||
y: absolute_tile_y,
|
y: absolute_tile_y,
|
||||||
});
|
});
|
||||||
return img;
|
return old_image.image;
|
||||||
} else {
|
} else {
|
||||||
|
let cached_img = this.images_cache.splice(cached_img_index, 1)[0];
|
||||||
|
this.images_cache.push(cached_img);
|
||||||
return cached_img.image;
|
return cached_img.image;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compute_tile_image(absolute_tile_x, absolute_tile_y) {
|
compute_tile_image(img, absolute_tile_x, absolute_tile_y) {
|
||||||
let screen_width = g.getWidth();
|
img.transparent = img.toColor(1, 1, 1);
|
||||||
let screen_height = g.getHeight();
|
img.setBgColor(1, 1, 1).clear();
|
||||||
let tiles_per_diagonals = this.zoomed_in ? 3 : 5;
|
|
||||||
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
|
|
||||||
|
|
||||||
let img = Graphics.createArrayBuffer(img_side, img_side, 4, { msb: true });
|
|
||||||
img.transparent = img.toColor(1,1,1);
|
|
||||||
img.setBgColor(1,1,1).clear();
|
|
||||||
|
|
||||||
this.maps.forEach((m) => {
|
this.maps.forEach((m) => {
|
||||||
m.add_to_tile_image(img, absolute_tile_x, absolute_tile_y);
|
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);
|
this.interests.add_to_tile_image(img, absolute_tile_x, absolute_tile_y);
|
||||||
return img;
|
}
|
||||||
|
|
||||||
|
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() {
|
display() {
|
||||||
if (displaying || in_menu) {
|
if (displaying || in_menu) {
|
||||||
|
|
@ -758,46 +792,17 @@ class Status {
|
||||||
displaying = true;
|
displaying = true;
|
||||||
g.clear();
|
g.clear();
|
||||||
if (this.screen == MAP) {
|
if (this.screen == MAP) {
|
||||||
|
this.display_map();
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.position !== null) {
|
if (this.position !== null) {
|
||||||
|
// start_profiling();
|
||||||
this.display_path();
|
this.display_path();
|
||||||
|
// end_profiling("path display");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start_profiling();
|
||||||
this.display_direction();
|
this.display_direction();
|
||||||
this.display_stats();
|
this.display_stats();
|
||||||
|
// end_profiling("direction and stats display");
|
||||||
} else {
|
} else {
|
||||||
let current_position = 0;
|
let current_position = 0;
|
||||||
if (this.current_segment !== null) {
|
if (this.current_segment !== null) {
|
||||||
|
|
@ -1189,8 +1194,6 @@ function load_gps(filename) {
|
||||||
|
|
||||||
class Path {
|
class Path {
|
||||||
constructor(buffer, offset) {
|
constructor(buffer, offset) {
|
||||||
// let p = Uint16Array(buffer, offset, 1);
|
|
||||||
// console.log(p);
|
|
||||||
let points_number = Uint16Array(buffer, offset, 1)[0];
|
let points_number = Uint16Array(buffer, offset, 1)[0];
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
|
||||||
|
|
@ -1408,7 +1411,7 @@ function start_gipy(path, maps, interests, heights) {
|
||||||
value: status.zoomed_in,
|
value: status.zoomed_in,
|
||||||
format: (v) => (v ? "In" : "Out"),
|
format: (v) => (v ? "In" : "Out"),
|
||||||
onchange: (v) => {
|
onchange: (v) => {
|
||||||
status.images_cache = [];
|
status.reset_images_cache();
|
||||||
status.zoomed_in = v;
|
status.zoomed_in = v;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1486,51 +1489,52 @@ function start_gipy(path, maps, interests, heights) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (simulated) {
|
if (simulated) {
|
||||||
status.starting_time = getTime();
|
// status.starting_time = getTime();
|
||||||
// let's keep the screen on in simulations
|
// // let's keep the screen on in simulations
|
||||||
Bangle.setLCDTimeout(0);
|
// Bangle.setLCDTimeout(0);
|
||||||
Bangle.setLCDPower(1);
|
// Bangle.setLCDPower(1);
|
||||||
Bangle.loadWidgets(); // i don't know why i cannot load them at start : they would display on splash screen
|
// Bangle.loadWidgets(); // i don't know why i cannot load them at start : they would display on splash screen
|
||||||
|
|
||||||
function simulate_gps(status) {
|
// function simulate_gps(status) {
|
||||||
if (status.path === null) {
|
// if (status.path === null) {
|
||||||
let map = status.maps[0];
|
// let map = status.maps[0];
|
||||||
let p1 = new Point(map.start_coordinates[0], map.start_coordinates[1]);
|
// let p1 = new Point(map.start_coordinates[0], map.start_coordinates[1]);
|
||||||
let p2 = new Point(
|
// let p2 = new Point(
|
||||||
map.start_coordinates[0] + map.side * map.grid_size[0],
|
// map.start_coordinates[0] + map.side * map.grid_size[0],
|
||||||
map.start_coordinates[1] + map.side * map.grid_size[1]
|
// map.start_coordinates[1] + map.side * map.grid_size[1]
|
||||||
);
|
// );
|
||||||
let pos = p1.times(1 - fake_gps_point).plus(p2.times(fake_gps_point));
|
// let pos = p1.times(1 - fake_gps_point).plus(p2.times(fake_gps_point));
|
||||||
if (fake_gps_point < 1) {
|
// if (fake_gps_point < 1) {
|
||||||
fake_gps_point += 0.05;
|
// fake_gps_point += 0.05;
|
||||||
}
|
// }
|
||||||
status.update_position(pos);
|
// status.update_position(pos);
|
||||||
} else {
|
// } else {
|
||||||
if (fake_gps_point > status.path.len - 1 || fake_gps_point < 0) {
|
// if (fake_gps_point > status.path.len - 1 || fake_gps_point < 0) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
let point_index = Math.floor(fake_gps_point);
|
// let point_index = Math.floor(fake_gps_point);
|
||||||
if (point_index >= status.path.len / 2 - 1) {
|
// if (point_index >= status.path.len / 2 - 1) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
let p1 = status.path.point(2 * point_index); // use these to approximately follow path
|
// let p1 = status.path.point(2 * point_index); // use these to approximately follow path
|
||||||
let p2 = status.path.point(2 * (point_index + 1));
|
// let p2 = status.path.point(2 * (point_index + 1));
|
||||||
//let p1 = status.path.point(point_index); // use these to strictly follow path
|
// //let p1 = status.path.point(point_index); // use these to strictly follow path
|
||||||
//let p2 = status.path.point(point_index + 1);
|
// //let p2 = status.path.point(point_index + 1);
|
||||||
|
|
||||||
let alpha = fake_gps_point - point_index;
|
// let alpha = fake_gps_point - point_index;
|
||||||
let pos = p1.times(1 - alpha).plus(p2.times(alpha));
|
// let pos = p1.times(1 - alpha).plus(p2.times(alpha));
|
||||||
|
|
||||||
if (go_backwards) {
|
// if (go_backwards) {
|
||||||
fake_gps_point -= 0.2; // advance simulation
|
// fake_gps_point -= 0.2; // advance simulation
|
||||||
} else {
|
// } else {
|
||||||
fake_gps_point += 0.2; // advance simulation
|
// fake_gps_point += 0.2; // advance simulation
|
||||||
}
|
// }
|
||||||
status.update_position(pos);
|
// console.log(fake_gps_point);
|
||||||
}
|
// status.update_position(pos);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
setInterval(simulate_gps, 500, status);
|
// setInterval(simulate_gps, 500, status);
|
||||||
} else {
|
} else {
|
||||||
status.activate();
|
status.activate();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue