Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
27936d3
Add initial loader logic
joshua-dean Oct 1, 2024
7ecaa11
Better scheme for testing the loader
joshua-dean Oct 1, 2024
5658aea
Add overlay for loader
joshua-dean Oct 1, 2024
0c0da4b
Better names for loader functions
joshua-dean Oct 1, 2024
aa25034
Move initializer into own file
joshua-dean Oct 1, 2024
f9838bc
Remove hooey
joshua-dean Oct 1, 2024
9402b95
Got staggered load working
joshua-dean Oct 2, 2024
7746d6b
Added option for "build dev and demo"
joshua-dean Oct 2, 2024
c9e7ff5
Remove hooey and general cleanup
joshua-dean Oct 2, 2024
031fce7
Add note on hoisted display step
joshua-dean Oct 2, 2024
e2e59e9
Spacing
joshua-dean Oct 2, 2024
ce0cf16
First batch of listeners with many type updates
joshua-dean Oct 7, 2024
d6e79d6
Merge branch 'fix/set-saved-loading-issue' into org/listeners
joshua-dean Oct 7, 2024
d6ed260
Merge branch 'main' into org/listeners
joshua-dean Oct 7, 2024
572d921
Add remainder of listeners
joshua-dean Oct 7, 2024
348b9c1
Fix build issues by pulling night mode cookie
joshua-dean Oct 7, 2024
160c5d2
Refactor additional listeners to handlers
joshua-dean Oct 7, 2024
5514d46
Update corresponding listener calls
joshua-dean Oct 7, 2024
355d132
Nicer formatting for `index.d.ts`
joshua-dean Oct 7, 2024
0077afa
More verbose event listener function args
joshua-dean Oct 7, 2024
6bdc6cd
Add `ErrorLevel` enum
joshua-dean Oct 7, 2024
bc9c4fd
Port existing logging hooey to new call
joshua-dean Oct 7, 2024
7812ae2
Pull static canvas functions
joshua-dean Oct 7, 2024
064304c
Change `after_init` to be instance function
joshua-dean Oct 7, 2024
40320ec
Remove `load_image_promise` in favor of `decode`
joshua-dean Oct 7, 2024
eeaad53
Remove unecessary array cast
joshua-dean Oct 7, 2024
03bbdea
`first_img` -> `first_bg_img`
joshua-dean Oct 7, 2024
879a644
Fix other case of `raise_error`
joshua-dean Oct 7, 2024
132ae6f
Merge branch 'org/other-static-fns' into feature/loader
joshua-dean Oct 7, 2024
2c6d591
Merge branch 'org/other-static-fns' into feature/loader
joshua-dean Oct 7, 2024
da9b151
Update inline handlers to be arrow functions
joshua-dean Oct 9, 2024
e232130
Merge branch 'org/listeners' into org/other-static-fns
joshua-dean Oct 9, 2024
8939827
Clarify log message for invalid spatial type
joshua-dean Oct 10, 2024
8b1917a
Merge branch 'org/other-static-fns' into feature/loader
joshua-dean Oct 10, 2024
c11a5f1
Fix ESLint complaints globally with new rules
joshua-dean Oct 11, 2024
ec1a4ff
Update changelog for v0.16.0
joshua-dean Oct 11, 2024
5480b19
Couple loader style with loader div
joshua-dean Oct 11, 2024
9ca27d6
Merge branch 'main' into rc/v0.16.0
joshua-dean Nov 1, 2024
c7d0915
Merge branch 'rc/v0.16.0' into org/listeners
joshua-dean Nov 1, 2024
c7cedfb
Merge branch 'org/listeners' into org/other-static-fns
joshua-dean Nov 1, 2024
b06828e
Merge branch 'org/other-static-fns' into feature/loader
joshua-dean Nov 1, 2024
519212a
Consolidate to non-staggered init (still async)
joshua-dean Nov 1, 2024
508d065
Build after latest changes
joshua-dean Nov 1, 2024
ae8a5e4
Merge pull request #200 from SenteraLLC/org/listeners
joshua-dean Nov 8, 2024
d55e996
Merge branch 'rc/v0.16.0' into org/other-static-fns
joshua-dean Nov 8, 2024
d1e8bb0
Merge pull request #201 from SenteraLLC/org/other-static-fns
joshua-dean Nov 8, 2024
7ebaa0a
Merge branch 'rc/v0.16.0' into feature/loader
joshua-dean Nov 8, 2024
e35f83f
Merge pull request #190 from SenteraLLC/feature/loader
joshua-dean Nov 8, 2024
9dcc471
Updated date in changelog
joshua-dean Nov 8, 2024
5b704ce
Bump minor and build `v0.16.0`
joshua-dean Nov 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ All notable changes to this project will be documented here.

