lightning-pose-app 1.8.1a1__py3-none-any.whl → 1.8.1a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lightning_pose_app-1.8.1a1.dist-info → lightning_pose_app-1.8.1a3.dist-info}/METADATA +1 -1
- lightning_pose_app-1.8.1a3.dist-info/RECORD +21 -0
- litpose_app/main.py +300 -290
- litpose_app/ngdist/ng_app/3rdpartylicenses.txt +11 -11
- litpose_app/ngdist/ng_app/app.component-UHVEDPZR.css.map +7 -0
- litpose_app/ngdist/ng_app/index.html +11 -2
- litpose_app/ngdist/ng_app/{main-QMBNNDJG.js → main-LJHMLKBL.js} +1008 -634
- litpose_app/ngdist/ng_app/main-LJHMLKBL.js.map +1 -0
- litpose_app/ngdist/ng_app/project-settings.component-5IRK7U7U.css.map +7 -0
- litpose_app/ngdist/ng_app/{styles-JT3DWFJR.css → styles-4V6RXJMC.css} +402 -25
- litpose_app/ngdist/ng_app/styles-4V6RXJMC.css.map +7 -0
- litpose_app/ngdist/ng_app/video-tile.component-XSYKMARQ.css.map +7 -0
- litpose_app/ngdist/ng_app/viewer-page.component-MRTIUFL2.css.map +7 -0
- litpose_app/run_ffprobe.py +132 -132
- litpose_app/super_rglob.py +48 -36
- lightning_pose_app-1.8.1a1.dist-info/RECORD +0 -17
- litpose_app/ngdist/ng_app/main-QMBNNDJG.js.map +0 -1
- litpose_app/ngdist/ng_app/styles-JT3DWFJR.css.map +0 -7
- {lightning_pose_app-1.8.1a1.dist-info → lightning_pose_app-1.8.1a3.dist-info}/WHEEL +0 -0
|
@@ -7892,6 +7892,10 @@ var OutputEmitterRef = class {
|
|
|
7892
7892
|
}
|
|
7893
7893
|
}
|
|
7894
7894
|
};
|
|
7895
|
+
function output(opts) {
|
|
7896
|
+
ngDevMode && assertInInjectionContext(output);
|
|
7897
|
+
return new OutputEmitterRef();
|
|
7898
|
+
}
|
|
7895
7899
|
function inputFunction(initialValue, opts) {
|
|
7896
7900
|
ngDevMode && assertInInjectionContext(input);
|
|
7897
7901
|
return createInputSignal(initialValue, opts);
|
|
@@ -20793,13 +20797,13 @@ function listenToOutput(tNode, lView, directiveIndex, lookupName, eventName, lis
|
|
|
20793
20797
|
const tView = lView[TVIEW];
|
|
20794
20798
|
const def = tView.data[directiveIndex];
|
|
20795
20799
|
const propertyName = def.outputs[lookupName];
|
|
20796
|
-
const
|
|
20797
|
-
if (ngDevMode && !isOutputSubscribable(
|
|
20800
|
+
const output2 = instance[propertyName];
|
|
20801
|
+
if (ngDevMode && !isOutputSubscribable(output2)) {
|
|
20798
20802
|
throw new Error(`@Output ${propertyName} not initialized in '${instance.constructor.name}'.`);
|
|
20799
20803
|
}
|
|
20800
20804
|
const tCleanup = tView.firstCreatePass ? getOrCreateTViewCleanup(tView) : null;
|
|
20801
20805
|
const lCleanup = getOrCreateLViewCleanup(lView);
|
|
20802
|
-
const subscription =
|
|
20806
|
+
const subscription = output2.subscribe(listenerFn);
|
|
20803
20807
|
const idx = lCleanup.length;
|
|
20804
20808
|
lCleanup.push(listenerFn, subscription);
|
|
20805
20809
|
tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
|
|
@@ -39894,6 +39898,423 @@ function provideRouterInitializer() {
|
|
|
39894
39898
|
// node_modules/@angular/router/fesm2022/router.mjs
|
|
39895
39899
|
var VERSION4 = new Version("19.2.11");
|
|
39896
39900
|
|
|
39901
|
+
// src/app/rpc.service.ts
|
|
39902
|
+
var RpcService = class _RpcService {
|
|
39903
|
+
http = inject(HttpClient);
|
|
39904
|
+
call(method, params) {
|
|
39905
|
+
const observable2 = this.http.post(`/app/v0/rpc/${method}`, params ?? null, {
|
|
39906
|
+
headers: { "Content-type": "application/json" }
|
|
39907
|
+
});
|
|
39908
|
+
return firstValueFrom(observable2);
|
|
39909
|
+
}
|
|
39910
|
+
static \u0275fac = function RpcService_Factory(__ngFactoryType__) {
|
|
39911
|
+
return new (__ngFactoryType__ || _RpcService)();
|
|
39912
|
+
};
|
|
39913
|
+
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _RpcService, factory: _RpcService.\u0275fac, providedIn: "root" });
|
|
39914
|
+
};
|
|
39915
|
+
(() => {
|
|
39916
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(RpcService, [{
|
|
39917
|
+
type: Injectable,
|
|
39918
|
+
args: [{
|
|
39919
|
+
providedIn: "root"
|
|
39920
|
+
}]
|
|
39921
|
+
}], null, null);
|
|
39922
|
+
})();
|
|
39923
|
+
|
|
39924
|
+
// src/app/project-info.ts
|
|
39925
|
+
var ProjectInfo = class {
|
|
39926
|
+
// absolute path on server filesystem
|
|
39927
|
+
data_dir;
|
|
39928
|
+
// absolute path on server filesystem
|
|
39929
|
+
model_dir;
|
|
39930
|
+
views;
|
|
39931
|
+
constructor(projectInfo) {
|
|
39932
|
+
this.data_dir = String(projectInfo.data_dir);
|
|
39933
|
+
this.model_dir = String(projectInfo.model_dir);
|
|
39934
|
+
this.views = projectInfo.views ?? [];
|
|
39935
|
+
}
|
|
39936
|
+
};
|
|
39937
|
+
|
|
39938
|
+
// node_modules/@angular/core/fesm2022/rxjs-interop.mjs
|
|
39939
|
+
function takeUntilDestroyed(destroyRef) {
|
|
39940
|
+
if (!destroyRef) {
|
|
39941
|
+
assertInInjectionContext(takeUntilDestroyed);
|
|
39942
|
+
destroyRef = inject(DestroyRef);
|
|
39943
|
+
}
|
|
39944
|
+
const destroyed$ = new Observable((observer) => {
|
|
39945
|
+
const unregisterFn = destroyRef.onDestroy(observer.next.bind(observer));
|
|
39946
|
+
return unregisterFn;
|
|
39947
|
+
});
|
|
39948
|
+
return (source) => {
|
|
39949
|
+
return source.pipe(takeUntil(destroyed$));
|
|
39950
|
+
};
|
|
39951
|
+
}
|
|
39952
|
+
function toSignal(source, options) {
|
|
39953
|
+
typeof ngDevMode !== "undefined" && ngDevMode && assertNotInReactiveContext(toSignal, "Invoking `toSignal` causes new subscriptions every time. Consider moving `toSignal` outside of the reactive context and read the signal value where needed.");
|
|
39954
|
+
const requiresCleanup = !options?.manualCleanup;
|
|
39955
|
+
requiresCleanup && !options?.injector && assertInInjectionContext(toSignal);
|
|
39956
|
+
const cleanupRef = requiresCleanup ? options?.injector?.get(DestroyRef) ?? inject(DestroyRef) : null;
|
|
39957
|
+
const equal = makeToSignalEqual(options?.equal);
|
|
39958
|
+
let state;
|
|
39959
|
+
if (options?.requireSync) {
|
|
39960
|
+
state = signal({
|
|
39961
|
+
kind: 0
|
|
39962
|
+
/* StateKind.NoValue */
|
|
39963
|
+
}, {
|
|
39964
|
+
equal
|
|
39965
|
+
});
|
|
39966
|
+
} else {
|
|
39967
|
+
state = signal({
|
|
39968
|
+
kind: 1,
|
|
39969
|
+
value: options?.initialValue
|
|
39970
|
+
}, {
|
|
39971
|
+
equal
|
|
39972
|
+
});
|
|
39973
|
+
}
|
|
39974
|
+
const sub = source.subscribe({
|
|
39975
|
+
next: (value) => state.set({
|
|
39976
|
+
kind: 1,
|
|
39977
|
+
value
|
|
39978
|
+
}),
|
|
39979
|
+
error: (error) => {
|
|
39980
|
+
if (options?.rejectErrors) {
|
|
39981
|
+
throw error;
|
|
39982
|
+
}
|
|
39983
|
+
state.set({
|
|
39984
|
+
kind: 2,
|
|
39985
|
+
error
|
|
39986
|
+
});
|
|
39987
|
+
}
|
|
39988
|
+
// Completion of the Observable is meaningless to the signal. Signals don't have a concept of
|
|
39989
|
+
// "complete".
|
|
39990
|
+
});
|
|
39991
|
+
if (options?.requireSync && state().kind === 0) {
|
|
39992
|
+
throw new RuntimeError(601, (typeof ngDevMode === "undefined" || ngDevMode) && "`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.");
|
|
39993
|
+
}
|
|
39994
|
+
cleanupRef?.onDestroy(sub.unsubscribe.bind(sub));
|
|
39995
|
+
return computed(() => {
|
|
39996
|
+
const current = state();
|
|
39997
|
+
switch (current.kind) {
|
|
39998
|
+
case 1:
|
|
39999
|
+
return current.value;
|
|
40000
|
+
case 2:
|
|
40001
|
+
throw current.error;
|
|
40002
|
+
case 0:
|
|
40003
|
+
throw new RuntimeError(601, (typeof ngDevMode === "undefined" || ngDevMode) && "`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.");
|
|
40004
|
+
}
|
|
40005
|
+
}, {
|
|
40006
|
+
equal: options?.equal
|
|
40007
|
+
});
|
|
40008
|
+
}
|
|
40009
|
+
function makeToSignalEqual(userEquality = Object.is) {
|
|
40010
|
+
return (a, b) => a.kind === 1 && b.kind === 1 && userEquality(a.value, b.value);
|
|
40011
|
+
}
|
|
40012
|
+
|
|
40013
|
+
// src/app/project-info.service.ts
|
|
40014
|
+
var ProjectInfoService = class _ProjectInfoService {
|
|
40015
|
+
rpc = inject(RpcService);
|
|
40016
|
+
// undefined means not yet requested
|
|
40017
|
+
// null means requested and not present
|
|
40018
|
+
_projectInfo = void 0;
|
|
40019
|
+
async loadProjectInfo() {
|
|
40020
|
+
const response = await this.rpc.call("getProjectInfo");
|
|
40021
|
+
this._projectInfo = response.projectInfo ? new ProjectInfo(response.projectInfo) : null;
|
|
40022
|
+
this.setAllViews(this._projectInfo?.views ?? []);
|
|
40023
|
+
}
|
|
40024
|
+
get projectInfo() {
|
|
40025
|
+
return this._projectInfo;
|
|
40026
|
+
}
|
|
40027
|
+
async setProjectInfo(projectInfo) {
|
|
40028
|
+
await this.rpc.call("setProjectInfo", { projectInfo });
|
|
40029
|
+
window.location.reload();
|
|
40030
|
+
}
|
|
40031
|
+
// Newer style of models.
|
|
40032
|
+
_allViews = new BehaviorSubject([]);
|
|
40033
|
+
allViews$ = this._allViews.asObservable().pipe(distinctUntilChanged());
|
|
40034
|
+
allViews = toSignal(this.allViews$, { requireSync: true });
|
|
40035
|
+
setAllViews(views) {
|
|
40036
|
+
this._allViews.next(views.concat(["unknown"]));
|
|
40037
|
+
}
|
|
40038
|
+
_allKeypoints = new BehaviorSubject([]);
|
|
40039
|
+
allKeypoints$ = this._allKeypoints.asObservable().pipe(distinctUntilChanged());
|
|
40040
|
+
allKeypoints = toSignal(this.allKeypoints$, { requireSync: true });
|
|
40041
|
+
setAllKeypoints(keypoints) {
|
|
40042
|
+
this._allKeypoints.next(keypoints);
|
|
40043
|
+
}
|
|
40044
|
+
_allModels = new BehaviorSubject([]);
|
|
40045
|
+
allModels$ = this._allModels.asObservable().pipe(distinctUntilChanged());
|
|
40046
|
+
allModels = toSignal(this.allModels$, { requireSync: true });
|
|
40047
|
+
setAllModels(models) {
|
|
40048
|
+
this._allModels.next(models);
|
|
40049
|
+
}
|
|
40050
|
+
static \u0275fac = function ProjectInfoService_Factory(__ngFactoryType__) {
|
|
40051
|
+
return new (__ngFactoryType__ || _ProjectInfoService)();
|
|
40052
|
+
};
|
|
40053
|
+
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _ProjectInfoService, factory: _ProjectInfoService.\u0275fac, providedIn: "root" });
|
|
40054
|
+
};
|
|
40055
|
+
(() => {
|
|
40056
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ProjectInfoService, [{
|
|
40057
|
+
type: Injectable,
|
|
40058
|
+
args: [{
|
|
40059
|
+
providedIn: "root"
|
|
40060
|
+
}]
|
|
40061
|
+
}], null, null);
|
|
40062
|
+
})();
|
|
40063
|
+
|
|
40064
|
+
// src/app/csv-parser.service.ts
|
|
40065
|
+
var import_ndarray = __toESM(require_ndarray());
|
|
40066
|
+
var import_papaparse = __toESM(require_papaparse_min());
|
|
40067
|
+
var CsvParserService = class _CsvParserService {
|
|
40068
|
+
/**
|
|
40069
|
+
* Parses a CSV string from pose estimation into a 3D array (ndarray-like structure)
|
|
40070
|
+
* using the PapaParse library.
|
|
40071
|
+
* The output shape is (number of frames, number of bodyparts, 2 for x/y coordinates).
|
|
40072
|
+
*
|
|
40073
|
+
* @param csvString The CSV data as a string.
|
|
40074
|
+
* @returns A 3D array of numbers: number[][][].
|
|
40075
|
+
* Returns an empty array if the CSV is malformed, has no data, or PapaParse fails.
|
|
40076
|
+
*/
|
|
40077
|
+
getBodyParts(csvString) {
|
|
40078
|
+
const parseOutput = import_papaparse.default.parse(csvString.trim(), {
|
|
40079
|
+
dynamicTyping: false,
|
|
40080
|
+
skipEmptyLines: true
|
|
40081
|
+
});
|
|
40082
|
+
if (parseOutput.errors.length > 0) {
|
|
40083
|
+
console.error("PapaParse errors:", parseOutput.errors);
|
|
40084
|
+
return [];
|
|
40085
|
+
}
|
|
40086
|
+
const allRows = parseOutput.data;
|
|
40087
|
+
if (allRows.length < 4) {
|
|
40088
|
+
console.error("CSV must have at least 3 header lines and 1 data line.");
|
|
40089
|
+
return [];
|
|
40090
|
+
}
|
|
40091
|
+
const bodypartsHeader = allRows[1];
|
|
40092
|
+
if (!bodypartsHeader || bodypartsHeader.length <= 1 || (bodypartsHeader.length - 1) % 3 !== 0) {
|
|
40093
|
+
console.error("Malformed bodyparts header line (line 2 of CSV).", bodypartsHeader);
|
|
40094
|
+
return [];
|
|
40095
|
+
}
|
|
40096
|
+
return bodypartsHeader.filter((_element, index) => {
|
|
40097
|
+
return (index - 1) % 3 === 0 && index >= 1;
|
|
40098
|
+
});
|
|
40099
|
+
}
|
|
40100
|
+
parsePredictionFile(csvString) {
|
|
40101
|
+
const parseOutput = import_papaparse.default.parse(csvString.trim(), {
|
|
40102
|
+
dynamicTyping: false,
|
|
40103
|
+
skipEmptyLines: true
|
|
40104
|
+
});
|
|
40105
|
+
if (parseOutput.errors.length > 0) {
|
|
40106
|
+
console.error("PapaParse errors:", parseOutput.errors);
|
|
40107
|
+
return (0, import_ndarray.default)(new Float64Array(0), [0, 0, 2]);
|
|
40108
|
+
}
|
|
40109
|
+
const allRows = parseOutput.data;
|
|
40110
|
+
if (allRows.length < 4) {
|
|
40111
|
+
console.error("CSV must have at least 3 header lines and 1 data line.");
|
|
40112
|
+
return (0, import_ndarray.default)(new Float64Array(0), [0, 0, 2]);
|
|
40113
|
+
}
|
|
40114
|
+
const bodypartsHeader = allRows[1];
|
|
40115
|
+
if (!bodypartsHeader || bodypartsHeader.length <= 1 || (bodypartsHeader.length - 1) % 3 !== 0) {
|
|
40116
|
+
console.error("Malformed bodyparts header line (line 2 of CSV).", bodypartsHeader);
|
|
40117
|
+
return (0, import_ndarray.default)(new Float64Array(0), [0, 0, 2]);
|
|
40118
|
+
}
|
|
40119
|
+
const numBodyParts = (bodypartsHeader.length - 1) / 3;
|
|
40120
|
+
const coordsHeader = allRows[2];
|
|
40121
|
+
if (!coordsHeader || coordsHeader.length !== bodypartsHeader.length) {
|
|
40122
|
+
console.error("Coordinate header (line 3 of CSV) length mismatch with bodyparts header.");
|
|
40123
|
+
return (0, import_ndarray.default)(new Float64Array(0), [
|
|
40124
|
+
0,
|
|
40125
|
+
numBodyParts > 0 ? numBodyParts : 0,
|
|
40126
|
+
2
|
|
40127
|
+
]);
|
|
40128
|
+
}
|
|
40129
|
+
const dataRowsOnly = allRows.slice(3);
|
|
40130
|
+
const numFrames = dataRowsOnly.length;
|
|
40131
|
+
if (numFrames === 0 || numBodyParts === 0) {
|
|
40132
|
+
return (0, import_ndarray.default)(new Float64Array(0), [numFrames, numBodyParts, 2]);
|
|
40133
|
+
}
|
|
40134
|
+
const flatData = new Float64Array(numFrames * numBodyParts * 2);
|
|
40135
|
+
let flatIndex = 0;
|
|
40136
|
+
for (let rowIndex = 0; rowIndex < numFrames; rowIndex++) {
|
|
40137
|
+
const values = dataRowsOnly[rowIndex];
|
|
40138
|
+
if (values.length < 1 + numBodyParts * 3) {
|
|
40139
|
+
console.warn(`Skipping malformed data row ${rowIndex + 4} (not enough columns): "${values.slice(0, 5).join(",")}..."`);
|
|
40140
|
+
for (let i = 0; i < numBodyParts; i++) {
|
|
40141
|
+
flatData[flatIndex++] = NaN;
|
|
40142
|
+
flatData[flatIndex++] = NaN;
|
|
40143
|
+
}
|
|
40144
|
+
continue;
|
|
40145
|
+
}
|
|
40146
|
+
for (let bodyPartIdx = 0; bodyPartIdx < numBodyParts; bodyPartIdx++) {
|
|
40147
|
+
const xDataIndex = 1 + bodyPartIdx * 3;
|
|
40148
|
+
const yDataIndex = 1 + bodyPartIdx * 3 + 1;
|
|
40149
|
+
if (xDataIndex >= values.length || yDataIndex >= values.length) {
|
|
40150
|
+
console.warn(`Skipping body part ${bodyPartIdx} in data row ${rowIndex + 4} due to insufficient data.`);
|
|
40151
|
+
flatData[flatIndex++] = NaN;
|
|
40152
|
+
flatData[flatIndex++] = NaN;
|
|
40153
|
+
continue;
|
|
40154
|
+
}
|
|
40155
|
+
const xString = values[xDataIndex];
|
|
40156
|
+
const yString = values[yDataIndex];
|
|
40157
|
+
const x = parseFloat(xString);
|
|
40158
|
+
const y = parseFloat(yString);
|
|
40159
|
+
if (isNaN(x) || isNaN(y)) {
|
|
40160
|
+
console.warn(`Could not parse x or y as number for body part ${bodyPartIdx} in data row ${rowIndex + 4}. Values: x='${xString}', y='${yString}'.`);
|
|
40161
|
+
flatData[flatIndex++] = NaN;
|
|
40162
|
+
flatData[flatIndex++] = NaN;
|
|
40163
|
+
} else {
|
|
40164
|
+
flatData[flatIndex++] = x;
|
|
40165
|
+
flatData[flatIndex++] = y;
|
|
40166
|
+
}
|
|
40167
|
+
}
|
|
40168
|
+
}
|
|
40169
|
+
return (0, import_ndarray.default)(flatData, [numFrames, numBodyParts, 2]);
|
|
40170
|
+
}
|
|
40171
|
+
static \u0275fac = function CsvParserService_Factory(__ngFactoryType__) {
|
|
40172
|
+
return new (__ngFactoryType__ || _CsvParserService)();
|
|
40173
|
+
};
|
|
40174
|
+
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _CsvParserService, factory: _CsvParserService.\u0275fac, providedIn: "root" });
|
|
40175
|
+
};
|
|
40176
|
+
(() => {
|
|
40177
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CsvParserService, [{
|
|
40178
|
+
type: Injectable,
|
|
40179
|
+
args: [{
|
|
40180
|
+
providedIn: "root"
|
|
40181
|
+
}]
|
|
40182
|
+
}], null, null);
|
|
40183
|
+
})();
|
|
40184
|
+
|
|
40185
|
+
// src/app/session.service.ts
|
|
40186
|
+
var SessionService = class _SessionService {
|
|
40187
|
+
rpc = inject(RpcService);
|
|
40188
|
+
httpClient = inject(HttpClient);
|
|
40189
|
+
predictionFiles = [];
|
|
40190
|
+
projectInfoService = inject(ProjectInfoService);
|
|
40191
|
+
csvParser = inject(CsvParserService);
|
|
40192
|
+
_allSessions = new BehaviorSubject([]);
|
|
40193
|
+
allSessions$ = this._allSessions.asObservable();
|
|
40194
|
+
allSessions = toSignal(this.allSessions$, { requireSync: true });
|
|
40195
|
+
async loadSessions() {
|
|
40196
|
+
const projectInfo = this.projectInfoService.projectInfo;
|
|
40197
|
+
const response = await this.rpc.call("rglob", {
|
|
40198
|
+
baseDir: projectInfo.data_dir,
|
|
40199
|
+
pattern: "**/*.fine.mp4",
|
|
40200
|
+
//temporary
|
|
40201
|
+
noDirs: true
|
|
40202
|
+
});
|
|
40203
|
+
const mp4Files = response.entries.filter((entry) => entry.type === "file").map((entry) => entry.path);
|
|
40204
|
+
const sessions = this.groupVideoFilesIntoSessions(mp4Files, this.projectInfoService.projectInfo.views);
|
|
40205
|
+
return this._allSessions.next(sessions);
|
|
40206
|
+
}
|
|
40207
|
+
async loadPredictionIndex() {
|
|
40208
|
+
const projectInfo = this.projectInfoService.projectInfo;
|
|
40209
|
+
const response = await this.rpc.call("rglob", {
|
|
40210
|
+
baseDir: projectInfo.model_dir,
|
|
40211
|
+
pattern: "**/video_preds/**/*.csv",
|
|
40212
|
+
noDirs: true
|
|
40213
|
+
});
|
|
40214
|
+
this.predictionFiles = response.entries.filter((entry) => {
|
|
40215
|
+
if (entry.type !== "file")
|
|
40216
|
+
return false;
|
|
40217
|
+
if (entry.path.endsWith("_bbox.csv"))
|
|
40218
|
+
return false;
|
|
40219
|
+
if (entry.path.endsWith("_error.csv"))
|
|
40220
|
+
return false;
|
|
40221
|
+
if (entry.path.endsWith("_loss.csv"))
|
|
40222
|
+
return false;
|
|
40223
|
+
if (entry.path.endsWith("_norm.csv"))
|
|
40224
|
+
return false;
|
|
40225
|
+
return true;
|
|
40226
|
+
}).map((entry) => {
|
|
40227
|
+
let match2 = entry.path.match(/(.+)\/video_preds\/([^/]+)\.mp4\/predictions\.csv/);
|
|
40228
|
+
if (!match2) {
|
|
40229
|
+
match2 = entry.path.match(/(.+)\/video_preds\/([^/]+)\.csv/);
|
|
40230
|
+
}
|
|
40231
|
+
if (!match2)
|
|
40232
|
+
return null;
|
|
40233
|
+
const modelKey = match2[1];
|
|
40234
|
+
const sessionView = match2[2];
|
|
40235
|
+
const viewName = this.projectInfoService.allViews().find((v) => sessionView.includes(v));
|
|
40236
|
+
if (!viewName)
|
|
40237
|
+
return null;
|
|
40238
|
+
const sessionKey = sessionView.replace(viewName, "*");
|
|
40239
|
+
return {
|
|
40240
|
+
path: entry.path,
|
|
40241
|
+
modelKey,
|
|
40242
|
+
sessionKey,
|
|
40243
|
+
viewName
|
|
40244
|
+
};
|
|
40245
|
+
}).filter((entry) => entry != null);
|
|
40246
|
+
this.initModels();
|
|
40247
|
+
await this.initKeypoints();
|
|
40248
|
+
}
|
|
40249
|
+
initModels() {
|
|
40250
|
+
const uniqueModels = new Set(this.predictionFiles.map((pfile) => pfile.modelKey).filter((x) => x));
|
|
40251
|
+
this.projectInfoService.setAllModels(Array.from(uniqueModels).sort());
|
|
40252
|
+
}
|
|
40253
|
+
async initKeypoints() {
|
|
40254
|
+
if (this.predictionFiles.length === 0)
|
|
40255
|
+
return;
|
|
40256
|
+
const csvFile = await this.getPredictionFile(this.predictionFiles[0]);
|
|
40257
|
+
if (!csvFile)
|
|
40258
|
+
return;
|
|
40259
|
+
const allKeypoints = this.csvParser.getBodyParts(csvFile);
|
|
40260
|
+
this.projectInfoService.setAllKeypoints(allKeypoints);
|
|
40261
|
+
}
|
|
40262
|
+
getPredictionFilesForSession(sessionKey) {
|
|
40263
|
+
const predictionFiles = this.predictionFiles.filter((p) => p.sessionKey === sessionKey || p.sessionKey === sessionKey.replace(/\.fine$/, ""));
|
|
40264
|
+
return predictionFiles;
|
|
40265
|
+
}
|
|
40266
|
+
async getPredictionFile(pfile) {
|
|
40267
|
+
const modelDir = this.projectInfoService.projectInfo?.model_dir;
|
|
40268
|
+
const src = "/app/v0/files/" + modelDir + "/" + pfile.path;
|
|
40269
|
+
return await firstValueFrom(this.httpClient.get(src, { responseType: "text" }).pipe(catchError((error) => {
|
|
40270
|
+
if (error.status === 404) {
|
|
40271
|
+
return [null];
|
|
40272
|
+
}
|
|
40273
|
+
throw error;
|
|
40274
|
+
})));
|
|
40275
|
+
}
|
|
40276
|
+
async ffprobe(file) {
|
|
40277
|
+
const response = await this.rpc.call("ffprobe", {
|
|
40278
|
+
path: file
|
|
40279
|
+
});
|
|
40280
|
+
return response;
|
|
40281
|
+
}
|
|
40282
|
+
groupVideoFilesIntoSessions(filenames, views) {
|
|
40283
|
+
const sessionKeyToItsViewFiles = /* @__PURE__ */ new Map();
|
|
40284
|
+
for (const filename of filenames) {
|
|
40285
|
+
let viewName = views.find((v) => filename.includes(v));
|
|
40286
|
+
if (!viewName) {
|
|
40287
|
+
viewName = "unknown";
|
|
40288
|
+
}
|
|
40289
|
+
const sessionKey = viewName == "unknown" ? filename : filename.replace(viewName, "*");
|
|
40290
|
+
if (!sessionKeyToItsViewFiles.has(sessionKey)) {
|
|
40291
|
+
sessionKeyToItsViewFiles.set(sessionKey, []);
|
|
40292
|
+
}
|
|
40293
|
+
const projectInfo = this.projectInfoService.projectInfo;
|
|
40294
|
+
sessionKeyToItsViewFiles.get(sessionKey).push({
|
|
40295
|
+
viewName,
|
|
40296
|
+
videoPath: projectInfo.data_dir + "/" + filename
|
|
40297
|
+
});
|
|
40298
|
+
}
|
|
40299
|
+
return Array.from(sessionKeyToItsViewFiles.entries()).map(([key, sessionViews]) => ({
|
|
40300
|
+
key: key.replace(/\.mp4$/, ""),
|
|
40301
|
+
views: sessionViews
|
|
40302
|
+
}));
|
|
40303
|
+
}
|
|
40304
|
+
static \u0275fac = function SessionService_Factory(__ngFactoryType__) {
|
|
40305
|
+
return new (__ngFactoryType__ || _SessionService)();
|
|
40306
|
+
};
|
|
40307
|
+
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _SessionService, factory: _SessionService.\u0275fac, providedIn: "root" });
|
|
40308
|
+
};
|
|
40309
|
+
(() => {
|
|
40310
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(SessionService, [{
|
|
40311
|
+
type: Injectable,
|
|
40312
|
+
args: [{
|
|
40313
|
+
providedIn: "root"
|
|
40314
|
+
}]
|
|
40315
|
+
}], null, null);
|
|
40316
|
+
})();
|
|
40317
|
+
|
|
39897
40318
|
// node_modules/@angular/cdk/fesm2022/boolean-property-DaaVhX5A.mjs
|
|
39898
40319
|
function coerceBooleanProperty(value) {
|
|
39899
40320
|
return value != null && `${value}` !== "false";
|
|
@@ -40880,12 +41301,12 @@ var BreakpointObserver = class _BreakpointObserver {
|
|
|
40880
41301
|
query,
|
|
40881
41302
|
matches
|
|
40882
41303
|
})), takeUntil(this._destroySubject));
|
|
40883
|
-
const
|
|
41304
|
+
const output2 = {
|
|
40884
41305
|
observable: queryObservable,
|
|
40885
41306
|
mql
|
|
40886
41307
|
};
|
|
40887
|
-
this._queries.set(query,
|
|
40888
|
-
return
|
|
41308
|
+
this._queries.set(query, output2);
|
|
41309
|
+
return output2;
|
|
40889
41310
|
}
|
|
40890
41311
|
static \u0275fac = function BreakpointObserver_Factory(__ngFactoryType__) {
|
|
40891
41312
|
return new (__ngFactoryType__ || _BreakpointObserver)();
|
|
@@ -52946,14 +53367,14 @@ var ViewportRuler = class _ViewportRuler {
|
|
|
52946
53367
|
if (!this._viewportSize) {
|
|
52947
53368
|
this._updateViewportSize();
|
|
52948
53369
|
}
|
|
52949
|
-
const
|
|
53370
|
+
const output2 = {
|
|
52950
53371
|
width: this._viewportSize.width,
|
|
52951
53372
|
height: this._viewportSize.height
|
|
52952
53373
|
};
|
|
52953
53374
|
if (!this._platform.isBrowser) {
|
|
52954
53375
|
this._viewportSize = null;
|
|
52955
53376
|
}
|
|
52956
|
-
return
|
|
53377
|
+
return output2;
|
|
52957
53378
|
}
|
|
52958
53379
|
/** Gets a DOMRect for the viewport's bounds. */
|
|
52959
53380
|
getViewportRect() {
|
|
@@ -53892,399 +54313,6 @@ var ScrollingModule = class _ScrollingModule {
|
|
|
53892
54313
|
}], null, null);
|
|
53893
54314
|
})();
|
|
53894
54315
|
|
|
53895
|
-
// node_modules/@angular/core/fesm2022/rxjs-interop.mjs
|
|
53896
|
-
function takeUntilDestroyed(destroyRef) {
|
|
53897
|
-
if (!destroyRef) {
|
|
53898
|
-
assertInInjectionContext(takeUntilDestroyed);
|
|
53899
|
-
destroyRef = inject(DestroyRef);
|
|
53900
|
-
}
|
|
53901
|
-
const destroyed$ = new Observable((observer) => {
|
|
53902
|
-
const unregisterFn = destroyRef.onDestroy(observer.next.bind(observer));
|
|
53903
|
-
return unregisterFn;
|
|
53904
|
-
});
|
|
53905
|
-
return (source) => {
|
|
53906
|
-
return source.pipe(takeUntil(destroyed$));
|
|
53907
|
-
};
|
|
53908
|
-
}
|
|
53909
|
-
function toSignal(source, options) {
|
|
53910
|
-
typeof ngDevMode !== "undefined" && ngDevMode && assertNotInReactiveContext(toSignal, "Invoking `toSignal` causes new subscriptions every time. Consider moving `toSignal` outside of the reactive context and read the signal value where needed.");
|
|
53911
|
-
const requiresCleanup = !options?.manualCleanup;
|
|
53912
|
-
requiresCleanup && !options?.injector && assertInInjectionContext(toSignal);
|
|
53913
|
-
const cleanupRef = requiresCleanup ? options?.injector?.get(DestroyRef) ?? inject(DestroyRef) : null;
|
|
53914
|
-
const equal = makeToSignalEqual(options?.equal);
|
|
53915
|
-
let state;
|
|
53916
|
-
if (options?.requireSync) {
|
|
53917
|
-
state = signal({
|
|
53918
|
-
kind: 0
|
|
53919
|
-
/* StateKind.NoValue */
|
|
53920
|
-
}, {
|
|
53921
|
-
equal
|
|
53922
|
-
});
|
|
53923
|
-
} else {
|
|
53924
|
-
state = signal({
|
|
53925
|
-
kind: 1,
|
|
53926
|
-
value: options?.initialValue
|
|
53927
|
-
}, {
|
|
53928
|
-
equal
|
|
53929
|
-
});
|
|
53930
|
-
}
|
|
53931
|
-
const sub = source.subscribe({
|
|
53932
|
-
next: (value) => state.set({
|
|
53933
|
-
kind: 1,
|
|
53934
|
-
value
|
|
53935
|
-
}),
|
|
53936
|
-
error: (error) => {
|
|
53937
|
-
if (options?.rejectErrors) {
|
|
53938
|
-
throw error;
|
|
53939
|
-
}
|
|
53940
|
-
state.set({
|
|
53941
|
-
kind: 2,
|
|
53942
|
-
error
|
|
53943
|
-
});
|
|
53944
|
-
}
|
|
53945
|
-
// Completion of the Observable is meaningless to the signal. Signals don't have a concept of
|
|
53946
|
-
// "complete".
|
|
53947
|
-
});
|
|
53948
|
-
if (options?.requireSync && state().kind === 0) {
|
|
53949
|
-
throw new RuntimeError(601, (typeof ngDevMode === "undefined" || ngDevMode) && "`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.");
|
|
53950
|
-
}
|
|
53951
|
-
cleanupRef?.onDestroy(sub.unsubscribe.bind(sub));
|
|
53952
|
-
return computed(() => {
|
|
53953
|
-
const current = state();
|
|
53954
|
-
switch (current.kind) {
|
|
53955
|
-
case 1:
|
|
53956
|
-
return current.value;
|
|
53957
|
-
case 2:
|
|
53958
|
-
throw current.error;
|
|
53959
|
-
case 0:
|
|
53960
|
-
throw new RuntimeError(601, (typeof ngDevMode === "undefined" || ngDevMode) && "`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.");
|
|
53961
|
-
}
|
|
53962
|
-
}, {
|
|
53963
|
-
equal: options?.equal
|
|
53964
|
-
});
|
|
53965
|
-
}
|
|
53966
|
-
function makeToSignalEqual(userEquality = Object.is) {
|
|
53967
|
-
return (a, b) => a.kind === 1 && b.kind === 1 && userEquality(a.value, b.value);
|
|
53968
|
-
}
|
|
53969
|
-
|
|
53970
|
-
// src/app/rpc.service.ts
|
|
53971
|
-
var RpcService = class _RpcService {
|
|
53972
|
-
http = inject(HttpClient);
|
|
53973
|
-
call(method, params) {
|
|
53974
|
-
const observable2 = this.http.post(`/app/v0/rpc/${method}`, params ?? null, {
|
|
53975
|
-
headers: { "Content-type": "application/json" }
|
|
53976
|
-
});
|
|
53977
|
-
return firstValueFrom(observable2);
|
|
53978
|
-
}
|
|
53979
|
-
static \u0275fac = function RpcService_Factory(__ngFactoryType__) {
|
|
53980
|
-
return new (__ngFactoryType__ || _RpcService)();
|
|
53981
|
-
};
|
|
53982
|
-
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _RpcService, factory: _RpcService.\u0275fac, providedIn: "root" });
|
|
53983
|
-
};
|
|
53984
|
-
(() => {
|
|
53985
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(RpcService, [{
|
|
53986
|
-
type: Injectable,
|
|
53987
|
-
args: [{
|
|
53988
|
-
providedIn: "root"
|
|
53989
|
-
}]
|
|
53990
|
-
}], null, null);
|
|
53991
|
-
})();
|
|
53992
|
-
|
|
53993
|
-
// src/app/project-info.service.ts
|
|
53994
|
-
var ProjectInfoService = class _ProjectInfoService {
|
|
53995
|
-
rpc = inject(RpcService);
|
|
53996
|
-
// inits to undefined, set to null if no project exists.
|
|
53997
|
-
_projectInfo = void 0;
|
|
53998
|
-
async loadProjectInfo() {
|
|
53999
|
-
const response = await this.rpc.call("getProjectInfo");
|
|
54000
|
-
this._projectInfo = response.projectInfo;
|
|
54001
|
-
this.setAllViews(response.projectInfo.views);
|
|
54002
|
-
}
|
|
54003
|
-
get projectInfo() {
|
|
54004
|
-
return this._projectInfo;
|
|
54005
|
-
}
|
|
54006
|
-
async setProjectInfo(projectInfo) {
|
|
54007
|
-
await this.rpc.call("setProjectInfo", { projectInfo });
|
|
54008
|
-
}
|
|
54009
|
-
// Newer style of models.
|
|
54010
|
-
_allViews = new BehaviorSubject([]);
|
|
54011
|
-
allViews$ = this._allViews.asObservable().pipe(distinctUntilChanged());
|
|
54012
|
-
allViews = toSignal(this.allViews$, { requireSync: true });
|
|
54013
|
-
setAllViews(views) {
|
|
54014
|
-
this._allViews.next(views);
|
|
54015
|
-
}
|
|
54016
|
-
_allKeypoints = new BehaviorSubject([]);
|
|
54017
|
-
allKeypoints$ = this._allKeypoints.asObservable().pipe(distinctUntilChanged());
|
|
54018
|
-
allKeypoints = toSignal(this.allKeypoints$, { requireSync: true });
|
|
54019
|
-
setAllKeypoints(keypoints) {
|
|
54020
|
-
this._allKeypoints.next(keypoints);
|
|
54021
|
-
}
|
|
54022
|
-
_allModels = new BehaviorSubject([]);
|
|
54023
|
-
allModels$ = this._allModels.asObservable().pipe(distinctUntilChanged());
|
|
54024
|
-
allModels = toSignal(this.allModels$, { requireSync: true });
|
|
54025
|
-
setAllModels(models) {
|
|
54026
|
-
this._allModels.next(models);
|
|
54027
|
-
}
|
|
54028
|
-
static \u0275fac = function ProjectInfoService_Factory(__ngFactoryType__) {
|
|
54029
|
-
return new (__ngFactoryType__ || _ProjectInfoService)();
|
|
54030
|
-
};
|
|
54031
|
-
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _ProjectInfoService, factory: _ProjectInfoService.\u0275fac, providedIn: "root" });
|
|
54032
|
-
};
|
|
54033
|
-
(() => {
|
|
54034
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ProjectInfoService, [{
|
|
54035
|
-
type: Injectable,
|
|
54036
|
-
args: [{
|
|
54037
|
-
providedIn: "root"
|
|
54038
|
-
}]
|
|
54039
|
-
}], null, null);
|
|
54040
|
-
})();
|
|
54041
|
-
|
|
54042
|
-
// src/app/csv-parser.service.ts
|
|
54043
|
-
var import_ndarray = __toESM(require_ndarray());
|
|
54044
|
-
var import_papaparse = __toESM(require_papaparse_min());
|
|
54045
|
-
var CsvParserService = class _CsvParserService {
|
|
54046
|
-
/**
|
|
54047
|
-
* Parses a CSV string from pose estimation into a 3D array (ndarray-like structure)
|
|
54048
|
-
* using the PapaParse library.
|
|
54049
|
-
* The output shape is (number of frames, number of bodyparts, 2 for x/y coordinates).
|
|
54050
|
-
*
|
|
54051
|
-
* @param csvString The CSV data as a string.
|
|
54052
|
-
* @returns A 3D array of numbers: number[][][].
|
|
54053
|
-
* Returns an empty array if the CSV is malformed, has no data, or PapaParse fails.
|
|
54054
|
-
*/
|
|
54055
|
-
getBodyParts(csvString) {
|
|
54056
|
-
const parseOutput = import_papaparse.default.parse(csvString.trim(), {
|
|
54057
|
-
dynamicTyping: false,
|
|
54058
|
-
skipEmptyLines: true
|
|
54059
|
-
});
|
|
54060
|
-
if (parseOutput.errors.length > 0) {
|
|
54061
|
-
console.error("PapaParse errors:", parseOutput.errors);
|
|
54062
|
-
return [];
|
|
54063
|
-
}
|
|
54064
|
-
const allRows = parseOutput.data;
|
|
54065
|
-
if (allRows.length < 4) {
|
|
54066
|
-
console.error("CSV must have at least 3 header lines and 1 data line.");
|
|
54067
|
-
return [];
|
|
54068
|
-
}
|
|
54069
|
-
const bodypartsHeader = allRows[1];
|
|
54070
|
-
if (!bodypartsHeader || bodypartsHeader.length <= 1 || (bodypartsHeader.length - 1) % 3 !== 0) {
|
|
54071
|
-
console.error("Malformed bodyparts header line (line 2 of CSV).", bodypartsHeader);
|
|
54072
|
-
return [];
|
|
54073
|
-
}
|
|
54074
|
-
return bodypartsHeader.filter((_element, index) => {
|
|
54075
|
-
return (index - 1) % 3 === 0 && index >= 1;
|
|
54076
|
-
});
|
|
54077
|
-
}
|
|
54078
|
-
parsePredictionFile(csvString) {
|
|
54079
|
-
const parseOutput = import_papaparse.default.parse(csvString.trim(), {
|
|
54080
|
-
dynamicTyping: false,
|
|
54081
|
-
skipEmptyLines: true
|
|
54082
|
-
});
|
|
54083
|
-
if (parseOutput.errors.length > 0) {
|
|
54084
|
-
console.error("PapaParse errors:", parseOutput.errors);
|
|
54085
|
-
return (0, import_ndarray.default)(new Float64Array(0), [0, 0, 2]);
|
|
54086
|
-
}
|
|
54087
|
-
const allRows = parseOutput.data;
|
|
54088
|
-
if (allRows.length < 4) {
|
|
54089
|
-
console.error("CSV must have at least 3 header lines and 1 data line.");
|
|
54090
|
-
return (0, import_ndarray.default)(new Float64Array(0), [0, 0, 2]);
|
|
54091
|
-
}
|
|
54092
|
-
const bodypartsHeader = allRows[1];
|
|
54093
|
-
if (!bodypartsHeader || bodypartsHeader.length <= 1 || (bodypartsHeader.length - 1) % 3 !== 0) {
|
|
54094
|
-
console.error("Malformed bodyparts header line (line 2 of CSV).", bodypartsHeader);
|
|
54095
|
-
return (0, import_ndarray.default)(new Float64Array(0), [0, 0, 2]);
|
|
54096
|
-
}
|
|
54097
|
-
const numBodyParts = (bodypartsHeader.length - 1) / 3;
|
|
54098
|
-
const coordsHeader = allRows[2];
|
|
54099
|
-
if (!coordsHeader || coordsHeader.length !== bodypartsHeader.length) {
|
|
54100
|
-
console.error("Coordinate header (line 3 of CSV) length mismatch with bodyparts header.");
|
|
54101
|
-
return (0, import_ndarray.default)(new Float64Array(0), [
|
|
54102
|
-
0,
|
|
54103
|
-
numBodyParts > 0 ? numBodyParts : 0,
|
|
54104
|
-
2
|
|
54105
|
-
]);
|
|
54106
|
-
}
|
|
54107
|
-
const dataRowsOnly = allRows.slice(3);
|
|
54108
|
-
const numFrames = dataRowsOnly.length;
|
|
54109
|
-
if (numFrames === 0 || numBodyParts === 0) {
|
|
54110
|
-
return (0, import_ndarray.default)(new Float64Array(0), [numFrames, numBodyParts, 2]);
|
|
54111
|
-
}
|
|
54112
|
-
const flatData = new Float64Array(numFrames * numBodyParts * 2);
|
|
54113
|
-
let flatIndex = 0;
|
|
54114
|
-
for (let rowIndex = 0; rowIndex < numFrames; rowIndex++) {
|
|
54115
|
-
const values = dataRowsOnly[rowIndex];
|
|
54116
|
-
if (values.length < 1 + numBodyParts * 3) {
|
|
54117
|
-
console.warn(`Skipping malformed data row ${rowIndex + 4} (not enough columns): "${values.slice(0, 5).join(",")}..."`);
|
|
54118
|
-
for (let i = 0; i < numBodyParts; i++) {
|
|
54119
|
-
flatData[flatIndex++] = NaN;
|
|
54120
|
-
flatData[flatIndex++] = NaN;
|
|
54121
|
-
}
|
|
54122
|
-
continue;
|
|
54123
|
-
}
|
|
54124
|
-
for (let bodyPartIdx = 0; bodyPartIdx < numBodyParts; bodyPartIdx++) {
|
|
54125
|
-
const xDataIndex = 1 + bodyPartIdx * 3;
|
|
54126
|
-
const yDataIndex = 1 + bodyPartIdx * 3 + 1;
|
|
54127
|
-
if (xDataIndex >= values.length || yDataIndex >= values.length) {
|
|
54128
|
-
console.warn(`Skipping body part ${bodyPartIdx} in data row ${rowIndex + 4} due to insufficient data.`);
|
|
54129
|
-
flatData[flatIndex++] = NaN;
|
|
54130
|
-
flatData[flatIndex++] = NaN;
|
|
54131
|
-
continue;
|
|
54132
|
-
}
|
|
54133
|
-
const xString = values[xDataIndex];
|
|
54134
|
-
const yString = values[yDataIndex];
|
|
54135
|
-
const x = parseFloat(xString);
|
|
54136
|
-
const y = parseFloat(yString);
|
|
54137
|
-
if (isNaN(x) || isNaN(y)) {
|
|
54138
|
-
console.warn(`Could not parse x or y as number for body part ${bodyPartIdx} in data row ${rowIndex + 4}. Values: x='${xString}', y='${yString}'.`);
|
|
54139
|
-
flatData[flatIndex++] = NaN;
|
|
54140
|
-
flatData[flatIndex++] = NaN;
|
|
54141
|
-
} else {
|
|
54142
|
-
flatData[flatIndex++] = x;
|
|
54143
|
-
flatData[flatIndex++] = y;
|
|
54144
|
-
}
|
|
54145
|
-
}
|
|
54146
|
-
}
|
|
54147
|
-
return (0, import_ndarray.default)(flatData, [numFrames, numBodyParts, 2]);
|
|
54148
|
-
}
|
|
54149
|
-
static \u0275fac = function CsvParserService_Factory(__ngFactoryType__) {
|
|
54150
|
-
return new (__ngFactoryType__ || _CsvParserService)();
|
|
54151
|
-
};
|
|
54152
|
-
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _CsvParserService, factory: _CsvParserService.\u0275fac, providedIn: "root" });
|
|
54153
|
-
};
|
|
54154
|
-
(() => {
|
|
54155
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CsvParserService, [{
|
|
54156
|
-
type: Injectable,
|
|
54157
|
-
args: [{
|
|
54158
|
-
providedIn: "root"
|
|
54159
|
-
}]
|
|
54160
|
-
}], null, null);
|
|
54161
|
-
})();
|
|
54162
|
-
|
|
54163
|
-
// src/app/session.service.ts
|
|
54164
|
-
var SessionService = class _SessionService {
|
|
54165
|
-
rpc = inject(RpcService);
|
|
54166
|
-
httpClient = inject(HttpClient);
|
|
54167
|
-
allSessions = new BehaviorSubject([]);
|
|
54168
|
-
predictionFiles = [];
|
|
54169
|
-
projectInfoService = inject(ProjectInfoService);
|
|
54170
|
-
csvParser = inject(CsvParserService);
|
|
54171
|
-
getAllSessions() {
|
|
54172
|
-
return this.allSessions.asObservable();
|
|
54173
|
-
}
|
|
54174
|
-
async loadSessions() {
|
|
54175
|
-
const projectInfo = this.projectInfoService.projectInfo;
|
|
54176
|
-
const response = await this.rpc.call("rglob", {
|
|
54177
|
-
baseDir: projectInfo.data_dir,
|
|
54178
|
-
pattern: "**/*.fine.mp4",
|
|
54179
|
-
//temporary
|
|
54180
|
-
noDirs: true
|
|
54181
|
-
});
|
|
54182
|
-
const mp4Files = response.entries.filter((entry) => entry.type === "file").map((entry) => entry.path);
|
|
54183
|
-
const sessions = getUniqueSessionTemplates(mp4Files, this.projectInfoService.projectInfo.views).map((templateName) => {
|
|
54184
|
-
return { key: templateName.replace(/\.mp4$/, "") };
|
|
54185
|
-
});
|
|
54186
|
-
return this.allSessions.next(sessions);
|
|
54187
|
-
}
|
|
54188
|
-
async loadPredictionIndex() {
|
|
54189
|
-
const projectInfo = this.projectInfoService.projectInfo;
|
|
54190
|
-
const response = await this.rpc.call("rglob", {
|
|
54191
|
-
baseDir: projectInfo.model_dir,
|
|
54192
|
-
pattern: "**/video_preds/**/*.csv",
|
|
54193
|
-
noDirs: true
|
|
54194
|
-
});
|
|
54195
|
-
this.predictionFiles = response.entries.filter((entry) => {
|
|
54196
|
-
if (entry.type !== "file")
|
|
54197
|
-
return false;
|
|
54198
|
-
if (entry.path.endsWith("_bbox.csv"))
|
|
54199
|
-
return false;
|
|
54200
|
-
if (entry.path.endsWith("_error.csv"))
|
|
54201
|
-
return false;
|
|
54202
|
-
if (entry.path.endsWith("_loss.csv"))
|
|
54203
|
-
return false;
|
|
54204
|
-
if (entry.path.endsWith("_norm.csv"))
|
|
54205
|
-
return false;
|
|
54206
|
-
return true;
|
|
54207
|
-
}).map((entry) => {
|
|
54208
|
-
let match2 = entry.path.match(/(.+)\/video_preds\/([^/]+)\.mp4\/predictions\.csv/);
|
|
54209
|
-
if (!match2) {
|
|
54210
|
-
match2 = entry.path.match(/(.+)\/video_preds\/([^/]+)\.csv/);
|
|
54211
|
-
}
|
|
54212
|
-
if (!match2)
|
|
54213
|
-
return null;
|
|
54214
|
-
const modelKey = match2[1];
|
|
54215
|
-
const sessionView = match2[2];
|
|
54216
|
-
const viewName = this.projectInfoService.allViews().find((v) => sessionView.includes(v));
|
|
54217
|
-
if (!viewName)
|
|
54218
|
-
return null;
|
|
54219
|
-
const sessionKey = sessionView.replace(viewName, "*");
|
|
54220
|
-
return {
|
|
54221
|
-
path: entry.path,
|
|
54222
|
-
modelKey,
|
|
54223
|
-
sessionKey,
|
|
54224
|
-
viewName
|
|
54225
|
-
};
|
|
54226
|
-
}).filter((entry) => entry != null);
|
|
54227
|
-
this.initModels();
|
|
54228
|
-
await this.initKeypoints();
|
|
54229
|
-
}
|
|
54230
|
-
initModels() {
|
|
54231
|
-
const uniqueModels = new Set(this.predictionFiles.map((pfile) => pfile.modelKey).filter((x) => x));
|
|
54232
|
-
this.projectInfoService.setAllModels(Array.from(uniqueModels).sort());
|
|
54233
|
-
}
|
|
54234
|
-
async initKeypoints() {
|
|
54235
|
-
if (this.predictionFiles.length === 0)
|
|
54236
|
-
return;
|
|
54237
|
-
const csvFile = await this.getPredictionFile(this.predictionFiles[0]);
|
|
54238
|
-
if (!csvFile)
|
|
54239
|
-
return;
|
|
54240
|
-
const allKeypoints = this.csvParser.getBodyParts(csvFile);
|
|
54241
|
-
this.projectInfoService.setAllKeypoints(allKeypoints);
|
|
54242
|
-
}
|
|
54243
|
-
getPredictionFilesForSession(sessionKey) {
|
|
54244
|
-
const predictionFiles = this.predictionFiles.filter((p) => p.sessionKey === sessionKey || p.sessionKey === sessionKey.replace(/\.fine$/, ""));
|
|
54245
|
-
return predictionFiles;
|
|
54246
|
-
}
|
|
54247
|
-
async getPredictionFile(pfile) {
|
|
54248
|
-
const modelDir = this.projectInfoService.projectInfo?.model_dir;
|
|
54249
|
-
const src = "/app/v0/files/" + modelDir + "/" + pfile.path;
|
|
54250
|
-
return await firstValueFrom(this.httpClient.get(src, { responseType: "text" }).pipe(catchError((error) => {
|
|
54251
|
-
if (error.status === 404) {
|
|
54252
|
-
return [null];
|
|
54253
|
-
}
|
|
54254
|
-
throw error;
|
|
54255
|
-
})));
|
|
54256
|
-
}
|
|
54257
|
-
async ffprobe(file) {
|
|
54258
|
-
const response = await this.rpc.call("ffprobe", {
|
|
54259
|
-
path: file
|
|
54260
|
-
});
|
|
54261
|
-
return response;
|
|
54262
|
-
}
|
|
54263
|
-
static \u0275fac = function SessionService_Factory(__ngFactoryType__) {
|
|
54264
|
-
return new (__ngFactoryType__ || _SessionService)();
|
|
54265
|
-
};
|
|
54266
|
-
static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _SessionService, factory: _SessionService.\u0275fac, providedIn: "root" });
|
|
54267
|
-
};
|
|
54268
|
-
(() => {
|
|
54269
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(SessionService, [{
|
|
54270
|
-
type: Injectable,
|
|
54271
|
-
args: [{
|
|
54272
|
-
providedIn: "root"
|
|
54273
|
-
}]
|
|
54274
|
-
}], null, null);
|
|
54275
|
-
})();
|
|
54276
|
-
function getUniqueSessionTemplates(filenames, views) {
|
|
54277
|
-
const sessionTemplates = /* @__PURE__ */ new Set();
|
|
54278
|
-
for (const filename of filenames) {
|
|
54279
|
-
const viewName = views.find((v) => filename.includes(v));
|
|
54280
|
-
const sessionTemplate = viewName ? filename.replace(viewName, "*") : filename;
|
|
54281
|
-
if (!sessionTemplates.has(sessionTemplate)) {
|
|
54282
|
-
sessionTemplates.add(sessionTemplate);
|
|
54283
|
-
}
|
|
54284
|
-
}
|
|
54285
|
-
return Array.from(sessionTemplates);
|
|
54286
|
-
}
|
|
54287
|
-
|
|
54288
54316
|
// src/app/viewer/viewer-left-panel/viewer-sessions-panel.component.ts
|
|
54289
54317
|
var _c03 = (a0) => ["/viewer", a0];
|
|
54290
54318
|
var _forTrack0 = ($index, $item) => $item.key;
|
|
@@ -54303,19 +54331,11 @@ function ViewerSessionsPanelComponent_For_16_Template(rf, ctx) {
|
|
|
54303
54331
|
}
|
|
54304
54332
|
}
|
|
54305
54333
|
var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
|
|
54306
|
-
sessionService;
|
|
54307
|
-
sessions;
|
|
54308
|
-
constructor(sessionService) {
|
|
54309
|
-
this.sessionService = sessionService;
|
|
54310
|
-
this.sessions = toSignal(this.sessionService.getAllSessions(), {
|
|
54311
|
-
requireSync: true
|
|
54312
|
-
});
|
|
54313
|
-
}
|
|
54314
|
-
selectedSession = input(null);
|
|
54334
|
+
sessionService = inject(SessionService);
|
|
54315
54335
|
static \u0275fac = function ViewerSessionsPanelComponent_Factory(__ngFactoryType__) {
|
|
54316
|
-
return new (__ngFactoryType__ || _ViewerSessionsPanelComponent)(
|
|
54336
|
+
return new (__ngFactoryType__ || _ViewerSessionsPanelComponent)();
|
|
54317
54337
|
};
|
|
54318
|
-
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ViewerSessionsPanelComponent, selectors: [["app-sessions-panel"]],
|
|
54338
|
+
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ViewerSessionsPanelComponent, selectors: [["app-sessions-panel"]], decls: 17, vars: 0, consts: [[1, "flex", "justify-between"], [1, "px-2"], [1, "dropdown", "dropdown-end"], [1, "btn", "btn-xs", "btn-ghost"], [1, "material-icons", "text-base!"], ["tabindex", "0", 1, "dropdown-content", "menu", "menu-sm", "bg-base-100", "z-40", "w-52", "p-2", "shadow-sm"], [1, "panel-content", "inset-shadow-xs", "inset-shadow-black/30"], [1, "overflow-x-auto"], ["tabindex", "0", "role", "listbox", 1, "w-fit", "relative"], ["routerLinkActive", "bg-sky-700", 1, "text-nowrap"], ["role", "option", 1, "p-1", "block", "hover:bg-base-content/10", "rounded-sm", 3, "routerLink"]], template: function ViewerSessionsPanelComponent_Template(rf, ctx) {
|
|
54319
54339
|
if (rf & 1) {
|
|
54320
54340
|
\u0275\u0275elementStart(0, "div", 0)(1, "h1", 1);
|
|
54321
54341
|
\u0275\u0275text(2, "Sessions");
|
|
@@ -54332,7 +54352,7 @@ var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
|
|
|
54332
54352
|
}
|
|
54333
54353
|
if (rf & 2) {
|
|
54334
54354
|
\u0275\u0275advance(15);
|
|
54335
|
-
\u0275\u0275repeater(ctx.
|
|
54355
|
+
\u0275\u0275repeater(ctx.sessionService.allSessions());
|
|
54336
54356
|
}
|
|
54337
54357
|
}, dependencies: [RouterLink, RouterLinkActive, MatListModule, ScrollingModule], encapsulation: 2, changeDetection: 0 });
|
|
54338
54358
|
};
|
|
@@ -54361,10 +54381,11 @@ var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
|
|
|
54361
54381
|
<div class="panel-content inset-shadow-xs inset-shadow-black/30">
|
|
54362
54382
|
<!--panel content-->
|
|
54363
54383
|
<div class="overflow-x-auto">
|
|
54364
|
-
<ul class="w-fit relative">
|
|
54365
|
-
@for (session of
|
|
54384
|
+
<ul class="w-fit relative" tabindex="0" role="listbox">
|
|
54385
|
+
@for (session of sessionService.allSessions(); track session.key) {
|
|
54366
54386
|
<li class="text-nowrap" routerLinkActive="bg-sky-700">
|
|
54367
54387
|
<a
|
|
54388
|
+
role="option"
|
|
54368
54389
|
class="p-1 block hover:bg-base-content/10 rounded-sm"
|
|
54369
54390
|
[routerLink]="['/viewer', session.key]"
|
|
54370
54391
|
>
|
|
@@ -54376,28 +54397,41 @@ var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
|
|
|
54376
54397
|
</div>
|
|
54377
54398
|
</div>
|
|
54378
54399
|
` }]
|
|
54379
|
-
}],
|
|
54400
|
+
}], null, null);
|
|
54380
54401
|
})();
|
|
54381
54402
|
(() => {
|
|
54382
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerSessionsPanelComponent, { className: "ViewerSessionsPanelComponent", filePath: "src/app/viewer/viewer-left-panel/viewer-sessions-panel.component.ts", lineNumber:
|
|
54403
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerSessionsPanelComponent, { className: "ViewerSessionsPanelComponent", filePath: "src/app/viewer/viewer-left-panel/viewer-sessions-panel.component.ts", lineNumber: 24 });
|
|
54383
54404
|
})();
|
|
54384
54405
|
|
|
54406
|
+
// src/app/utils/comparators.ts
|
|
54407
|
+
var compareStringArraysOrdered = (prev, curr) => {
|
|
54408
|
+
if (prev.length !== curr.length) {
|
|
54409
|
+
return false;
|
|
54410
|
+
}
|
|
54411
|
+
for (let i = 0; i < prev.length; i++) {
|
|
54412
|
+
if (prev[i] !== curr[i]) {
|
|
54413
|
+
return false;
|
|
54414
|
+
}
|
|
54415
|
+
}
|
|
54416
|
+
return true;
|
|
54417
|
+
};
|
|
54418
|
+
|
|
54385
54419
|
// src/app/view-settings.model.ts
|
|
54386
54420
|
var ViewSettings = class _ViewSettings {
|
|
54387
54421
|
_viewsShown = new BehaviorSubject([]);
|
|
54388
|
-
viewsShown$ = this._viewsShown.asObservable().pipe(distinctUntilChanged());
|
|
54422
|
+
viewsShown$ = this._viewsShown.asObservable().pipe(distinctUntilChanged(compareStringArraysOrdered));
|
|
54389
54423
|
viewsShown = toSignal(this.viewsShown$, { requireSync: true });
|
|
54390
54424
|
setViewsShown(selected) {
|
|
54391
54425
|
this._viewsShown.next(selected);
|
|
54392
54426
|
}
|
|
54393
54427
|
_keypointsShown = new BehaviorSubject([]);
|
|
54394
|
-
keypointsShown$ = this._keypointsShown.asObservable().pipe(distinctUntilChanged());
|
|
54428
|
+
keypointsShown$ = this._keypointsShown.asObservable().pipe(distinctUntilChanged(compareStringArraysOrdered));
|
|
54395
54429
|
keypointsShown = toSignal(this.keypointsShown$, { requireSync: true });
|
|
54396
54430
|
setKeypointsShown(selected) {
|
|
54397
54431
|
this._keypointsShown.next(selected);
|
|
54398
54432
|
}
|
|
54399
54433
|
_modelsShown = new BehaviorSubject([]);
|
|
54400
|
-
modelsShown$ = this._modelsShown.asObservable().pipe(distinctUntilChanged());
|
|
54434
|
+
modelsShown$ = this._modelsShown.asObservable().pipe(distinctUntilChanged(compareStringArraysOrdered));
|
|
54401
54435
|
modelsShown = toSignal(this.modelsShown$, { requireSync: true });
|
|
54402
54436
|
setModelsShown(selected) {
|
|
54403
54437
|
this._modelsShown.next(selected);
|
|
@@ -54630,7 +54664,7 @@ var VideoPlayerControlsComponent = class _VideoPlayerControlsComponent {
|
|
|
54630
54664
|
var _c04 = ["videoEl"];
|
|
54631
54665
|
var _c13 = ["*"];
|
|
54632
54666
|
var VideoTileComponent = class _VideoTileComponent {
|
|
54633
|
-
|
|
54667
|
+
host;
|
|
54634
54668
|
videoElement = null;
|
|
54635
54669
|
src = input("");
|
|
54636
54670
|
// Current time of the video element, for displaying for debug info.
|
|
@@ -54638,8 +54672,8 @@ var VideoTileComponent = class _VideoTileComponent {
|
|
|
54638
54672
|
// the main state, injected from parent so it can be easily shared across video players
|
|
54639
54673
|
videoPlayerState = inject(VideoPlayerState);
|
|
54640
54674
|
contentEnd = new EventEmitter();
|
|
54641
|
-
constructor(
|
|
54642
|
-
this.
|
|
54675
|
+
constructor(host) {
|
|
54676
|
+
this.host = host;
|
|
54643
54677
|
this.videoPlayerState.registerVideoPlayer(this);
|
|
54644
54678
|
combineLatest([
|
|
54645
54679
|
this.videoPlayerState.currentTime,
|
|
@@ -54648,6 +54682,12 @@ var VideoTileComponent = class _VideoTileComponent {
|
|
|
54648
54682
|
this.updateLocalCurrentTime();
|
|
54649
54683
|
});
|
|
54650
54684
|
}
|
|
54685
|
+
ngOnInit() {
|
|
54686
|
+
const observer = new ResizeObserver(() => {
|
|
54687
|
+
this.updateScaleFactorSignal();
|
|
54688
|
+
});
|
|
54689
|
+
observer.observe(this.host.nativeElement);
|
|
54690
|
+
}
|
|
54651
54691
|
ngOnDestroy() {
|
|
54652
54692
|
this.videoPlayerState.unregisterVideoPlayer(this);
|
|
54653
54693
|
}
|
|
@@ -54665,9 +54705,12 @@ var VideoTileComponent = class _VideoTileComponent {
|
|
|
54665
54705
|
width: this.videoElement?.nativeElement.videoWidth ?? 1,
|
|
54666
54706
|
duration: this.videoElement?.nativeElement.duration ?? 0
|
|
54667
54707
|
});
|
|
54668
|
-
this.
|
|
54708
|
+
this.updateScaleFactorSignal();
|
|
54669
54709
|
this.showProjectedContent.set(true);
|
|
54670
54710
|
}
|
|
54711
|
+
updateScaleFactorSignal() {
|
|
54712
|
+
this.scaleFactor.set(this.host.nativeElement.clientWidth / this.videoMetadata.value.width);
|
|
54713
|
+
}
|
|
54671
54714
|
updateLocalCurrentTime() {
|
|
54672
54715
|
this.localCurrentTime.set(this.videoElement?.nativeElement.currentTime ?? 0);
|
|
54673
54716
|
}
|
|
@@ -54686,7 +54729,7 @@ var VideoTileComponent = class _VideoTileComponent {
|
|
|
54686
54729
|
let _t;
|
|
54687
54730
|
\u0275\u0275queryRefresh(_t = \u0275\u0275loadQuery()) && (ctx.videoElement = _t.first);
|
|
54688
54731
|
}
|
|
54689
|
-
}, inputs: { src: [1, "src"] }, outputs: { contentEnd: "contentEnd" }, ngContentSelectors: _c13, decls: 6, vars: 3, consts: [["videoEl", ""], [1, "relative", "shadow-lg", "shadow-black/20", "bg-base-200"], ["preload", "auto", "muted", "",
|
|
54732
|
+
}, inputs: { src: [1, "src"] }, outputs: { contentEnd: "contentEnd" }, ngContentSelectors: _c13, decls: 6, vars: 3, consts: [["videoEl", ""], [1, "relative", "shadow-lg", "shadow-black/20", "bg-base-200"], ["preload", "auto", "muted", "", 3, "loadedmetadata", "timeupdate", "ended"], ["type", "video/mp4", 3, "src"]], template: function VideoTileComponent_Template(rf, ctx) {
|
|
54690
54733
|
if (rf & 1) {
|
|
54691
54734
|
const _r1 = \u0275\u0275getCurrentView();
|
|
54692
54735
|
\u0275\u0275projectionDef();
|
|
@@ -54713,12 +54756,12 @@ var VideoTileComponent = class _VideoTileComponent {
|
|
|
54713
54756
|
\u0275\u0275advance();
|
|
54714
54757
|
\u0275\u0275classProp("invisible", !ctx.showProjectedContent());
|
|
54715
54758
|
}
|
|
54716
|
-
},
|
|
54759
|
+
}, styles: ["\n\n[_nghost-%COMP%] {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-XSYKMARQ.css.map */"], changeDetection: 0 });
|
|
54717
54760
|
};
|
|
54718
54761
|
(() => {
|
|
54719
54762
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(VideoTileComponent, [{
|
|
54720
54763
|
type: Component,
|
|
54721
|
-
args: [{ selector: "app-video-tile", imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: '<div class="relative shadow-lg shadow-black/20 bg-base-200">\n <!-- bg is visible during loading state -->\n <!-- wrapper to contain video + keypoints -->\n <video\n #videoEl\n preload="auto"\n (loadedmetadata)="onLoadedMetadata()"\n muted\
|
|
54764
|
+
args: [{ selector: "app-video-tile", imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\r\n :host {\r\n /* Display for angular elements is inline by default.\r\n This is a problem when querying the host height, because it ends up being just 0.\r\n */\r\n display: block;\r\n }\r\n</style>\r\n\r\n<div class="relative shadow-lg shadow-black/20 bg-base-200">\r\n <!-- bg is visible during loading state -->\r\n <!-- wrapper to contain video + keypoints -->\r\n <video\r\n #videoEl\r\n preload="auto"\r\n (loadedmetadata)="onLoadedMetadata()"\r\n muted\r\n (timeupdate)="updateLocalCurrentTime()"\r\n (ended)="onEnd()"\r\n >\r\n <source [src]="src()" type="video/mp4" />\r\n </video>\r\n <div [class.invisible]="!showProjectedContent()">\r\n <!-- wrapper to hide content until video inited -->\r\n <ng-content></ng-content>\r\n </div>\r\n</div>\r\n<!--<p>T: {{ localCurrentTime() }}</p>-->\r\n', styles: ["/* angular:styles/component:css;cca0ce7e24de60b661844ed36e56ee3734a21f5d38966246b6b69d25889a81d9;/home/ksikka/lightning-pose-app/web_ui/src/app/components/video-player/video-tile/video-tile.component.html */\n:host {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-XSYKMARQ.css.map */\n"] }]
|
|
54722
54765
|
}], () => [{ type: ElementRef }], { videoElement: [{
|
|
54723
54766
|
type: ViewChild,
|
|
54724
54767
|
args: ["videoEl", { static: true }]
|
|
@@ -58960,22 +59003,30 @@ var Pair = class _Pair {
|
|
|
58960
59003
|
var _forTrack03 = ($index, $item) => $item.videoSrc;
|
|
58961
59004
|
function ViewerCenterPanelComponent_For_3_Template(rf, ctx) {
|
|
58962
59005
|
if (rf & 1) {
|
|
58963
|
-
\u0275\
|
|
58964
|
-
\u0275\
|
|
59006
|
+
const _r1 = \u0275\u0275getCurrentView();
|
|
59007
|
+
\u0275\u0275elementStart(0, "div", 2)(1, "div", 3)(2, "h3");
|
|
59008
|
+
\u0275\u0275text(3);
|
|
58965
59009
|
\u0275\u0275elementEnd();
|
|
59010
|
+
\u0275\u0275elementStart(4, "span", 4);
|
|
59011
|
+
\u0275\u0275listener("click", function ViewerCenterPanelComponent_For_3_Template_span_click_4_listener() {
|
|
59012
|
+
const w_r2 = \u0275\u0275restoreView(_r1).$implicit;
|
|
59013
|
+
const ctx_r2 = \u0275\u0275nextContext();
|
|
59014
|
+
return \u0275\u0275resetView(ctx_r2.onWidgetCloseClick(w_r2));
|
|
59015
|
+
});
|
|
59016
|
+
\u0275\u0275text(5, "close");
|
|
59017
|
+
\u0275\u0275elementEnd()();
|
|
59018
|
+
\u0275\u0275elementStart(6, "app-video-tile", 5);
|
|
59019
|
+
\u0275\u0275element(7, "app-keypoint-container", 6);
|
|
59020
|
+
\u0275\u0275elementEnd()();
|
|
58966
59021
|
}
|
|
58967
59022
|
if (rf & 2) {
|
|
58968
|
-
const
|
|
58969
|
-
\u0275\
|
|
59023
|
+
const w_r2 = ctx.$implicit;
|
|
59024
|
+
\u0275\u0275advance(3);
|
|
59025
|
+
\u0275\u0275textInterpolate(w_r2.id);
|
|
59026
|
+
\u0275\u0275advance(3);
|
|
59027
|
+
\u0275\u0275property("src", w_r2.videoSrc);
|
|
58970
59028
|
\u0275\u0275advance();
|
|
58971
|
-
\u0275\u0275property("labelerMode", false)("keypointModels",
|
|
58972
|
-
}
|
|
58973
|
-
}
|
|
58974
|
-
function ViewerCenterPanelComponent_ForEmpty_4_Template(rf, ctx) {
|
|
58975
|
-
if (rf & 1) {
|
|
58976
|
-
\u0275\u0275elementStart(0, "p");
|
|
58977
|
-
\u0275\u0275text(1, "Loading...");
|
|
58978
|
-
\u0275\u0275elementEnd();
|
|
59029
|
+
\u0275\u0275property("labelerMode", false)("keypointModels", w_r2.keypoints());
|
|
58979
59030
|
}
|
|
58980
59031
|
}
|
|
58981
59032
|
var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
|
|
@@ -58999,26 +59050,32 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
|
|
|
58999
59050
|
const sessionKey = this.sessionKey();
|
|
59000
59051
|
if (!sessionKey)
|
|
59001
59052
|
return;
|
|
59002
|
-
this.
|
|
59003
|
-
|
|
59004
|
-
|
|
59005
|
-
|
|
59006
|
-
|
|
59007
|
-
|
|
59008
|
-
|
|
59009
|
-
|
|
59010
|
-
|
|
59011
|
-
|
|
59012
|
-
|
|
59013
|
-
|
|
59053
|
+
const session = this.sessionService.allSessions().find((session2) => session2.key === sessionKey);
|
|
59054
|
+
if (!session)
|
|
59055
|
+
return;
|
|
59056
|
+
this.widgetModels.set(
|
|
59057
|
+
//this.projectInfoService.allViews().flatMap((view) => {
|
|
59058
|
+
session.views.flatMap((sessionView) => {
|
|
59059
|
+
const keypointModels = this.projectInfoService.allModels().flatMap((modelKey) => {
|
|
59060
|
+
const predictions = this.predictions.get(new Pair(sessionView.viewName, modelKey).toMapKey());
|
|
59061
|
+
if (predictions) {
|
|
59062
|
+
return this.projectInfoService.allKeypoints().map((k) => this.buildKeypoint(k, predictions, modelKey));
|
|
59063
|
+
} else {
|
|
59064
|
+
return [];
|
|
59065
|
+
}
|
|
59066
|
+
});
|
|
59067
|
+
const widget = this.buildWidget(sessionView, keypointModels);
|
|
59068
|
+
return widget;
|
|
59069
|
+
})
|
|
59070
|
+
);
|
|
59014
59071
|
}
|
|
59015
|
-
buildWidget(
|
|
59072
|
+
buildWidget(sessionView, allKeypoints) {
|
|
59016
59073
|
const filteredKeypoints = computed(() => {
|
|
59017
59074
|
return allKeypoints.filter((k) => this.viewSettings.modelsShown().includes(k.modelKey) && this.viewSettings.keypointsShown().includes(k.name));
|
|
59018
59075
|
});
|
|
59019
59076
|
return {
|
|
59020
|
-
id:
|
|
59021
|
-
videoSrc: this.getVideoSrc(
|
|
59077
|
+
id: sessionView.viewName,
|
|
59078
|
+
videoSrc: this.getVideoSrc(sessionView),
|
|
59022
59079
|
keypoints: filteredKeypoints
|
|
59023
59080
|
};
|
|
59024
59081
|
}
|
|
@@ -59055,8 +59112,8 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
|
|
|
59055
59112
|
const dataDir = this.projectInfoService.projectInfo?.data_dir;
|
|
59056
59113
|
return dataDir + "/" + sessionKey.replace(/\*/g, view) + ".mp4";
|
|
59057
59114
|
}
|
|
59058
|
-
getVideoSrc(
|
|
59059
|
-
return "/app/v0/files/" +
|
|
59115
|
+
getVideoSrc(sessionView) {
|
|
59116
|
+
return "/app/v0/files/" + sessionView.videoPath;
|
|
59060
59117
|
}
|
|
59061
59118
|
async loadSession(sessionKey) {
|
|
59062
59119
|
this.sessionKey.set(sessionKey);
|
|
@@ -59071,7 +59128,7 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
|
|
|
59071
59128
|
this.loadPredictionFiles()
|
|
59072
59129
|
]);
|
|
59073
59130
|
this.loadingService.isLoading.set(false);
|
|
59074
|
-
this.buildWidgetModels();
|
|
59131
|
+
await this.buildWidgetModels();
|
|
59075
59132
|
}
|
|
59076
59133
|
async loadPredictionFiles() {
|
|
59077
59134
|
const sessionKey = this.sessionKey();
|
|
@@ -59105,15 +59162,19 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
|
|
|
59105
59162
|
this.videoPlayerState.duration.set(data.duration);
|
|
59106
59163
|
this.videoPlayerState.fps.set(data.fps);
|
|
59107
59164
|
}
|
|
59165
|
+
onWidgetCloseClick(w) {
|
|
59166
|
+
const nextViewsShown = this.viewSettings.viewsShown().filter((v) => v != w.id);
|
|
59167
|
+
this.viewSettings.setViewsShown(nextViewsShown);
|
|
59168
|
+
}
|
|
59108
59169
|
static \u0275fac = function ViewerCenterPanelComponent_Factory(__ngFactoryType__) {
|
|
59109
59170
|
return new (__ngFactoryType__ || _ViewerCenterPanelComponent)();
|
|
59110
59171
|
};
|
|
59111
|
-
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ViewerCenterPanelComponent, selectors: [["app-viewer-center-panel"]], inputs: { viewSettings: "viewSettings" }, decls:
|
|
59172
|
+
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ViewerCenterPanelComponent, selectors: [["app-viewer-center-panel"]], inputs: { viewSettings: "viewSettings" }, decls: 5, vars: 0, consts: [[1, "flex", "flex-col", "h-full"], [1, "w-full", "grow", "flex", "flex-wrap", "overflow-y-auto", "gap-4", "*:max-w-xs", "justify-start", "items-start", "content-start", "p-4"], [1, "panel", "!border-none"], [1, "px-2", "py-1", "text-sm", "flex", "justify-between"], [1, "btn", "btn-circle", "btn-xs", "material-icons", "!text-sm", 3, "click"], [1, "w-auto", 3, "src"], [3, "labelerMode", "keypointModels"]], template: function ViewerCenterPanelComponent_Template(rf, ctx) {
|
|
59112
59173
|
if (rf & 1) {
|
|
59113
59174
|
\u0275\u0275elementStart(0, "div", 0)(1, "div", 1);
|
|
59114
|
-
\u0275\u0275repeaterCreate(2, ViewerCenterPanelComponent_For_3_Template,
|
|
59175
|
+
\u0275\u0275repeaterCreate(2, ViewerCenterPanelComponent_For_3_Template, 8, 4, "div", 2, _forTrack03);
|
|
59115
59176
|
\u0275\u0275elementEnd();
|
|
59116
|
-
\u0275\u0275element(
|
|
59177
|
+
\u0275\u0275element(4, "app-video-player-controls");
|
|
59117
59178
|
\u0275\u0275elementEnd();
|
|
59118
59179
|
}
|
|
59119
59180
|
if (rf & 2) {
|
|
@@ -59133,13 +59194,13 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
|
|
|
59133
59194
|
VideoPlayerControlsComponent,
|
|
59134
59195
|
VideoTileComponent,
|
|
59135
59196
|
KeypointContainerComponent
|
|
59136
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: '<div class="flex flex-col h-full">\n <!-- Video content area -->\n <div\n class="
|
|
59197
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: '<div class="flex flex-col h-full">\r\n <!-- Video content area -->\r\n <div\r\n class="w-full grow flex flex-wrap overflow-y-auto gap-4 *:max-w-xs justify-start items-start content-start p-4"\r\n >\r\n <!-- `track videosrc` makes the widget component get\r\n destroyed and recreated if videoSrc changes. Reactively\r\n changing videoSrc of an existing video does not work unfortunately. -->\r\n @for (w of filteredWidgetModels(); track w.videoSrc) {\r\n <div class="panel !border-none">\r\n <div class="px-2 py-1 text-sm flex justify-between">\r\n <h3>{{ w.id }}</h3>\r\n <span\r\n class="btn btn-circle btn-xs material-icons !text-sm"\r\n (click)="onWidgetCloseClick(w)"\r\n >close</span\r\n >\r\n </div>\r\n <app-video-tile [src]="w.videoSrc" class="w-auto">\r\n <app-keypoint-container\r\n [labelerMode]="false"\r\n [keypointModels]="w.keypoints()"\r\n ></app-keypoint-container>\r\n </app-video-tile>\r\n </div>\r\n }\r\n </div>\r\n\r\n <app-video-player-controls></app-video-player-controls>\r\n</div>\r\n' }]
|
|
59137
59198
|
}], null, { viewSettings: [{
|
|
59138
59199
|
type: Input
|
|
59139
59200
|
}] });
|
|
59140
59201
|
})();
|
|
59141
59202
|
(() => {
|
|
59142
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerCenterPanelComponent, { className: "ViewerCenterPanelComponent", filePath: "src/app/viewer/viewer-center-panel/viewer-center-panel.component.ts", lineNumber:
|
|
59203
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerCenterPanelComponent, { className: "ViewerCenterPanelComponent", filePath: "src/app/viewer/viewer-center-panel/viewer-center-panel.component.ts", lineNumber: 38 });
|
|
59143
59204
|
})();
|
|
59144
59205
|
|
|
59145
59206
|
// src/app/loading-bar/loading-bar.component.ts
|
|
@@ -59182,19 +59243,14 @@ var LoadingBarComponent = class _LoadingBarComponent {
|
|
|
59182
59243
|
|
|
59183
59244
|
// src/app/viewer/viewer-page/viewer-page.component.ts
|
|
59184
59245
|
var _c05 = (a0) => [a0];
|
|
59185
|
-
function
|
|
59186
|
-
if (rf & 1) {
|
|
59187
|
-
\u0275\u0275text(0, " Loading...\n");
|
|
59188
|
-
}
|
|
59189
|
-
}
|
|
59190
|
-
function ViewerPageComponent_Conditional_1_For_14_Template(rf, ctx) {
|
|
59246
|
+
function ViewerPageComponent_Conditional_0_For_14_Template(rf, ctx) {
|
|
59191
59247
|
if (rf & 1) {
|
|
59192
59248
|
const _r1 = \u0275\u0275getCurrentView();
|
|
59193
|
-
\u0275\u0275elementStart(0, "li",
|
|
59249
|
+
\u0275\u0275elementStart(0, "li", 9)(1, "span");
|
|
59194
59250
|
\u0275\u0275text(2);
|
|
59195
59251
|
\u0275\u0275elementEnd();
|
|
59196
|
-
\u0275\u0275elementStart(3, "input",
|
|
59197
|
-
\u0275\u0275listener("change", function
|
|
59252
|
+
\u0275\u0275elementStart(3, "input", 12);
|
|
59253
|
+
\u0275\u0275listener("change", function ViewerPageComponent_Conditional_0_For_14_Template_input_change_3_listener($event) {
|
|
59198
59254
|
const cam_r2 = \u0275\u0275restoreView(_r1).$implicit;
|
|
59199
59255
|
const ctx_r2 = \u0275\u0275nextContext(2);
|
|
59200
59256
|
return \u0275\u0275resetView(ctx_r2.onViewCheckboxChange($event, cam_r2));
|
|
@@ -59210,14 +59266,14 @@ function ViewerPageComponent_Conditional_1_For_14_Template(rf, ctx) {
|
|
|
59210
59266
|
\u0275\u0275property("checked", ctx_r2.viewSelectionModel.isSelected(cam_r2));
|
|
59211
59267
|
}
|
|
59212
59268
|
}
|
|
59213
|
-
function
|
|
59269
|
+
function ViewerPageComponent_Conditional_0_For_22_Template(rf, ctx) {
|
|
59214
59270
|
if (rf & 1) {
|
|
59215
59271
|
const _r4 = \u0275\u0275getCurrentView();
|
|
59216
|
-
\u0275\u0275elementStart(0, "li",
|
|
59272
|
+
\u0275\u0275elementStart(0, "li", 9)(1, "span");
|
|
59217
59273
|
\u0275\u0275text(2);
|
|
59218
59274
|
\u0275\u0275elementEnd();
|
|
59219
|
-
\u0275\u0275elementStart(3, "input",
|
|
59220
|
-
\u0275\u0275listener("change", function
|
|
59275
|
+
\u0275\u0275elementStart(3, "input", 12);
|
|
59276
|
+
\u0275\u0275listener("change", function ViewerPageComponent_Conditional_0_For_22_Template_input_change_3_listener($event) {
|
|
59221
59277
|
const kp_r5 = \u0275\u0275restoreView(_r4).$implicit;
|
|
59222
59278
|
const ctx_r2 = \u0275\u0275nextContext(2);
|
|
59223
59279
|
return \u0275\u0275resetView(ctx_r2.onKeypointCheckboxChange($event, kp_r5));
|
|
@@ -59233,9 +59289,9 @@ function ViewerPageComponent_Conditional_1_For_22_Template(rf, ctx) {
|
|
|
59233
59289
|
\u0275\u0275property("checked", ctx_r2.keypointSelectionModel.isSelected(kp_r5));
|
|
59234
59290
|
}
|
|
59235
59291
|
}
|
|
59236
|
-
function
|
|
59292
|
+
function ViewerPageComponent_Conditional_0_For_31_For_5_Template(rf, ctx) {
|
|
59237
59293
|
if (rf & 1) {
|
|
59238
|
-
\u0275\u0275elementStart(0, "option",
|
|
59294
|
+
\u0275\u0275elementStart(0, "option", 14);
|
|
59239
59295
|
\u0275\u0275text(1);
|
|
59240
59296
|
\u0275\u0275elementEnd();
|
|
59241
59297
|
}
|
|
@@ -59247,19 +59303,19 @@ function ViewerPageComponent_Conditional_1_For_31_For_5_Template(rf, ctx) {
|
|
|
59247
59303
|
\u0275\u0275textInterpolate1(" ", dropdownItem_r8, " ");
|
|
59248
59304
|
}
|
|
59249
59305
|
}
|
|
59250
|
-
function
|
|
59306
|
+
function ViewerPageComponent_Conditional_0_For_31_Template(rf, ctx) {
|
|
59251
59307
|
if (rf & 1) {
|
|
59252
59308
|
const _r6 = \u0275\u0275getCurrentView();
|
|
59253
59309
|
\u0275\u0275declareLet(0);
|
|
59254
|
-
\u0275\u0275elementStart(1, "li",
|
|
59255
|
-
\u0275\u0275listener("change", function
|
|
59310
|
+
\u0275\u0275elementStart(1, "li", 11)(2, "select", 13);
|
|
59311
|
+
\u0275\u0275listener("change", function ViewerPageComponent_Conditional_0_For_31_Template_select_change_2_listener($event) {
|
|
59256
59312
|
\u0275\u0275restoreView(_r6);
|
|
59257
59313
|
const modelIndex_r7 = \u0275\u0275readContextLet(0);
|
|
59258
59314
|
const ctx_r2 = \u0275\u0275nextContext(2);
|
|
59259
59315
|
return \u0275\u0275resetView(ctx_r2.onModelDropdownItemClick(modelIndex_r7, $event));
|
|
59260
59316
|
});
|
|
59261
59317
|
\u0275\u0275declareLet(3);
|
|
59262
|
-
\u0275\u0275repeaterCreate(4,
|
|
59318
|
+
\u0275\u0275repeaterCreate(4, ViewerPageComponent_Conditional_0_For_31_For_5_Template, 2, 2, "option", 14, \u0275\u0275repeaterTrackByIdentity);
|
|
59263
59319
|
\u0275\u0275elementEnd()();
|
|
59264
59320
|
}
|
|
59265
59321
|
if (rf & 2) {
|
|
@@ -59273,45 +59329,43 @@ function ViewerPageComponent_Conditional_1_For_31_Template(rf, ctx) {
|
|
|
59273
59329
|
\u0275\u0275repeater(options_r12);
|
|
59274
59330
|
}
|
|
59275
59331
|
}
|
|
59276
|
-
function
|
|
59332
|
+
function ViewerPageComponent_Conditional_0_Template(rf, ctx) {
|
|
59277
59333
|
if (rf & 1) {
|
|
59278
59334
|
\u0275\u0275elementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
|
|
59279
|
-
\u0275\u0275element(3, "app-sessions-panel"
|
|
59335
|
+
\u0275\u0275element(3, "app-sessions-panel");
|
|
59280
59336
|
\u0275\u0275elementEnd();
|
|
59281
|
-
\u0275\u0275element(4, "app-loading-bar",
|
|
59337
|
+
\u0275\u0275element(4, "app-loading-bar", 3);
|
|
59282
59338
|
\u0275\u0275elementEnd();
|
|
59283
|
-
\u0275\u0275element(5, "app-viewer-center-panel",
|
|
59284
|
-
\u0275\u0275elementStart(6, "div",
|
|
59339
|
+
\u0275\u0275element(5, "app-viewer-center-panel", 4);
|
|
59340
|
+
\u0275\u0275elementStart(6, "div", 5)(7, "div", 6)(8, "div")(9, "h1", 7);
|
|
59285
59341
|
\u0275\u0275text(10, "Views");
|
|
59286
59342
|
\u0275\u0275elementEnd()();
|
|
59287
|
-
\u0275\u0275elementStart(11, "div",
|
|
59288
|
-
\u0275\u0275repeaterCreate(13,
|
|
59343
|
+
\u0275\u0275elementStart(11, "div", 8)(12, "ul");
|
|
59344
|
+
\u0275\u0275repeaterCreate(13, ViewerPageComponent_Conditional_0_For_14_Template, 4, 2, "li", 9, \u0275\u0275repeaterTrackByIdentity);
|
|
59289
59345
|
\u0275\u0275elementEnd()()();
|
|
59290
|
-
\u0275\u0275elementStart(15, "div",
|
|
59346
|
+
\u0275\u0275elementStart(15, "div", 6)(16, "div")(17, "h1", 7);
|
|
59291
59347
|
\u0275\u0275text(18, "Keypoints");
|
|
59292
59348
|
\u0275\u0275elementEnd()();
|
|
59293
|
-
\u0275\u0275elementStart(19, "div",
|
|
59294
|
-
\u0275\u0275repeaterCreate(21,
|
|
59349
|
+
\u0275\u0275elementStart(19, "div", 8)(20, "ul");
|
|
59350
|
+
\u0275\u0275repeaterCreate(21, ViewerPageComponent_Conditional_0_For_22_Template, 4, 2, "li", 9, \u0275\u0275repeaterTrackByIdentity);
|
|
59295
59351
|
\u0275\u0275elementEnd()()();
|
|
59296
|
-
\u0275\u0275elementStart(23, "div",
|
|
59352
|
+
\u0275\u0275elementStart(23, "div", 6)(24, "div")(25, "h1", 7);
|
|
59297
59353
|
\u0275\u0275text(26, "Models");
|
|
59298
59354
|
\u0275\u0275elementEnd()();
|
|
59299
|
-
\u0275\u0275elementStart(27, "div",
|
|
59355
|
+
\u0275\u0275elementStart(27, "div", 10)(28, "ul");
|
|
59300
59356
|
\u0275\u0275declareLet(29);
|
|
59301
|
-
\u0275\u0275repeaterCreate(30,
|
|
59357
|
+
\u0275\u0275repeaterCreate(30, ViewerPageComponent_Conditional_0_For_31_Template, 6, 7, "li", 11, \u0275\u0275repeaterTrackByIndex);
|
|
59302
59358
|
\u0275\u0275elementEnd()()()()();
|
|
59303
59359
|
}
|
|
59304
59360
|
if (rf & 2) {
|
|
59305
59361
|
const ctx_r2 = \u0275\u0275nextContext();
|
|
59306
|
-
\u0275\u0275advance(
|
|
59307
|
-
\u0275\u0275property("selectedSession", ctx_r2.selectedSession());
|
|
59308
|
-
\u0275\u0275advance(2);
|
|
59362
|
+
\u0275\u0275advance(5);
|
|
59309
59363
|
\u0275\u0275property("viewSettings", ctx_r2.viewSettings);
|
|
59310
59364
|
\u0275\u0275advance(8);
|
|
59311
59365
|
\u0275\u0275repeater(ctx_r2.allViews);
|
|
59312
59366
|
\u0275\u0275advance(8);
|
|
59313
59367
|
\u0275\u0275repeater(ctx_r2.projectInfoService.allKeypoints());
|
|
59314
|
-
const modelSelectors_r13 = ctx_r2.viewSettings.modelsShown().length < 2 ? ctx_r2.viewSettings.modelsShown().concat(\u0275\u0275pureFunction1(
|
|
59368
|
+
const modelSelectors_r13 = ctx_r2.viewSettings.modelsShown().length < 2 ? ctx_r2.viewSettings.modelsShown().concat(\u0275\u0275pureFunction1(1, _c05, ctx_r2.noneOption)) : ctx_r2.viewSettings.modelsShown();
|
|
59315
59369
|
\u0275\u0275advance(9);
|
|
59316
59370
|
\u0275\u0275repeater(modelSelectors_r13);
|
|
59317
59371
|
}
|
|
@@ -59344,10 +59398,8 @@ var ViewerPageComponent = class _ViewerPageComponent {
|
|
|
59344
59398
|
isIniting = signal(true);
|
|
59345
59399
|
async ngOnInit() {
|
|
59346
59400
|
this.loadingService.isLoading.set(true);
|
|
59347
|
-
this.loadingService.maxProgress.set(
|
|
59401
|
+
this.loadingService.maxProgress.set(2);
|
|
59348
59402
|
this.loadingService.progress.set(0);
|
|
59349
|
-
await this.projectInfoService.loadProjectInfo();
|
|
59350
|
-
this.loadingService.progress.update((x) => x + 1);
|
|
59351
59403
|
const p = this.sessionService.loadPredictionIndex();
|
|
59352
59404
|
const s = this.sessionService.loadSessions().then(() => {
|
|
59353
59405
|
this.loadingService.progress.update((x) => x + 1);
|
|
@@ -59380,20 +59432,16 @@ var ViewerPageComponent = class _ViewerPageComponent {
|
|
|
59380
59432
|
this.viewSelectionModel.changed.asObservable().pipe(takeUntilDestroyed()).subscribe(() => {
|
|
59381
59433
|
this.viewSettings.setViewsShown(this.viewSelectionModel.selected);
|
|
59382
59434
|
});
|
|
59435
|
+
this.viewSettings.viewsShown$.pipe(takeUntilDestroyed()).subscribe((viewsShown) => {
|
|
59436
|
+
this.viewSelectionModel.setSelection(...viewsShown);
|
|
59437
|
+
});
|
|
59383
59438
|
this.keypointSelectionModel.changed.asObservable().pipe(takeUntilDestroyed()).subscribe(() => {
|
|
59384
59439
|
this.viewSettings.setKeypointsShown(this.keypointSelectionModel.selected);
|
|
59385
59440
|
});
|
|
59441
|
+
this.viewSettings.keypointsShown$.pipe(takeUntilDestroyed()).subscribe((keypointsShown) => {
|
|
59442
|
+
this.keypointSelectionModel.setSelection(...keypointsShown);
|
|
59443
|
+
});
|
|
59386
59444
|
}
|
|
59387
|
-
selectedSession = computed(() => {
|
|
59388
|
-
const sessionKey = this._sessionKey();
|
|
59389
|
-
if (!sessionKey) {
|
|
59390
|
-
return null;
|
|
59391
|
-
} else {
|
|
59392
|
-
return {
|
|
59393
|
-
key: sessionKey
|
|
59394
|
-
};
|
|
59395
|
-
}
|
|
59396
|
-
});
|
|
59397
59445
|
onKeypointCheckboxChange(event, keypointName) {
|
|
59398
59446
|
const target = event.target;
|
|
59399
59447
|
if (target.checked) {
|
|
@@ -59432,18 +59480,18 @@ var ViewerPageComponent = class _ViewerPageComponent {
|
|
|
59432
59480
|
if (rf & 2) {
|
|
59433
59481
|
\u0275\u0275queryAdvance();
|
|
59434
59482
|
}
|
|
59435
|
-
}, inputs: { sessionKey: "sessionKey" }, features: [\u0275\u0275ProvidersFeature([VideoPlayerState, ViewSettings])], decls:
|
|
59483
|
+
}, inputs: { sessionKey: "sessionKey" }, features: [\u0275\u0275ProvidersFeature([VideoPlayerState, ViewSettings])], decls: 1, vars: 1, consts: [[1, "grow", "flex", "flex-row", "items-stretch"], [1, "bg-base-200", "w-60", "xl:w-80", "flex", "flex-col", "justify-between", "shadow-lg"], [1, "p-4"], [1, "h-20", "p-4"], [1, "grow", "z-30", 3, "viewSettings"], [1, "shrink-0", "w-40", "xl:w-60", "h-full"], [1, "panel", "m-4"], [1, "px-2", "mb-1"], [1, "panel-content", "max-h-60", "overflow-y-auto"], [1, "p-1", "flex", "justify-between", "items-center"], [1, "panel-content", "max-h-60", "overflow-y-visible"], [1, "p-1", "w-full"], ["type", "checkbox", 1, "checkbox", "checkbox-xs", 3, "change", "checked"], [1, "select", "select-sm", "w-full", "flex-grow", 3, "change"], [3, "selected"]], template: function ViewerPageComponent_Template(rf, ctx) {
|
|
59436
59484
|
if (rf & 1) {
|
|
59437
|
-
\u0275\u0275template(0, ViewerPageComponent_Conditional_0_Template,
|
|
59485
|
+
\u0275\u0275template(0, ViewerPageComponent_Conditional_0_Template, 32, 3, "div", 0);
|
|
59438
59486
|
}
|
|
59439
59487
|
if (rf & 2) {
|
|
59440
|
-
\u0275\u0275conditional(ctx.isIniting() ? 0 : 1);
|
|
59488
|
+
\u0275\u0275conditional(!ctx.isIniting() ? 0 : -1);
|
|
59441
59489
|
}
|
|
59442
59490
|
}, dependencies: [
|
|
59443
59491
|
ViewerSessionsPanelComponent,
|
|
59444
59492
|
ViewerCenterPanelComponent,
|
|
59445
59493
|
LoadingBarComponent
|
|
59446
|
-
],
|
|
59494
|
+
], styles: ["\n\n[_nghost-%COMP%] {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-MRTIUFL2.css.map */"], changeDetection: 0 });
|
|
59447
59495
|
};
|
|
59448
59496
|
(() => {
|
|
59449
59497
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ViewerPageComponent, [{
|
|
@@ -59452,116 +59500,122 @@ var ViewerPageComponent = class _ViewerPageComponent {
|
|
|
59452
59500
|
ViewerSessionsPanelComponent,
|
|
59453
59501
|
ViewerCenterPanelComponent,
|
|
59454
59502
|
LoadingBarComponent
|
|
59455
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [VideoPlayerState, ViewSettings], template:
|
|
59456
|
-
|
|
59457
|
-
|
|
59458
|
-
|
|
59459
|
-
|
|
59460
|
-
|
|
59461
|
-
|
|
59462
|
-
|
|
59463
|
-
|
|
59464
|
-
|
|
59465
|
-
|
|
59466
|
-
|
|
59467
|
-
|
|
59468
|
-
<
|
|
59469
|
-
|
|
59470
|
-
|
|
59471
|
-
|
|
59472
|
-
|
|
59473
|
-
|
|
59474
|
-
|
|
59475
|
-
|
|
59476
|
-
|
|
59477
|
-
|
|
59478
|
-
|
|
59479
|
-
|
|
59480
|
-
|
|
59481
|
-
|
|
59482
|
-
|
|
59483
|
-
<div
|
|
59484
|
-
|
|
59485
|
-
|
|
59486
|
-
|
|
59487
|
-
|
|
59488
|
-
|
|
59489
|
-
|
|
59490
|
-
|
|
59491
|
-
|
|
59492
|
-
|
|
59493
|
-
|
|
59494
|
-
|
|
59495
|
-
|
|
59496
|
-
|
|
59497
|
-
|
|
59498
|
-
|
|
59499
|
-
|
|
59500
|
-
|
|
59501
|
-
|
|
59502
|
-
|
|
59503
|
-
|
|
59504
|
-
|
|
59505
|
-
|
|
59506
|
-
|
|
59507
|
-
|
|
59508
|
-
|
|
59509
|
-
|
|
59510
|
-
|
|
59511
|
-
|
|
59512
|
-
|
|
59513
|
-
|
|
59514
|
-
|
|
59515
|
-
|
|
59516
|
-
|
|
59517
|
-
|
|
59518
|
-
|
|
59519
|
-
|
|
59520
|
-
|
|
59521
|
-
|
|
59522
|
-
|
|
59523
|
-
|
|
59524
|
-
|
|
59525
|
-
|
|
59526
|
-
|
|
59527
|
-
|
|
59528
|
-
|
|
59529
|
-
|
|
59530
|
-
|
|
59531
|
-
|
|
59532
|
-
|
|
59533
|
-
@
|
|
59534
|
-
|
|
59535
|
-
|
|
59536
|
-
|
|
59537
|
-
|
|
59538
|
-
|
|
59539
|
-
|
|
59540
|
-
|
|
59541
|
-
|
|
59542
|
-
|
|
59543
|
-
|
|
59544
|
-
|
|
59545
|
-
|
|
59546
|
-
|
|
59547
|
-
|
|
59548
|
-
|
|
59549
|
-
|
|
59550
|
-
|
|
59551
|
-
|
|
59552
|
-
|
|
59553
|
-
|
|
59554
|
-
|
|
59555
|
-
|
|
59556
|
-
|
|
59557
|
-
}
|
|
59558
|
-
|
|
59503
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [VideoPlayerState, ViewSettings], template: `<style>\r
|
|
59504
|
+
:host {\r
|
|
59505
|
+
flex-grow: 1;\r
|
|
59506
|
+
display: flex;\r
|
|
59507
|
+
}\r
|
|
59508
|
+
</style>\r
|
|
59509
|
+
\r
|
|
59510
|
+
@if (!isIniting()) {\r
|
|
59511
|
+
<div class="grow flex flex-row items-stretch">\r
|
|
59512
|
+
<!-- Left pane -->\r
|
|
59513
|
+
<div\r
|
|
59514
|
+
class="bg-base-200 w-60 xl:w-80 flex flex-col justify-between shadow-lg"\r
|
|
59515
|
+
>\r
|
|
59516
|
+
<div class="p-4">\r
|
|
59517
|
+
<app-sessions-panel></app-sessions-panel>\r
|
|
59518
|
+
</div>\r
|
|
59519
|
+
<app-loading-bar class="h-20 p-4"></app-loading-bar>\r
|
|
59520
|
+
</div>\r
|
|
59521
|
+
\r
|
|
59522
|
+
<!-- Center content -->\r
|
|
59523
|
+
<app-viewer-center-panel\r
|
|
59524
|
+
class="grow z-30"\r
|
|
59525
|
+
[viewSettings]="viewSettings"\r
|
|
59526
|
+
></app-viewer-center-panel>\r
|
|
59527
|
+
\r
|
|
59528
|
+
<!-- Right pane -->\r
|
|
59529
|
+
<div class="shrink-0 w-40 xl:w-60 h-full">\r
|
|
59530
|
+
<div class="panel m-4">\r
|
|
59531
|
+
<div>\r
|
|
59532
|
+
<!-- panel header -->\r
|
|
59533
|
+
<h1 class="px-2 mb-1">Views</h1>\r
|
|
59534
|
+
</div>\r
|
|
59535
|
+
<div class="panel-content max-h-60 overflow-y-auto">\r
|
|
59536
|
+
<ul>\r
|
|
59537
|
+
@for (cam of allViews; track cam) {\r
|
|
59538
|
+
<li class="p-1 flex justify-between items-center">\r
|
|
59539
|
+
<span>{{ cam }}</span>\r
|
|
59540
|
+
<input\r
|
|
59541
|
+
type="checkbox"\r
|
|
59542
|
+
[checked]="viewSelectionModel.isSelected(cam)"\r
|
|
59543
|
+
class="checkbox checkbox-xs"\r
|
|
59544
|
+
(change)="onViewCheckboxChange($event, cam)"\r
|
|
59545
|
+
/>\r
|
|
59546
|
+
</li>\r
|
|
59547
|
+
}\r
|
|
59548
|
+
</ul>\r
|
|
59549
|
+
</div>\r
|
|
59550
|
+
</div>\r
|
|
59551
|
+
\r
|
|
59552
|
+
<div class="panel m-4">\r
|
|
59553
|
+
<div>\r
|
|
59554
|
+
<!-- panel header -->\r
|
|
59555
|
+
<h1 class="px-2 mb-1">Keypoints</h1>\r
|
|
59556
|
+
</div>\r
|
|
59557
|
+
<div class="panel-content max-h-60 overflow-y-auto">\r
|
|
59558
|
+
<ul>\r
|
|
59559
|
+
@for (kp of projectInfoService.allKeypoints(); track kp) {\r
|
|
59560
|
+
<li class="p-1 flex justify-between items-center">\r
|
|
59561
|
+
<span>{{ kp }}</span>\r
|
|
59562
|
+
<input\r
|
|
59563
|
+
type="checkbox"\r
|
|
59564
|
+
[checked]="keypointSelectionModel.isSelected(kp)"\r
|
|
59565
|
+
class="checkbox checkbox-xs"\r
|
|
59566
|
+
(change)="onKeypointCheckboxChange($event, kp)"\r
|
|
59567
|
+
/>\r
|
|
59568
|
+
</li>\r
|
|
59569
|
+
}\r
|
|
59570
|
+
</ul>\r
|
|
59571
|
+
</div>\r
|
|
59572
|
+
</div>\r
|
|
59573
|
+
\r
|
|
59574
|
+
<div class="panel m-4">\r
|
|
59575
|
+
<div>\r
|
|
59576
|
+
<!-- panel header -->\r
|
|
59577
|
+
<h1 class="px-2 mb-1">Models</h1>\r
|
|
59578
|
+
</div>\r
|
|
59579
|
+
<div class="panel-content max-h-60 overflow-y-visible">\r
|
|
59580
|
+
<ul>\r
|
|
59581
|
+
@let modelSelectors =\r
|
|
59582
|
+
viewSettings.modelsShown().length < 2\r
|
|
59583
|
+
? viewSettings.modelsShown().concat([noneOption])\r
|
|
59584
|
+
: viewSettings.modelsShown();\r
|
|
59585
|
+
<!-- it's important to track index, not model.\r
|
|
59586
|
+
if we put model here, something weird happens when you clear all models and then set a model. -->\r
|
|
59587
|
+
@for (model of modelSelectors; track $index) {\r
|
|
59588
|
+
@let modelIndex = $index;\r
|
|
59589
|
+
<li class="p-1 w-full">\r
|
|
59590
|
+
<select\r
|
|
59591
|
+
class="select select-sm w-full flex-grow"\r
|
|
59592
|
+
[class.border-red-300]="modelIndex === 0"\r
|
|
59593
|
+
[class.border-green-300]="modelIndex === 1"\r
|
|
59594
|
+
(change)="onModelDropdownItemClick(modelIndex, $event)"\r
|
|
59595
|
+
>\r
|
|
59596
|
+
@let options =\r
|
|
59597
|
+
[noneOption].concat(projectInfoService.allModels());\r
|
|
59598
|
+
@for (dropdownItem of options; track dropdownItem) {\r
|
|
59599
|
+
<option [selected]="dropdownItem === model">\r
|
|
59600
|
+
{{ dropdownItem }}\r
|
|
59601
|
+
</option>\r
|
|
59602
|
+
}\r
|
|
59603
|
+
</select>\r
|
|
59604
|
+
</li>\r
|
|
59605
|
+
}\r
|
|
59606
|
+
</ul>\r
|
|
59607
|
+
</div>\r
|
|
59608
|
+
</div>\r
|
|
59609
|
+
</div>\r
|
|
59610
|
+
</div>\r
|
|
59611
|
+
}\r
|
|
59612
|
+
`, styles: ["/* angular:styles/component:css;1fa15f7a0ae1b0259233282b2d49fb361834bf7fd726c58584c186240f9d8f04;/home/ksikka/lightning-pose-app/web_ui/src/app/viewer/viewer-page/viewer-page.component.html */\n:host {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-MRTIUFL2.css.map */\n"] }]
|
|
59559
59613
|
}], () => [], { sessionKey: [{
|
|
59560
59614
|
type: Input
|
|
59561
59615
|
}] });
|
|
59562
59616
|
})();
|
|
59563
59617
|
(() => {
|
|
59564
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerPageComponent, { className: "ViewerPageComponent", filePath: "src/app/viewer/viewer-page/viewer-page.component.ts", lineNumber:
|
|
59618
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerPageComponent, { className: "ViewerPageComponent", filePath: "src/app/viewer/viewer-page/viewer-page.component.ts", lineNumber: 35 });
|
|
59565
59619
|
})();
|
|
59566
59620
|
|
|
59567
59621
|
// src/app/labeler/labeler-page.component.ts
|
|
@@ -65999,36 +66053,356 @@ var appConfig = {
|
|
|
65999
66053
|
]
|
|
66000
66054
|
};
|
|
66001
66055
|
|
|
66056
|
+
// src/app/project-settings/project-settings.component.ts
|
|
66057
|
+
function ProjectSettingsComponent_Conditional_1_Template(rf, ctx) {
|
|
66058
|
+
if (rf & 1) {
|
|
66059
|
+
\u0275\u0275text(0, " Setup ");
|
|
66060
|
+
}
|
|
66061
|
+
}
|
|
66062
|
+
function ProjectSettingsComponent_Conditional_2_Template(rf, ctx) {
|
|
66063
|
+
if (rf & 1) {
|
|
66064
|
+
\u0275\u0275text(0, " Settings ");
|
|
66065
|
+
}
|
|
66066
|
+
}
|
|
66067
|
+
function ProjectSettingsComponent_Conditional_31_Template(rf, ctx) {
|
|
66068
|
+
if (rf & 1) {
|
|
66069
|
+
const _r1 = \u0275\u0275getCurrentView();
|
|
66070
|
+
\u0275\u0275elementStart(0, "button", 14);
|
|
66071
|
+
\u0275\u0275listener("click", function ProjectSettingsComponent_Conditional_31_Template_button_click_0_listener() {
|
|
66072
|
+
\u0275\u0275restoreView(_r1);
|
|
66073
|
+
const ctx_r1 = \u0275\u0275nextContext();
|
|
66074
|
+
return \u0275\u0275resetView(ctx_r1.done.emit(null));
|
|
66075
|
+
});
|
|
66076
|
+
\u0275\u0275text(1, " Cancel ");
|
|
66077
|
+
\u0275\u0275elementEnd();
|
|
66078
|
+
}
|
|
66079
|
+
}
|
|
66080
|
+
var ProjectSettingsComponent = class _ProjectSettingsComponent {
|
|
66081
|
+
done = output();
|
|
66082
|
+
setupMode = input(false);
|
|
66083
|
+
projectInfoForm;
|
|
66084
|
+
viewsInitialRows = signal(4);
|
|
66085
|
+
projectInfoService = inject(ProjectInfoService);
|
|
66086
|
+
fb = inject(FormBuilder);
|
|
66087
|
+
constructor() {
|
|
66088
|
+
this.projectInfoForm = this.fb.group({
|
|
66089
|
+
dataDir: [""],
|
|
66090
|
+
modelDir: [""],
|
|
66091
|
+
views: [""]
|
|
66092
|
+
});
|
|
66093
|
+
}
|
|
66094
|
+
ngOnInit() {
|
|
66095
|
+
const projectInfo = this.projectInfoService.projectInfo;
|
|
66096
|
+
if (projectInfo) {
|
|
66097
|
+
this.projectInfoForm.patchValue({
|
|
66098
|
+
dataDir: projectInfo.data_dir,
|
|
66099
|
+
modelDir: projectInfo.model_dir,
|
|
66100
|
+
views: projectInfo.views.join("\n")
|
|
66101
|
+
});
|
|
66102
|
+
this.viewsInitialRows.update((x) => Math.max(x, projectInfo.views.length));
|
|
66103
|
+
}
|
|
66104
|
+
}
|
|
66105
|
+
async onSaveClick() {
|
|
66106
|
+
const projectInfo = {};
|
|
66107
|
+
projectInfo.data_dir = this.projectInfoForm.get("dataDir")?.value ?? "";
|
|
66108
|
+
projectInfo.model_dir = this.projectInfoForm.get("modelDir")?.value ?? "";
|
|
66109
|
+
projectInfo.views = this.parseMultilineText(this.projectInfoForm.get("views")?.value ?? "");
|
|
66110
|
+
await this.projectInfoService.setProjectInfo(projectInfo);
|
|
66111
|
+
}
|
|
66112
|
+
parseMultilineText(text) {
|
|
66113
|
+
return text.split("\n").map((x) => x.trim()).filter((x) => Boolean(x));
|
|
66114
|
+
}
|
|
66115
|
+
get cameraViewPlaceholder() {
|
|
66116
|
+
return `view1
|
|
66117
|
+
view2
|
|
66118
|
+
...`;
|
|
66119
|
+
}
|
|
66120
|
+
static \u0275fac = function ProjectSettingsComponent_Factory(__ngFactoryType__) {
|
|
66121
|
+
return new (__ngFactoryType__ || _ProjectSettingsComponent)();
|
|
66122
|
+
};
|
|
66123
|
+
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _ProjectSettingsComponent, selectors: [["app-project-settings"]], inputs: { setupMode: [1, "setupMode"] }, outputs: { done: "done" }, decls: 34, vars: 6, consts: [[1, "text-xl", "font-bold"], [3, "formGroup"], [1, "fieldset", "my-6"], [1, "fieldset-legend", "text-md"], [1, "input", "w-full"], [1, "material-icons", "!text-sm", "opacity-50"], ["formControlName", "dataDir", "type", "text", "placeholder", "/", 1, "grow"], [1, "label"], [1, "fieldset", "my-4"], ["formControlName", "modelDir", "type", "text", "placeholder", "/", 1, "grow"], ["formControlName", "views", 1, "textarea", "w-full", 3, "rows", "placeholder"], [1, "modal-action"], [1, "btn", "btn-soft", "btn-secondary"], [1, "btn", "btn-soft", "btn-primary", 3, "click", "disabled"], [1, "btn", "btn-soft", "btn-secondary", 3, "click"]], template: function ProjectSettingsComponent_Template(rf, ctx) {
|
|
66124
|
+
if (rf & 1) {
|
|
66125
|
+
\u0275\u0275elementStart(0, "h3", 0);
|
|
66126
|
+
\u0275\u0275template(1, ProjectSettingsComponent_Conditional_1_Template, 1, 0)(2, ProjectSettingsComponent_Conditional_2_Template, 1, 0);
|
|
66127
|
+
\u0275\u0275elementEnd();
|
|
66128
|
+
\u0275\u0275elementStart(3, "form", 1)(4, "fieldset", 2)(5, "label", 3);
|
|
66129
|
+
\u0275\u0275text(6, "Data directory");
|
|
66130
|
+
\u0275\u0275elementEnd();
|
|
66131
|
+
\u0275\u0275elementStart(7, "div", 4)(8, "span", 5);
|
|
66132
|
+
\u0275\u0275text(9, "folder");
|
|
66133
|
+
\u0275\u0275elementEnd();
|
|
66134
|
+
\u0275\u0275element(10, "input", 6);
|
|
66135
|
+
\u0275\u0275elementEnd();
|
|
66136
|
+
\u0275\u0275elementStart(11, "p", 7);
|
|
66137
|
+
\u0275\u0275text(12, "Root of the data directory for the project.");
|
|
66138
|
+
\u0275\u0275elementEnd()();
|
|
66139
|
+
\u0275\u0275elementStart(13, "fieldset", 8)(14, "label", 3);
|
|
66140
|
+
\u0275\u0275text(15, "Model directory");
|
|
66141
|
+
\u0275\u0275elementEnd();
|
|
66142
|
+
\u0275\u0275elementStart(16, "div", 4)(17, "span", 5);
|
|
66143
|
+
\u0275\u0275text(18, "folder");
|
|
66144
|
+
\u0275\u0275elementEnd();
|
|
66145
|
+
\u0275\u0275element(19, "input", 9);
|
|
66146
|
+
\u0275\u0275elementEnd();
|
|
66147
|
+
\u0275\u0275elementStart(20, "p", 7);
|
|
66148
|
+
\u0275\u0275text(21, "Root of the model directory for the project.");
|
|
66149
|
+
\u0275\u0275elementEnd()();
|
|
66150
|
+
\u0275\u0275elementStart(22, "fieldset", 8)(23, "label", 3);
|
|
66151
|
+
\u0275\u0275text(24, "Camera Views");
|
|
66152
|
+
\u0275\u0275elementEnd();
|
|
66153
|
+
\u0275\u0275element(25, "textarea", 10);
|
|
66154
|
+
\u0275\u0275elementStart(26, "p", 7);
|
|
66155
|
+
\u0275\u0275text(27, " Names of views embedded in video filenames.");
|
|
66156
|
+
\u0275\u0275element(28, "br");
|
|
66157
|
+
\u0275\u0275text(29, " Used to group files into sessions. ");
|
|
66158
|
+
\u0275\u0275elementEnd()();
|
|
66159
|
+
\u0275\u0275elementStart(30, "div", 11);
|
|
66160
|
+
\u0275\u0275template(31, ProjectSettingsComponent_Conditional_31_Template, 2, 0, "button", 12);
|
|
66161
|
+
\u0275\u0275elementStart(32, "button", 13);
|
|
66162
|
+
\u0275\u0275listener("click", function ProjectSettingsComponent_Template_button_click_32_listener() {
|
|
66163
|
+
return ctx.onSaveClick();
|
|
66164
|
+
});
|
|
66165
|
+
\u0275\u0275text(33, " Save ");
|
|
66166
|
+
\u0275\u0275elementEnd()()();
|
|
66167
|
+
}
|
|
66168
|
+
if (rf & 2) {
|
|
66169
|
+
\u0275\u0275advance();
|
|
66170
|
+
\u0275\u0275conditional(ctx.setupMode() ? 1 : 2);
|
|
66171
|
+
\u0275\u0275advance(2);
|
|
66172
|
+
\u0275\u0275property("formGroup", ctx.projectInfoForm);
|
|
66173
|
+
\u0275\u0275advance(22);
|
|
66174
|
+
\u0275\u0275property("rows", ctx.viewsInitialRows())("placeholder", ctx.cameraViewPlaceholder);
|
|
66175
|
+
\u0275\u0275advance(6);
|
|
66176
|
+
\u0275\u0275conditional(!ctx.setupMode() ? 31 : -1);
|
|
66177
|
+
\u0275\u0275advance();
|
|
66178
|
+
\u0275\u0275property("disabled", !ctx.projectInfoForm.dirty);
|
|
66179
|
+
}
|
|
66180
|
+
}, dependencies: [ReactiveFormsModule, \u0275NgNoValidate, DefaultValueAccessor, NgControlStatus, NgControlStatusGroup, FormGroupDirective, FormControlName], styles: ["\n\n[_ngcontent-%COMP%]::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-5IRK7U7U.css.map */"], changeDetection: 0 });
|
|
66181
|
+
};
|
|
66182
|
+
(() => {
|
|
66183
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ProjectSettingsComponent, [{
|
|
66184
|
+
type: Component,
|
|
66185
|
+
args: [{ selector: "app-project-settings", imports: [ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `<style>
|
|
66186
|
+
::placeholder {
|
|
66187
|
+
/** bring back the default style from daisy. dont know why it's getting overridden. */
|
|
66188
|
+
color: color-mix(in oklch, currentColor 50%, #0000) !important;
|
|
66189
|
+
}
|
|
66190
|
+
</style>
|
|
66191
|
+
|
|
66192
|
+
<h3 class="text-xl font-bold">
|
|
66193
|
+
@if (setupMode()) {
|
|
66194
|
+
Setup
|
|
66195
|
+
} @else {
|
|
66196
|
+
Settings
|
|
66197
|
+
}
|
|
66198
|
+
</h3>
|
|
66199
|
+
|
|
66200
|
+
<form [formGroup]="projectInfoForm">
|
|
66201
|
+
<fieldset class="fieldset my-6">
|
|
66202
|
+
<!-- We like the fieldset / fieldset-legend styles from daisy, but
|
|
66203
|
+
we're certainly not using it as intended. Daisy inputs
|
|
66204
|
+
have no other good way of putting the label on top.
|
|
66205
|
+
TODO fix properly. -->
|
|
66206
|
+
<label class="fieldset-legend text-md">Data directory</label>
|
|
66207
|
+
<div class="input w-full">
|
|
66208
|
+
<span class="material-icons !text-sm opacity-50">folder</span>
|
|
66209
|
+
<input
|
|
66210
|
+
formControlName="dataDir"
|
|
66211
|
+
type="text"
|
|
66212
|
+
class="grow"
|
|
66213
|
+
placeholder="/"
|
|
66214
|
+
/>
|
|
66215
|
+
</div>
|
|
66216
|
+
<p class="label">Root of the data directory for the project.</p>
|
|
66217
|
+
</fieldset>
|
|
66218
|
+
|
|
66219
|
+
<fieldset class="fieldset my-4">
|
|
66220
|
+
<label class="fieldset-legend text-md">Model directory</label>
|
|
66221
|
+
|
|
66222
|
+
<div class="input w-full">
|
|
66223
|
+
<span class="material-icons !text-sm opacity-50">folder</span>
|
|
66224
|
+
<input
|
|
66225
|
+
formControlName="modelDir"
|
|
66226
|
+
type="text"
|
|
66227
|
+
class="grow"
|
|
66228
|
+
placeholder="/"
|
|
66229
|
+
/>
|
|
66230
|
+
</div>
|
|
66231
|
+
<p class="label">Root of the model directory for the project.</p>
|
|
66232
|
+
</fieldset>
|
|
66233
|
+
|
|
66234
|
+
<fieldset class="fieldset my-4">
|
|
66235
|
+
<label class="fieldset-legend text-md">Camera Views</label>
|
|
66236
|
+
|
|
66237
|
+
<textarea
|
|
66238
|
+
class="textarea w-full"
|
|
66239
|
+
[rows]="viewsInitialRows()"
|
|
66240
|
+
[placeholder]="cameraViewPlaceholder"
|
|
66241
|
+
formControlName="views"
|
|
66242
|
+
></textarea>
|
|
66243
|
+
<p class="label">
|
|
66244
|
+
Names of views embedded in video filenames.<br />
|
|
66245
|
+
Used to group files into sessions.
|
|
66246
|
+
</p>
|
|
66247
|
+
</fieldset>
|
|
66248
|
+
|
|
66249
|
+
<div class="modal-action">
|
|
66250
|
+
@if (!setupMode()) {
|
|
66251
|
+
<button class="btn btn-soft btn-secondary" (click)="done.emit(null)">
|
|
66252
|
+
Cancel
|
|
66253
|
+
</button>
|
|
66254
|
+
}
|
|
66255
|
+
<button
|
|
66256
|
+
class="btn btn-soft btn-primary"
|
|
66257
|
+
(click)="onSaveClick()"
|
|
66258
|
+
[disabled]="!projectInfoForm.dirty"
|
|
66259
|
+
>
|
|
66260
|
+
Save
|
|
66261
|
+
</button>
|
|
66262
|
+
</div>
|
|
66263
|
+
</form>
|
|
66264
|
+
`, styles: ["/* angular:styles/component:css;5c3484fa86afc1ad339d983c727d8801928706c064dd49a4001f42c8eac3d364;/home/ksikka/lightning-pose-app/web_ui/src/app/project-settings/project-settings.component.html */\n::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-5IRK7U7U.css.map */\n"] }]
|
|
66265
|
+
}], () => [], null);
|
|
66266
|
+
})();
|
|
66267
|
+
(() => {
|
|
66268
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ProjectSettingsComponent, { className: "ProjectSettingsComponent", filePath: "src/app/project-settings/project-settings.component.ts", lineNumber: 21 });
|
|
66269
|
+
})();
|
|
66270
|
+
|
|
66002
66271
|
// src/app/app.component.ts
|
|
66272
|
+
var _c08 = ["settingsDialog"];
|
|
66273
|
+
function AppComponent_Conditional_18_Template(rf, ctx) {
|
|
66274
|
+
if (rf & 1) {
|
|
66275
|
+
\u0275\u0275element(0, "router-outlet");
|
|
66276
|
+
}
|
|
66277
|
+
}
|
|
66278
|
+
function AppComponent_Conditional_19_Template(rf, ctx) {
|
|
66279
|
+
if (rf & 1) {
|
|
66280
|
+
\u0275\u0275element(0, "app-project-settings", 12);
|
|
66281
|
+
}
|
|
66282
|
+
if (rf & 2) {
|
|
66283
|
+
\u0275\u0275property("setupMode", true);
|
|
66284
|
+
}
|
|
66285
|
+
}
|
|
66286
|
+
function AppComponent_Conditional_22_Template(rf, ctx) {
|
|
66287
|
+
if (rf & 1) {
|
|
66288
|
+
const _r2 = \u0275\u0275getCurrentView();
|
|
66289
|
+
\u0275\u0275elementStart(0, "app-project-settings", 14);
|
|
66290
|
+
\u0275\u0275listener("done", function AppComponent_Conditional_22_Template_app_project_settings_done_0_listener() {
|
|
66291
|
+
\u0275\u0275restoreView(_r2);
|
|
66292
|
+
const ctx_r2 = \u0275\u0275nextContext();
|
|
66293
|
+
return \u0275\u0275resetView(ctx_r2.settingsDialogOpen.set(false));
|
|
66294
|
+
});
|
|
66295
|
+
\u0275\u0275elementEnd();
|
|
66296
|
+
\u0275\u0275elementStart(1, "div", 15)(2, "button", 16);
|
|
66297
|
+
\u0275\u0275listener("click", function AppComponent_Conditional_22_Template_button_click_2_listener() {
|
|
66298
|
+
\u0275\u0275restoreView(_r2);
|
|
66299
|
+
const ctx_r2 = \u0275\u0275nextContext();
|
|
66300
|
+
return \u0275\u0275resetView(ctx_r2.settingsDialogOpen.set(false));
|
|
66301
|
+
});
|
|
66302
|
+
\u0275\u0275text(3, "close");
|
|
66303
|
+
\u0275\u0275elementEnd()();
|
|
66304
|
+
}
|
|
66305
|
+
}
|
|
66003
66306
|
var AppComponent = class _AppComponent {
|
|
66307
|
+
projectInfoService = inject(ProjectInfoService);
|
|
66308
|
+
// Whether the required initial setup has been done.
|
|
66309
|
+
// (Setting data directory, model directory, views).
|
|
66310
|
+
projectInfoRequestCompleted = signal(false);
|
|
66311
|
+
hasBeenSetup = signal(false);
|
|
66312
|
+
settingsDialog = viewChild.required("settingsDialog");
|
|
66313
|
+
settingsDialogOpen = signal(false);
|
|
66314
|
+
async ngOnInit() {
|
|
66315
|
+
await this.projectInfoService.loadProjectInfo();
|
|
66316
|
+
this.hasBeenSetup.set(Boolean(this.projectInfoService.projectInfo));
|
|
66317
|
+
this.projectInfoRequestCompleted.set(true);
|
|
66318
|
+
}
|
|
66319
|
+
constructor() {
|
|
66320
|
+
effect(() => {
|
|
66321
|
+
if (this.settingsDialogOpen()) {
|
|
66322
|
+
this.openSettingsDialog();
|
|
66323
|
+
} else {
|
|
66324
|
+
this.closeSettingsDialog();
|
|
66325
|
+
}
|
|
66326
|
+
});
|
|
66327
|
+
}
|
|
66328
|
+
openSettingsDialog() {
|
|
66329
|
+
const elementRef = this.settingsDialog();
|
|
66330
|
+
elementRef.nativeElement.showModal();
|
|
66331
|
+
}
|
|
66332
|
+
closeSettingsDialog() {
|
|
66333
|
+
const elementRef = this.settingsDialog();
|
|
66334
|
+
elementRef.nativeElement.close();
|
|
66335
|
+
}
|
|
66004
66336
|
static \u0275fac = function AppComponent_Factory(__ngFactoryType__) {
|
|
66005
66337
|
return new (__ngFactoryType__ || _AppComponent)();
|
|
66006
66338
|
};
|
|
66007
|
-
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _AppComponent, selectors: [["app-root"]],
|
|
66339
|
+
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _AppComponent, selectors: [["app-root"]], viewQuery: function AppComponent_Query(rf, ctx) {
|
|
66008
66340
|
if (rf & 1) {
|
|
66009
|
-
\u0275\
|
|
66010
|
-
|
|
66011
|
-
|
|
66012
|
-
\u0275\
|
|
66013
|
-
|
|
66341
|
+
\u0275\u0275viewQuerySignal(ctx.settingsDialog, _c08, 5);
|
|
66342
|
+
}
|
|
66343
|
+
if (rf & 2) {
|
|
66344
|
+
\u0275\u0275queryAdvance();
|
|
66345
|
+
}
|
|
66346
|
+
}, decls: 23, vars: 4, consts: [["settingsDialog", ""], [1, "navbar", "bg-base-200", "shadow-lg"], [1, "navbar-start"], [1, "text-lg", "font-semibold"], [1, "navbar-center"], [1, "menu", "menu-horizontal", "p-0"], ["routerLink", "/viewer", "routerLinkActive", "menu-active", "ariaCurrentWhenActive", "page"], ["routerLink", "/labeler", "routerLinkActive", "menu-active", "ariaCurrentWhenActive", "page"], [1, "navbar-end"], ["tabindex", "0", 1, "btn", "btn-ghost", 3, "click", "keydown.enter"], [1, "material-icons"], [1, "flex-grow", "flex", "min-h-0"], [1, "w-lg", "mt-8", 3, "setupMode"], [1, "modal"], [1, "modal-box", 3, "done"], [1, "modal-backdrop"], [3, "click"]], template: function AppComponent_Template(rf, ctx) {
|
|
66347
|
+
if (rf & 1) {
|
|
66348
|
+
const _r1 = \u0275\u0275getCurrentView();
|
|
66349
|
+
\u0275\u0275elementStart(0, "header")(1, "nav", 1)(2, "div", 2)(3, "span", 3);
|
|
66350
|
+
\u0275\u0275text(4, "Lightning Pose");
|
|
66014
66351
|
\u0275\u0275elementEnd()();
|
|
66015
|
-
\u0275\u0275elementStart(7, "li")(8, "a",
|
|
66016
|
-
\u0275\u0275text(9, "
|
|
66352
|
+
\u0275\u0275elementStart(5, "div", 4)(6, "ul", 5)(7, "li")(8, "a", 6);
|
|
66353
|
+
\u0275\u0275text(9, "Viewer");
|
|
66354
|
+
\u0275\u0275elementEnd()();
|
|
66355
|
+
\u0275\u0275elementStart(10, "li")(11, "a", 7);
|
|
66356
|
+
\u0275\u0275text(12, "Labeler");
|
|
66017
66357
|
\u0275\u0275elementEnd()()()();
|
|
66018
|
-
\u0275\
|
|
66358
|
+
\u0275\u0275elementStart(13, "div", 8)(14, "button", 9);
|
|
66359
|
+
\u0275\u0275listener("click", function AppComponent_Template_button_click_14_listener() {
|
|
66360
|
+
\u0275\u0275restoreView(_r1);
|
|
66361
|
+
return \u0275\u0275resetView(ctx.settingsDialogOpen.set(true));
|
|
66362
|
+
})("keydown.enter", function AppComponent_Template_button_keydown_enter_14_listener() {
|
|
66363
|
+
\u0275\u0275restoreView(_r1);
|
|
66364
|
+
return \u0275\u0275resetView(ctx.settingsDialogOpen.set(true));
|
|
66365
|
+
});
|
|
66366
|
+
\u0275\u0275elementStart(15, "span", 10);
|
|
66367
|
+
\u0275\u0275text(16, "settings");
|
|
66368
|
+
\u0275\u0275elementEnd()()()()();
|
|
66369
|
+
\u0275\u0275elementStart(17, "main", 11);
|
|
66370
|
+
\u0275\u0275template(18, AppComponent_Conditional_18_Template, 1, 0, "router-outlet")(19, AppComponent_Conditional_19_Template, 1, 1, "app-project-settings", 12);
|
|
66371
|
+
\u0275\u0275elementEnd();
|
|
66372
|
+
\u0275\u0275elementStart(20, "dialog", 13, 0);
|
|
66373
|
+
\u0275\u0275template(22, AppComponent_Conditional_22_Template, 4, 0);
|
|
66374
|
+
\u0275\u0275elementEnd();
|
|
66375
|
+
}
|
|
66376
|
+
if (rf & 2) {
|
|
66377
|
+
\u0275\u0275advance(17);
|
|
66378
|
+
\u0275\u0275classProp("justify-center", !ctx.hasBeenSetup());
|
|
66379
|
+
\u0275\u0275advance();
|
|
66380
|
+
\u0275\u0275conditional(ctx.hasBeenSetup() ? 18 : 19);
|
|
66381
|
+
\u0275\u0275advance(4);
|
|
66382
|
+
\u0275\u0275conditional(ctx.settingsDialogOpen() ? 22 : -1);
|
|
66019
66383
|
}
|
|
66020
|
-
}, dependencies: [
|
|
66384
|
+
}, dependencies: [
|
|
66385
|
+
RouterOutlet,
|
|
66386
|
+
RouterLink,
|
|
66387
|
+
RouterLinkActive,
|
|
66388
|
+
ProjectSettingsComponent
|
|
66389
|
+
], styles: ["\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar[_ngcontent-%COMP%] {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UHVEDPZR.css.map */"], changeDetection: 0 });
|
|
66021
66390
|
};
|
|
66022
66391
|
(() => {
|
|
66023
66392
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(AppComponent, [{
|
|
66024
66393
|
type: Component,
|
|
66025
|
-
args: [{ selector: "app-root", imports: [
|
|
66026
|
-
|
|
66394
|
+
args: [{ selector: "app-root", imports: [
|
|
66395
|
+
RouterOutlet,
|
|
66396
|
+
RouterLink,
|
|
66397
|
+
RouterLinkActive,
|
|
66398
|
+
ProjectSettingsComponent
|
|
66399
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\n :host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n }\n .navbar {\n min-height: 3rem; /* reduce from default of 4rem */\n }\n</style>\n<header>\n <nav class="navbar bg-base-200 shadow-lg">\n <div class="navbar-start">\n <span class="text-lg font-semibold">Lightning Pose</span>\n </div>\n\n <div class="navbar-center">\n <ul class="menu menu-horizontal p-0">\n <li>\n <a\n routerLink="/viewer"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Viewer</a\n >\n </li>\n <li>\n <a\n routerLink="/labeler"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Labeler</a\n >\n </li>\n </ul>\n </div>\n\n <div class="navbar-end">\n <button\n class="btn btn-ghost"\n tabindex="0"\n (click)="settingsDialogOpen.set(true)"\n (keydown.enter)="settingsDialogOpen.set(true)"\n >\n <span class="material-icons">settings</span>\n </button>\n </div>\n </nav>\n</header>\n\n<!-- Set min-height: 0 to allow it to shrink if its content is too large.\n Default min-height for flex items is auto. -->\n<main class="flex-grow flex min-h-0" [class.justify-center]="!hasBeenSetup()">\n @if (hasBeenSetup()) {\n <router-outlet />\n } @else {\n <app-project-settings\n [setupMode]="true"\n class="w-lg mt-8"\n ></app-project-settings>\n }\n</main>\n\n<!-- Settings dialog -->\n<dialog #settingsDialog class="modal">\n @if (settingsDialogOpen()) {\n <app-project-settings\n class="modal-box"\n (done)="settingsDialogOpen.set(false)"\n ></app-project-settings>\n\n <!-- Makes the dialog close when clicked from outside. -->\n <div class="modal-backdrop">\n <button (click)="settingsDialogOpen.set(false)">close</button>\n </div>\n }\n</dialog>\n', styles: ["/* angular:styles/component:css;22d8514f1dd5b50f33b3fb93fdb69668f78eeb349bd672e238e3ac9acfbbda19;/home/ksikka/lightning-pose-app/web_ui/src/app/app.component.html */\n:host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UHVEDPZR.css.map */\n"] }]
|
|
66400
|
+
}], () => [], null);
|
|
66027
66401
|
})();
|
|
66028
66402
|
(() => {
|
|
66029
|
-
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(AppComponent, { className: "AppComponent", filePath: "src/app/app.component.ts", lineNumber:
|
|
66403
|
+
(typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(AppComponent, { className: "AppComponent", filePath: "src/app/app.component.ts", lineNumber: 27 });
|
|
66030
66404
|
})();
|
|
66031
66405
|
|
|
66032
66406
|
// src/main.ts
|
|
66033
66407
|
bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
|
|
66034
|
-
//# sourceMappingURL=main-
|
|
66408
|
+
//# sourceMappingURL=main-LJHMLKBL.js.map
|