Loading index.html +1 −1 Changes for index.html: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ </head> <body v-scope> <div class="body-content" :style="{cursor: cursor}"> <h1 class="title" title="Image Viewer with Online Interactive Relighting">{{title}}</h1> <h1 class="title" title="Image Viewer with Online Interactive Relighting" v-text="title"></h1> <header class="navbar" v-cloak> <div> Loading src/app.ts +45 −30 Changes for src/app.ts: 45 added lines, 30 removed lines. Original line number Diff line number Diff line Loading @@ -801,14 +801,14 @@ export class App { * @param qy vertical coordinate of the quadrant * @param value position to set the given quadrant to * @param prev original state of the window * @param _centered to resize the window around its center * @param centered to resize the window around its center * @param keepRatio whether aspect ratio should be kept unmodified by the operation */ private setExportWindowQuadrant( [qx, qy]: [0 | 1 | 2, 0 | 1 | 2], value: vec2, prev: Rect, _centered: boolean, centered: boolean, keepRatio: boolean, ) { if (!this.source) return; Loading @@ -828,43 +828,41 @@ export class App { } else { // Switch from offset/size to start/end coordinates let xyMin = [xy[0], xy[1]] as [number, number], xyMax = [xy[0] + wh[0], xy[1] + wh[1]] as [number, number]; if (qx === 1 || qy === 1) { // Middle column or row const ax = qy === 1 ? 0 : 1; if (qx === 0 || qy === 0) { xyMax = [xy[0] + wh[0], xy[1] + wh[1]] as [number, number], xyMid = [xy[0] + wh[0] / 2, xy[1] + wh[1] / 2]; const setMinMax = (ax: number) => { const lb = (!ax && !qx) || (ax && !qy), minFact = lb ? 1 : centered ? -1 : 0, maxFact = !lb ? 1 : centered ? -1 : 0; if (minFact) { // Left or bottom xyMin[ax] = clamp(Math.round(xyMin[ax] + diff[ax]), 0, xyMax[ax] - 1); } else { xyMin[ax] = Math.floor( clamp(xyMin[ax] + diff[ax] * minFact, 0, (centered ? xyMid[ax] : xyMax[ax]) - 1), ); } if (maxFact) { // Right or top xyMax[ax] = clamp(Math.round(xyMax[ax] + diff[ax]), xyMin[ax] + 1, total[ax]); xyMax[ax] = Math.ceil( clamp(xyMax[ax] + diff[ax] * maxFact, (centered ? xyMid[ax] : xyMin[ax]) + 1, total[ax]), ); } }; if (qx === 1 || qy === 1) { // Side setMinMax(qy === 1 ? 0 : 1); if (keepRatio) { const newSize = wh[1 - ax] * (xyMax[ax] - xyMin[ax]) / wh[ax]; xyMin[1 - ax] += (wh[1 - ax] - newSize) / 2; xyMax[1 - ax] = xyMin[1 - ax] + newSize; const newSize = Math.max(1, (wh[1 - (qy === 1 ? 0 : 1)] * (xyMax[qy === 1 ? 0 : 1] - xyMin[qy === 1 ? 0 : 1])) / wh[qy === 1 ? 0 : 1]); xyMin[1 - (qy === 1 ? 0 : 1)] += Math.floor((wh[1 - (qy === 1 ? 0 : 1)] - newSize) / 2); xyMax[1 - (qy === 1 ? 0 : 1)] = xyMin[1 - (qy === 1 ? 0 : 1)] + Math.ceil(newSize); } } else { // Corner if (keepRatio) { let diagonal = [ prev.w * (1 - qx), prev.h * (1 - qy), ] as const; vec2.scale( diff, diagonal, vec2.dot(diff, diagonal) / Math.pow(vec2.len(diagonal), 2), ); let diagonal = [prev.w * (1 - qx), prev.h * (1 - qy)] as const; vec2.scale(diff, diagonal, vec2.dot(diff, diagonal) / Math.pow(vec2.len(diagonal), 2)); } for (const ax of [0, 1] as const) { if ([qx, qy][ax] === 0) { // Left or bottom xyMin[ax] = clamp(Math.round(xyMin[ax] + diff[ax]), 0, xyMax[ax] - 1); } else { // Right or top xyMax[ax] = clamp(Math.round(xyMax[ax] + diff[ax]), xyMin[ax] + 1, total[ax]); } setMinMax(ax); } } params.area = Rect.fromCorners(xyMin, xyMax); Loading @@ -883,6 +881,7 @@ export class App { } private bindPointerEvents() { let lastMoveEvent: PointerEvent | null = null; const onPointerEvent = (ev: PointerEvent | WheelEvent) => { const viewport = this.state.viewport!; const pointerState = this.rx.pointerState; Loading Loading @@ -958,7 +957,6 @@ export class App { pos[1] >= exPar.innerGrab.y ? (pos[1] >= exPar.innerGrab.y2 ? 2 : 1) : 0, ]; }; if (ev instanceof PointerEvent && ev.type === 'pointerdown') { if (pointerState.dragging || !ev.isPrimary || ev.button !== 0) return; if (!highlight()) { Loading @@ -982,6 +980,7 @@ export class App { } } else if (ev instanceof PointerEvent && (ev.type === 'pointerup' || ev.type === 'pointerleave')) { if (!pointerState.dragging || !ev.isPrimary || ev.button !== 0) return; lastMoveEvent = null; doUpdate(); pointerState.overLightball = lightball; pointerState.dragging = false; Loading @@ -991,6 +990,7 @@ export class App { window.removeEventListener('wheel', onPointerEvent); } else if (ev instanceof PointerEvent && ev.type === 'pointermove') if (pointerState.dragging) { lastMoveEvent = ev; doUpdate(); } else { highlight(); Loading @@ -1017,6 +1017,19 @@ export class App { ev.stopPropagation(); }; const keyUpDown = (ev: KeyboardEvent) => { if (lastMoveEvent && (ev.key === 'Control' || ev.key === 'Shift')) { ev.preventDefault(); ev.stopPropagation(); let data: any = {}; for (const key in lastMoveEvent) data[key] = (lastMoveEvent as any)[key]; data.shiftKey = ev.shiftKey; data.ctrlKey = ev.ctrlKey; onPointerEvent(new PointerEvent('pointermove', data)); } }; this.gls.relight.canvas.addEventListener('pointerdown', onPointerEvent); this.gls.relight.canvas.addEventListener('wheel', onPointerEvent, { passive: false }); this.gls.lightball.canvas.addEventListener('pointerdown', onPointerEvent); Loading @@ -1024,5 +1037,7 @@ export class App { window.addEventListener('pointerleave', onPointerEvent); window.addEventListener('pointerup', onPointerEvent); window.addEventListener('pointermove', onPointerEvent); window.addEventListener('keyup', keyUpDown); window.addEventListener('keydown', keyUpDown); } } Loading
index.html +1 −1 Changes for index.html: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ </head> <body v-scope> <div class="body-content" :style="{cursor: cursor}"> <h1 class="title" title="Image Viewer with Online Interactive Relighting">{{title}}</h1> <h1 class="title" title="Image Viewer with Online Interactive Relighting" v-text="title"></h1> <header class="navbar" v-cloak> <div> Loading
src/app.ts +45 −30 Changes for src/app.ts: 45 added lines, 30 removed lines. Original line number Diff line number Diff line Loading @@ -801,14 +801,14 @@ export class App { * @param qy vertical coordinate of the quadrant * @param value position to set the given quadrant to * @param prev original state of the window * @param _centered to resize the window around its center * @param centered to resize the window around its center * @param keepRatio whether aspect ratio should be kept unmodified by the operation */ private setExportWindowQuadrant( [qx, qy]: [0 | 1 | 2, 0 | 1 | 2], value: vec2, prev: Rect, _centered: boolean, centered: boolean, keepRatio: boolean, ) { if (!this.source) return; Loading @@ -828,43 +828,41 @@ export class App { } else { // Switch from offset/size to start/end coordinates let xyMin = [xy[0], xy[1]] as [number, number], xyMax = [xy[0] + wh[0], xy[1] + wh[1]] as [number, number]; if (qx === 1 || qy === 1) { // Middle column or row const ax = qy === 1 ? 0 : 1; if (qx === 0 || qy === 0) { xyMax = [xy[0] + wh[0], xy[1] + wh[1]] as [number, number], xyMid = [xy[0] + wh[0] / 2, xy[1] + wh[1] / 2]; const setMinMax = (ax: number) => { const lb = (!ax && !qx) || (ax && !qy), minFact = lb ? 1 : centered ? -1 : 0, maxFact = !lb ? 1 : centered ? -1 : 0; if (minFact) { // Left or bottom xyMin[ax] = clamp(Math.round(xyMin[ax] + diff[ax]), 0, xyMax[ax] - 1); } else { xyMin[ax] = Math.floor( clamp(xyMin[ax] + diff[ax] * minFact, 0, (centered ? xyMid[ax] : xyMax[ax]) - 1), ); } if (maxFact) { // Right or top xyMax[ax] = clamp(Math.round(xyMax[ax] + diff[ax]), xyMin[ax] + 1, total[ax]); xyMax[ax] = Math.ceil( clamp(xyMax[ax] + diff[ax] * maxFact, (centered ? xyMid[ax] : xyMin[ax]) + 1, total[ax]), ); } }; if (qx === 1 || qy === 1) { // Side setMinMax(qy === 1 ? 0 : 1); if (keepRatio) { const newSize = wh[1 - ax] * (xyMax[ax] - xyMin[ax]) / wh[ax]; xyMin[1 - ax] += (wh[1 - ax] - newSize) / 2; xyMax[1 - ax] = xyMin[1 - ax] + newSize; const newSize = Math.max(1, (wh[1 - (qy === 1 ? 0 : 1)] * (xyMax[qy === 1 ? 0 : 1] - xyMin[qy === 1 ? 0 : 1])) / wh[qy === 1 ? 0 : 1]); xyMin[1 - (qy === 1 ? 0 : 1)] += Math.floor((wh[1 - (qy === 1 ? 0 : 1)] - newSize) / 2); xyMax[1 - (qy === 1 ? 0 : 1)] = xyMin[1 - (qy === 1 ? 0 : 1)] + Math.ceil(newSize); } } else { // Corner if (keepRatio) { let diagonal = [ prev.w * (1 - qx), prev.h * (1 - qy), ] as const; vec2.scale( diff, diagonal, vec2.dot(diff, diagonal) / Math.pow(vec2.len(diagonal), 2), ); let diagonal = [prev.w * (1 - qx), prev.h * (1 - qy)] as const; vec2.scale(diff, diagonal, vec2.dot(diff, diagonal) / Math.pow(vec2.len(diagonal), 2)); } for (const ax of [0, 1] as const) { if ([qx, qy][ax] === 0) { // Left or bottom xyMin[ax] = clamp(Math.round(xyMin[ax] + diff[ax]), 0, xyMax[ax] - 1); } else { // Right or top xyMax[ax] = clamp(Math.round(xyMax[ax] + diff[ax]), xyMin[ax] + 1, total[ax]); } setMinMax(ax); } } params.area = Rect.fromCorners(xyMin, xyMax); Loading @@ -883,6 +881,7 @@ export class App { } private bindPointerEvents() { let lastMoveEvent: PointerEvent | null = null; const onPointerEvent = (ev: PointerEvent | WheelEvent) => { const viewport = this.state.viewport!; const pointerState = this.rx.pointerState; Loading Loading @@ -958,7 +957,6 @@ export class App { pos[1] >= exPar.innerGrab.y ? (pos[1] >= exPar.innerGrab.y2 ? 2 : 1) : 0, ]; }; if (ev instanceof PointerEvent && ev.type === 'pointerdown') { if (pointerState.dragging || !ev.isPrimary || ev.button !== 0) return; if (!highlight()) { Loading @@ -982,6 +980,7 @@ export class App { } } else if (ev instanceof PointerEvent && (ev.type === 'pointerup' || ev.type === 'pointerleave')) { if (!pointerState.dragging || !ev.isPrimary || ev.button !== 0) return; lastMoveEvent = null; doUpdate(); pointerState.overLightball = lightball; pointerState.dragging = false; Loading @@ -991,6 +990,7 @@ export class App { window.removeEventListener('wheel', onPointerEvent); } else if (ev instanceof PointerEvent && ev.type === 'pointermove') if (pointerState.dragging) { lastMoveEvent = ev; doUpdate(); } else { highlight(); Loading @@ -1017,6 +1017,19 @@ export class App { ev.stopPropagation(); }; const keyUpDown = (ev: KeyboardEvent) => { if (lastMoveEvent && (ev.key === 'Control' || ev.key === 'Shift')) { ev.preventDefault(); ev.stopPropagation(); let data: any = {}; for (const key in lastMoveEvent) data[key] = (lastMoveEvent as any)[key]; data.shiftKey = ev.shiftKey; data.ctrlKey = ev.ctrlKey; onPointerEvent(new PointerEvent('pointermove', data)); } }; this.gls.relight.canvas.addEventListener('pointerdown', onPointerEvent); this.gls.relight.canvas.addEventListener('wheel', onPointerEvent, { passive: false }); this.gls.lightball.canvas.addEventListener('pointerdown', onPointerEvent); Loading @@ -1024,5 +1037,7 @@ export class App { window.addEventListener('pointerleave', onPointerEvent); window.addEventListener('pointerup', onPointerEvent); window.addEventListener('pointermove', onPointerEvent); window.addEventListener('keyup', keyUpDown); window.addEventListener('keydown', keyUpDown); } }