Nothing yet.

## [0.16.0] - Nov 8th, 2024
- Added loading overlay with spinning icon while ULabel is initializing
- Moved several existing utilties (mostly static methods) to isolated files:
- Event listener management to `listeners.ts`
- Night mode cookie to `cookies.ts`
- Logging to `error_logging.ts`
- Canvas initialization to `canvas_utils.ts`
- Initialization logic (vastly simplified) to `initializer.ts`
- Changed `after_init` to be an instance function
- Removed `load_image_promise` in favor of `.decode()`
- Added many new entries in `index.d.ts`

## [0.15.3] - Oct 4th, 2024
- Fix issue where legacy submit button functionality would break loading

Expand Down
2 changes: 1 addition & 1 deletion dist/ulabel.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/ulabel.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default [
},
stylistic.configs.customize(
{
arrowParens: true,
braceStyle: "1tbs",
commaDangle: "always-multiline",
indent: 4,
Expand Down
130 changes: 123 additions & 7 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export type ClassDefinition = {
name: string;
id: number;
color: string;
keybind?: string;
};

export type SliderInfo = {
Expand Down Expand Up @@ -168,7 +169,7 @@ export type ImageData = {
export type ULabelSubtasks = { [key: string]: ULabelSubtask };

export class ULabel {
subtasks: ULabelSubtask[];
subtasks: ULabelSubtasks;
state: {
// Viewer state
zoom_val: number;
Expand All @@ -194,6 +195,9 @@ export class ULabel {
valid_class_ids: number[];
toolbox_order?: number[];
filter_distance_overlay?: FilterDistanceOverlay;
begining_time: number;
is_init: boolean;
resize_observers: ResizeObserver[];
/**
* @link https://github.com/SenteraLLC/ulabel/blob/main/api_spec.md#ulabel-constructor
*/
Expand All @@ -217,12 +221,20 @@ export class ULabel {
* @link https://github.com/SenteraLLC/ulabel/blob/main/api_spec.md#display-utility-functions
*/
public init(callback: () => void): void;
public after_init(): void;
public show_initial_crop(): void;
public show_whole_image(): void;
public swap_frame_image(new_src: string, frame?: number): string;
public swap_anno_bg_color(new_bg_color: string): string;

// Subtasks
public get_current_subtask_key(): string;
public get_current_subtask(): ULabelSubtask;
public readjust_subtask_opacities(): void;
public set_subtask(st_key: string): void;
public switch_to_next_subtask(): void;

// Annotations
public get_annotations(subtask: ULabelSubtask): ULabelAnnotation[];
public set_annotations(annotations: ULabelAnnotation[], subtask: ULabelSubtask);
public set_saved(saved: boolean);
Expand All @@ -235,8 +247,6 @@ export class ULabel {
public show_annotation_mode(
target_jq?: JQuery<HTMLElement>, // TODO (joshua-dean): validate this type
);
public raise_error(message: string, level?: number);
public rezoom(): void;
public update_frame(delta?: number, new_frame?: number): void;
public handle_id_dialog_hover(
mouse_event: JQuery.TriggeredEvent,
Expand All @@ -245,14 +255,120 @@ export class ULabel {
dist_prop: number;
},
): void;
public toggle_erase_mode(mouse_event: JQuery.TriggeredEvent): void;
public toggle_brush_mode(mouse_event: JQuery.TriggeredEvent): void;

// Brush
// TODO (joshua-dean): should these actually be optional?
public toggle_erase_mode(mouse_event?: JQuery.TriggeredEvent): void;
public toggle_brush_mode(mouse_event?: JQuery.TriggeredEvent): void;
public toggle_delete_class_id_in_toolbox(): void;
public change_brush_size(scale_factor: number): void;
public recolor_brush_circle(): void;
public destroy_brush_circle(): void;

// Listeners
public remove_listeners(): void;
static get_allowed_toolbox_item_enum(): AllowedToolboxItem;
static process_classes(ulabel_obj: ULabel, arg1: string, subtask_obj: ULabelSubtask);
static build_id_dialogs(ulabel_obj: ULabel);
static process_classes(ulabel_obj: ULabel, arg1: string, subtask_obj: ULabelSubtask): void;
static build_id_dialogs(ulabel_obj: ULabel): void;

// Instance init functions
public create_overlays(): void;

// nops
public redraw_demo(): void;

// Annotation lifecycle
// TODO (joshua-dean): type for redo_payload
public begin_annotation(mouse_event: JQuery.TriggeredEvent, redo_payload?: object): void;
public create_annotation(
spatial_type: ULabelSpatialType,
spatial_payload: ULabelSpatialPayload,
unique_id?: string,
): void;
public create_nonspatial_annotation(
redo_payload?: object,
): void;
public delete_annotation(
annotation_id: string,
redo_payload?: object,
record_action?: boolean,
): void;
public cancel_annotation(redo_payload?: object): void;
public get_active_class_id(): number;
public get_active_class_id_idx(): number;
public undo(is_internal_undo?: boolean): void;
public redo(): void;

// Mouse event handlers
public handle_mouse_down(mouse_event: JQuery.TriggeredEvent): void;
public handle_mouse_move(mouse_event: JQuery.TriggeredEvent): void;
public handle_mouse_up(mouse_event: JQuery.TriggeredEvent): void;
public handle_aux_click(mouse_event: JQuery.TriggeredEvent): void;
public handle_wheel(wheel_event: WheelEvent): void;
public start_drag(
drag_key: string,
release_button: string,
mouse_event: JQuery.TriggeredEvent,
): void;
public end_drag(mouse_event: JQuery.TriggeredEvent): void;
public drag_repan(mouse_event: JQuery.TriggeredEvent): void;
public drag_rezoom(mouse_event: JQuery.TriggeredEvent): void;

// "Mouse event interpreters"
public get_global_mouse_x(mouse_event: JQuery.TriggeredEvent): number;
public get_global_mouse_y(mouse_event: JQuery.TriggeredEvent): number;

// Edit suggestions
public suggest_edits(
mouse_event?: JQuery.TriggeredEvent,
nonspatial_id?: string,
): void;
public show_global_edit_suggestion(
annid: string,
offset?: {
diffX: number;
diffY: number;
diffZ?: number;
},
nonspatial_id?: string,
): void;
public hide_global_edit_suggestion(): void;

// Drawing
public rezoom(
foc_x?: number,
foc_y?: number,
abs?: boolean,
): void;
public reposition_dialogs(): void;
public handle_toolbox_overflow(): void;

// ID Dialog
public set_id_dialog_payload_nopin(
class_ind: number,
dist_prop: number
): void;
public update_id_dialog_display(
front?: boolean,
): void;
public handle_id_dialog_click(
mouse_event: JQuery.TriggeredEvent,
annotation_id?: string,
new_class_idx?: number,
): void;
public show_id_dialog(
gbx: number,
gby: number,
active_ann: string, // annotation id
thumbnail?: boolean,
nonspatial?: boolean,
): void;

// Canvases
public get_init_canvas_context_id(
annotation_id: string,
subtask?: string, // SUBTASK KEY
): string;
}

declare global {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ulabel",
"description": "An image annotation tool.",
"version": "0.15.4",
"version": "0.16.0",
"main": "dist/ulabel.js",
"module": "dist/ulabel.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/annotation_operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function mark_deprecated(
annotation.deprecated_by[deprecated_by_key] = deprecated;

// If the annotation has been deprecated by any method, then deprecate the annotation
if (Object.values(annotation.deprecated_by).some(x => x)) {
if (Object.values(annotation.deprecated_by).some((x) => x)) {
annotation.deprecated = true;
return;
}
Expand Down
70 changes: 70 additions & 0 deletions src/canvas_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Annotation canvas utilities.
* TODO (joshua-dean): Pull the rest of the canvas functions into here
*/

import type { ULabel, ULabelSubtasks } from "..";
import { NONSPATIAL_MODES } from "./annotation";
import { Configuration, DEFAULT_N_ANNOS_PER_CANVAS, TARGET_MAX_N_CANVASES_PER_SUBTASK } from "./configuration";

/**
* If the user doesn't provide a number of annotations per canvas, set it dynamically.
* This should help with performance.
*
* @param config ULabel configuration
* @param subtasks ULabel subtasks
*/
function dynamically_set_n_annos_per_canvas(
config: Configuration,
subtasks: ULabelSubtasks,
) {
// If they didn't provide a value, we'll still be using the default
if (config.n_annos_per_canvas === DEFAULT_N_ANNOS_PER_CANVAS) {
// Count max annotations per subtask
const max_annos = Math.max(
...Object.values(subtasks).map((subtask) => subtask.annotations.ordering.length),
);

// Performance starts to deteriorate when we require many canvases to be drawn on
// To be safe, check if max_annos / DEFAULT_N_ANNOS_PER_CANVAS is greater than TARGET_MAX_N_CANVASES_PER_SUBTASK
if (max_annos / DEFAULT_N_ANNOS_PER_CANVAS > TARGET_MAX_N_CANVASES_PER_SUBTASK) {
// If so, raise the default
config.n_annos_per_canvas = Math.ceil(max_annos / TARGET_MAX_N_CANVASES_PER_SUBTASK);
}
}
}

/**
* Initialize annotation canvases and assign annotations to them
*
* @param ulabel ULabel instance
* @param subtask_key Subtask key. If null, this will dynamically initialize for all subtasks.
*/
export function initialize_annotation_canvases(
ulabel: ULabel,
subtask_key: string = null,
) {
if (subtask_key === null) {
dynamically_set_n_annos_per_canvas(
ulabel.config,
ulabel.subtasks,
);
for (const subtask_key in ulabel.subtasks) {
initialize_annotation_canvases(ulabel, subtask_key);
}
return;
}

// TODO (joshua-dean): shouldn't this just be a separate function?
// Create the canvas for each annotation
const subtask = ulabel.subtasks[subtask_key];
for (const annotation_id in subtask.annotations.access) {
const annotation = subtask.annotations.access[annotation_id];
if (!NONSPATIAL_MODES.includes(annotation.spatial_type)) {
annotation["canvas_id"] = ulabel.get_init_canvas_context_id(
annotation_id,
subtask_key,
);
}
}
}
45 changes: 45 additions & 0 deletions src/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* ULabel cookie utilities.
*/

export abstract class NightModeCookie {
/**
* The name of the cookie that stores the night mode preference.
*/
public static readonly COOKIE_NAME: string = "nightmode";

/**
* Return whether the document has a night mode cookie.
*/
public static exists_in_document(): boolean {
const cookie_components = document.cookie.split(";");
const night_mode_comp = cookie_components.find(
(row) => row.trim().startsWith(`${NightModeCookie.COOKIE_NAME}=true`),
);
return night_mode_comp !== undefined;
}

/**
* Set the night mode cookie.
*/
public static set_cookie(): void {
const d = new Date();
d.setTime(d.getTime() + (10000 * 24 * 60 * 60 * 1000));
document.cookie = [
NightModeCookie.COOKIE_NAME + "=true",
"expires=" + d.toUTCString(),
"path=/",
].join(";");
}

/**
* Destroy the night mode cookie.
*/
public static destroy_cookie() {
document.cookie = [
NightModeCookie.COOKIE_NAME + "=true",
"expires=Thu, 01 Jan 1970 00:00:00 UTC",
"path=/",
].join(";");
}
}
40 changes: 40 additions & 0 deletions src/error_logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Error handling utilities for ULabel.
*/

export enum LogLevel {
VERBOSE = 0,
INFO = 1,
WARNING = 2,
ERROR = 3,
}

/**
* Log a message to the console at a level.
* This was ported from code that didn't use the console log levels,
* and is kept for compatibility.
*
* @param message Message to log
* @param log_level Level to log at
*/
export function log_message(
message: string,
log_level: LogLevel = LogLevel.INFO,
) {
switch (log_level) {
case LogLevel.VERBOSE:
console.debug(message);
break;
case LogLevel.INFO:
console.log(message);
break;
case LogLevel.WARNING:
console.warn(message);
alert("[WARNING] " + message);
break;
case LogLevel.ERROR:
console.error(message);
alert("[ERROR] " + message);
throw new Error(message);
}
}
Loading