All work on focus feature (still in progress)

This commit is contained in:
Matt McWilliams 2024-10-21 23:51:38 -04:00
parent e094372308
commit 58b9310128
18 changed files with 210 additions and 37 deletions

View File

@ -39,6 +39,7 @@ class Display {
this.create();
window.onresize = this.onResize.bind(this);
}
private create () {
this.canvas = this.parentElement.getElementsByTagName('canvas')[0];
this.ctx = this.canvas.getContext('2d');
@ -318,6 +319,9 @@ class Client {
case 'update' :
this.receiveUpdate(msg);
break;
case 'focus' :
this.receiveFocus(msg);
break;
default:
console.warn(`No command "${msg.cmd}"`);
break;
@ -437,6 +441,20 @@ class Client {
this.setUpdate(msg.state);
}
public sendFocus () {
console.log('send focus');
//this.disableClass('manualCtrl');
this.client.send(JSON.stringify({ cmd : 'focus' }));
}
private receiveFocus (msg : Message) {
this.display.updateImage();
}
/**
* HELPERS
**/
public fullscreen () {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();

View File

@ -1,5 +1,5 @@
import type { fdOutgoingPosition } from '../fd';
declare class Dimensions {
export declare class Dimensions {
width: number;
height: number;
constructor(width: number, height: number);
@ -18,4 +18,3 @@ export declare class Display {
getScreen(): Dimensions;
getSource(): Dimensions;
}
export type { Dimensions };

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Display = void 0;
exports.Display = exports.Dimensions = void 0;
class Dimensions {
constructor(width, height) {
this.width = width;
@ -10,6 +10,7 @@ class Dimensions {
return this.width / this.height;
}
}
exports.Dimensions = Dimensions;
class Display {
constructor(width, height) {
this.screen = new Dimensions(width, height);
@ -51,5 +52,5 @@ class Display {
}
}
exports.Display = Display;
module.exports = { Display };
module.exports = { Display, Dimensions };
//# sourceMappingURL=index.js.map

View File

@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":";;;AAEA,MAAM,UAAU;IAIf,YAAa,KAAc,EAAE,MAAe;QAC3C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAEM,QAAQ;QACd,OAAO,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;CACD;AAOD,MAAa,OAAO;IAQnB,YAAa,KAAc,EAAE,MAAe;QAC3C,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAG,CAAC,EAAE,CAAA;IAC9B,CAAC;IAEM,UAAU,CAAE,CAAU;QAC5B,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAEM,UAAU,CAAE,CAAU;QAC5B,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAEM,SAAS,CAAE,KAAc,EAAE,MAAe;QAChD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YACrD,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACzG,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YAClD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAAG,CAAC,EAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QACrD,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3G,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YAChD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAG,CAAC,EAAG,CAAC,EAAE,CAAC;QACrD,CAAC;IACF,CAAC;IAEM,aAAa;QACnB,OAAO;YACN,CAAC,EAAG,IAAI,CAAC,OAAO,CAAC,KAAK;YACtB,CAAC,EAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACvB,CAAC,EAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YACjB,CAAC,EAAG,IAAI,CAAC,MAAM,CAAC,CAAC;SACjB,CAAA;IACF,CAAC;IAEM,SAAS;QACf,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IAEM,SAAS;QACf,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;CACD;AAnDD,0BAmDC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,OAAO,EAAE,CAAC"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":";;;AAEA,MAAa,UAAU;IAItB,YAAa,KAAc,EAAE,MAAe;QAC3C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAEM,QAAQ;QACd,OAAO,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;CACD;AAZD,gCAYC;AAOD,MAAa,OAAO;IAQnB,YAAa,KAAc,EAAE,MAAe;QAC3C,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAG,CAAC,EAAE,CAAA;IAC9B,CAAC;IAEM,UAAU,CAAE,CAAU;QAC5B,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAEM,UAAU,CAAE,CAAU;QAC5B,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAEM,SAAS,CAAE,KAAc,EAAE,MAAe;QAChD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YACrD,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACzG,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YAClD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAAG,CAAC,EAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QACrD,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3G,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YAChD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAG,CAAC,EAAG,CAAC,EAAE,CAAC;QACrD,CAAC;IACF,CAAC;IAEM,aAAa;QACnB,OAAO;YACN,CAAC,EAAG,IAAI,CAAC,OAAO,CAAC,KAAK;YACtB,CAAC,EAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACvB,CAAC,EAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YACjB,CAAC,EAAG,IAAI,CAAC,MAAM,CAAC,CAAC;SACjB,CAAA;IACF,CAAC;IAEM,SAAS;QACf,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IAEM,SAAS;QACf,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;CACD;AAnDD,0BAmDC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,OAAO,EAAG,UAAU,EAAE,CAAC"}

46
dist/index.js vendored
View File

@ -35,6 +35,7 @@ const Handlebars = __importStar(require("handlebars"));
const ws_1 = require("ws");
const log_1 = require("./log");
const files_1 = require("./files");
const testimage_1 = require("./testimage");
const fd_1 = require("./fd");
const display_1 = require("./display");
const ffmpeg_1 = require("./ffmpeg");
@ -54,6 +55,7 @@ let image;
let camera;
let sequence;
let index;
let focusImage = null;
let port;
let wsPort;
let sequences;
@ -233,6 +235,9 @@ async function cmd(msg) {
case 'exposure':
exposureSet(msg.state.exposure);
break;
case 'focus':
await focus();
break;
default:
log.warn(`No matching command: ${msg.cmd}`);
}
@ -246,12 +251,15 @@ async function cameraClose() {
send({ cmd: 'close' });
}
function frameAdvance() {
focusImage = null;
sequence.frameAdvance();
}
function frameRewind() {
focusImage = null;
sequence.frameRewind();
}
function frameSet(frame) {
focusImage = null;
sequence.frameSet(frame);
}
function exposureSet(exposure) {
@ -285,6 +293,32 @@ async function send(msg) {
async function keepAlive() {
await send({ cmd: 'ping' });
}
async function focus() {
let pos;
let dims;
let state;
let filePath;
if (sequence.isLoaded()) {
state = sequence.getState();
pos = {
w: state.display.width,
h: state.display.height,
x: state.offset.x,
y: state.offset.y
};
}
else {
dims = display.getScreen();
pos = {
w: dims.width,
h: dims.height,
x: 0,
y: 0
};
}
focusImage = await testimage_1.TestImage.Focus(pos.w, pos.h);
send({ cmd: 'focus' });
}
app.get('/', async (req, res, next) => {
const sequencesArr = await files_1.Files.enumerateSequences(sequences);
//const videosArr : VideoObject[] = await Files.enumerateVideos(videos);
@ -296,7 +330,17 @@ app.get('/:width/:height/image.jpg', async (req, res, next) => {
let current = sequence.getCurrent();
let width = parseInt(req.params.width);
let height = parseInt(req.params.height);
if (current !== null) {
if (focusImage !== null) {
try {
data = await image.thumbnail(focusImage, width, height);
log.info(`Image: ${current.path} - ${width},${height}`);
}
catch (err) {
log.error('Error getting thumbnail of ${current}', err);
return next(new Error('Error getting thumbnail'));
}
}
else if (current !== null) {
try {
data = await image.thumbnail(current.path, width, height);
log.info(`Image: ${current.path} - ${width},${height}`);

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -29,6 +29,7 @@ export declare class Sequence {
start(): void;
stop(): void;
isRunning(): boolean;
isLoaded(): boolean;
private run;
load(seq: SequenceObject): Promise<void>;
updateClientsOnLoad(): void;

View File

@ -111,6 +111,9 @@ class Sequence {
isRunning() {
return this.running;
}
isLoaded() {
return this.current !== null;
}
async run() {
//update running
for (let i = this.frame; i < this.images.length; i++) {

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
import type { fdOutgoingPosition } from '../fd';
class Dimensions {
export class Dimensions {
public width : number;
public height : number;
@ -72,5 +72,4 @@ export class Display {
}
}
module.exports = { Display };
export type { Dimensions };
module.exports = { Display, Dimensions };

View File

@ -22,7 +22,8 @@ import { Shell } from './shell';
import { delay } from './delay';
import { TestImage } from './testimage';
import { FD } from './fd';
import { Display } from './display';
import type { fdOutgoingPosition } from './fd';
import { Display, Dimensions } from './display';
import { FFMPEG } from './ffmpeg';
import { FFPROBE } from './ffprobe';
import { Camera } from './camera';
@ -41,6 +42,7 @@ let image : Image;
let camera : Camera;
let sequence : Sequence;
let index : HandlebarsTemplateDelegate<any>;
let focusImage : string = null;
let port : number;
let wsPort : number;
@ -221,6 +223,9 @@ async function cmd (msg : Message) {
case 'exposure' :
exposureSet(msg.state.exposure);
break;
case 'focus' :
await focus();
break;
default :
log.warn(`No matching command: ${msg.cmd}`);
}
@ -237,14 +242,17 @@ async function cameraClose () {
}
function frameAdvance () {
focusImage = null;
sequence.frameAdvance();
}
function frameRewind () {
focusImage = null;
sequence.frameRewind();
}
function frameSet (frame : number) {
focusImage = null;
sequence.frameSet(frame);
}
@ -285,6 +293,32 @@ async function keepAlive () {
await send({ cmd : 'ping' });
}
async function focus () {
let pos : fdOutgoingPosition;
let dims : Dimensions;
let state : State;
let filePath : string;
if (sequence.isLoaded()) {
state = sequence.getState();
pos = {
w : state.display.width,
h : state.display.height,
x : state.offset.x,
y : state.offset.y
}
} else {
dims = display.getScreen();
pos = {
w : dims.width,
h : dims.height,
x : 0,
y : 0
}
}
focusImage = await TestImage.Focus(pos.w, pos.h);
send({ cmd : 'focus' });
}
app.get('/', async (req : Request, res : Response, next : NextFunction) => {
const sequencesArr : SequenceObject[] = await Files.enumerateSequences(sequences);
//const videosArr : VideoObject[] = await Files.enumerateVideos(videos);
@ -297,12 +331,20 @@ app.get('/:width/:height/image.jpg', async (req : Request, res : Response, next
let current : ImageObject = sequence.getCurrent();
let width : number = parseInt(req.params.width);
let height : number = parseInt(req.params.height);
if (current !== null) {
if (focusImage !== null) {
try {
data = await image.thumbnail(focusImage, width, height);
log.info(`Image: ${current.path} - ${width},${height}`);
} catch (err) {
log.error(`Error getting thumbnail of ${current}`, err);
return next(new Error('Error getting thumbnail'));
}
} else if (current !== null) {
try {
data = await image.thumbnail(current.path, width, height);
log.info(`Image: ${current.path} - ${width},${height}`);
} catch (err) {
log.error('Error getting thumbnail of ${current}', err);
log.error(`Error getting thumbnail of ${current}`, err);
return next(new Error('Error getting thumbnail'));
}
} else {

View File

@ -119,7 +119,6 @@ export class Sequence {
private running : boolean = false;
private paused : boolean = false;
private progress : number = 0;
private frame : number = 0;
private frames : number = 0;
@ -151,10 +150,14 @@ export class Sequence {
}
}
public isRunning () {
public isRunning () : boolean {
return this.running;
}
public isLoaded () : boolean {
return this.current !== null;
}
private async run () {
//update running
for (let i = this.frame; i < this.images.length; i++) {

View File

@ -25,7 +25,7 @@ export class TestImage {
return filePath;
}
public static async Focus (width : number, height : number, count : number = 20) {
public static async Focus (width : number, height : number, count : number = 20) : Promise<string> {
const id : string = uuid();
const filePath : string = join(tmpdir(), `frame-${id}.png`);
const img : PImage.Bitmap = PImage.make(width, height);

View File

@ -45,6 +45,30 @@ html, body{
max-height: 50vh;
}
#displayAdjust {
min-height: 112px;
}
#displayAdjustForm{
span {
display: inline-block;
vertical-align: top;
div{
min-height : 23px;
max-height: 23px;
text-align: center;
line-height: 23px;
}
}
}
#statisticsForm div div label {
width: 80px;
text-align: right;
display: inline-block;
padding-right: 3px;
}
.half{
box-sizing: border-box;
flex: 0 0 50%;
@ -81,13 +105,6 @@ fieldset.inline input.medium {
max-width: 70px;
}
label {
width: 80px;
text-align: right;
display: inline-block;
padding-right: 3px;
}
progress {
width: 97vw;
background-color: silver;

View File

@ -79,6 +79,8 @@ declare class Client {
sendStop(): void;
sendExposure(): void;
private receiveUpdate;
sendFocus(): void;
private receiveFocus;
fullscreen(): void;
exitFullscreen(): void;
private active;

View File

@ -263,6 +263,9 @@ class Client {
case 'update':
this.receiveUpdate(msg);
break;
case 'focus':
this.receiveFocus(msg);
break;
default:
console.warn(`No command "${msg.cmd}"`);
break;
@ -363,6 +366,13 @@ class Client {
receiveUpdate(msg) {
this.setUpdate(msg.state);
}
sendFocus() {
console.log('send focus');
this.client.send(JSON.stringify({ cmd: 'focus' }));
}
receiveFocus(msg) {
this.display.updateImage();
}
fullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,7 @@
<div>
<ul class="tree-view" id="display"><canvas></canvas></ul>
<div class="flex">
<fieldset id="screenResolution" class="inline third">
<fieldset id="screenResolution" class="inline quarter">
<legend>Screen</legend>
<span class="field-row">
<label for="width">Width</label>
@ -32,7 +32,7 @@
<input id="height" type="text" value="{{height}}" readonly />
</span>
</fieldset>
<fieldset id="sourceResolution" class="inline third">
<fieldset id="sourceResolution" class="inline quarter">
<legend>Source</legend>
<span class="field-row">
<label for="sourceWidth">Width</label>
@ -43,7 +43,7 @@
<input id="sourceHeight" type="text" value="0" readonly />
</span>
</fieldset>
<fieldset id="displayResolution" class="inline third">
<fieldset id="displayResolution" class="inline quarter">
<legend>Display</legend>
<span class="field-row">
<label for="displayWidth">Width</label>
@ -54,6 +54,17 @@
<input id="displayHeight" type="text" value="0" readonly />
</span>
</fieldset>
<fieldset id="offsetPosition" class="inline quarter">
<legend>Offset</legend>
<span class="field-row">
<label for="offsetLeft">Left</label>
<input id="offsetLeft" type="text" value="0" readonly />
</span>
<span class="field-row">
<label for="offsetTop">Height</label>
<input id="offsetTop" type="text" value="0" readonly />
</span>
</fieldset>
</div>
</div>
<!--
@ -108,7 +119,7 @@
<form id="manualCtrlForm" onsubmit="return false;" >
<button id="open" class="manualCtrl" onclick="client.sendCameraOpen()">Open</button>
<button id="close" class="manualCtrl" onclick="client.sendCameraClose()">Close</button>
<button id="focus" class="manualCtrl" onclick="">Focus</button>
<button id="focus" class="manualCtrl" onclick="client.sendFocus();">Focus</button>
<button id="framing" class="manualCtrl" onclick="">Framing</button>
</form>
</fieldset>
@ -118,14 +129,37 @@
<fieldset id="displayAdjust" class="inline half">
<legend>Display Adjust</legend>
<form id="displayAdjustForm" onsubmit="return false;">
<button class="small manualCtrl" id="offsetXPlus" disabled>X +</button>
<button class="small manualCtrl" id="offsetXMinus" disabled>X -</button>
<button class="small manualCtrl" id="offsetYPlus" disabled>Y +</button>
<span>
<div></div>
<div>
<button class="small manualCtrl" id="offsetXMinus" disabled><</button>
</div>
<div></div>
</span>
<span>
<div>
<button class="small manualCtrl" id="offsetYPlus" disabled>^</button>
</div>
<div>
Position
</div>
<div>
<button class="small manualCtrl" id="offsetYMinus" disabled>Y -</button>
</div>
</span>
<span>
<div></div>
<div>
<button class="small manualCtrl" id="offsetXPlus" disabled>></button>
</div>
<div></div>
</span>
<span>
<button class="small manualCtrl" id="widthPlus" disabled>W +</button>
<button class="small manualCtrl" id="widthMinus" disabled>W -</button>
<button class="small manualCtrl" id="heightPlus" disabled>H +</button>
<button class="small manualCtrl" id="heightMinus" disabled>H -</button>
</span>
</form>
</fieldset>
</div>