Skip to content

Commit

Permalink
Merge pull request CapSoftware#206 from CapSoftware/manual-zoom
Browse files Browse the repository at this point in the history
manual zoom
  • Loading branch information
Brendonovich authored Dec 13, 2024
2 parents 5a9f72a + 630fe1a commit 6c2a686
Show file tree
Hide file tree
Showing 18 changed files with 485 additions and 151 deletions.
24 changes: 24 additions & 0 deletions Cargo.lock

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

58 changes: 29 additions & 29 deletions apps/desktop/src-tauri/src/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,35 +311,35 @@ fn generate_zoom_segments_from_clicks(
const ZOOM_SEGMENT_AFTER_CLICK_PADDING: f64 = 1.5;

// single-segment only
for click in &recording.cursor_data.clicks {
let time = click.process_time_ms / 1000.0;

if segments.last().is_none() {
segments.push(ZoomSegment {
start: (click.process_time_ms / 1000.0 - (ZOOM_DURATION + 0.2)).max(0.0),
end: click.process_time_ms / 1000.0 + ZOOM_SEGMENT_AFTER_CLICK_PADDING,
amount: 2.0,
});
} else {
let last_segment = segments.last_mut().unwrap();

if click.down {
if last_segment.end > time {
last_segment.end =
(time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration());
} else if time < max_duration - ZOOM_DURATION {
segments.push(ZoomSegment {
start: (time - ZOOM_DURATION).max(0.0),
end: time + ZOOM_SEGMENT_AFTER_CLICK_PADDING,
amount: 2.0,
});
}
} else {
last_segment.end =
(time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration());
}
}
}
// for click in &recording.cursor_data.clicks {
// let time = click.process_time_ms / 1000.0;

// if segments.last().is_none() {
// segments.push(ZoomSegment {
// start: (click.process_time_ms / 1000.0 - (ZOOM_DURATION + 0.2)).max(0.0),
// end: click.process_time_ms / 1000.0 + ZOOM_SEGMENT_AFTER_CLICK_PADDING,
// amount: 2.0,
// });
// } else {
// let last_segment = segments.last_mut().unwrap();

// if click.down {
// if last_segment.end > time {
// last_segment.end =
// (time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration());
// } else if time < max_duration - ZOOM_DURATION {
// segments.push(ZoomSegment {
// start: (time - ZOOM_DURATION).max(0.0),
// end: time + ZOOM_SEGMENT_AFTER_CLICK_PADDING,
// amount: 2.0,
// });
// }
// } else {
// last_segment.end =
// (time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration());
// }
// }
// }

segments
}
Expand Down
15 changes: 9 additions & 6 deletions apps/desktop/src/routes/camera.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ export default function () {
{ name: "cameraWindowState" }
);

const [latestFrame, setLatestFrame] = createLazySignal<ImageData | null>();
const [latestFrame, setLatestFrame] = createLazySignal<{
width: number;
data: ImageData;
} | null>();
const [isLoading, setIsLoading] = createSignal(true);
const [error, setError] = createSignal<string | null>(null);

Expand All @@ -56,7 +59,7 @@ export default function () {
(imageData) => {
setLatestFrame(imageData);
const ctx = cameraCanvasRef?.getContext("2d");
ctx?.putImageData(imageData, 0, 0);
ctx?.putImageData(imageData.data, 0, 0);
setIsLoading(false);
}
);
Expand All @@ -81,7 +84,7 @@ export default function () {
(imageData) => {
setLatestFrame(imageData);
const ctx = cameraCanvasRef?.getContext("2d");
ctx?.putImageData(imageData, 0, 0);
ctx?.putImageData(imageData.data, 0, 0);
setIsLoading(false);
}
);
Expand Down Expand Up @@ -196,7 +199,7 @@ export default function () {
{(latestFrame) => {
const style = () => {
const aspectRatio =
latestFrame().width / latestFrame().height;
latestFrame().data.width / latestFrame().data.height;

const windowWidth = windowSize.latest?.size ?? 0;

Expand Down Expand Up @@ -232,8 +235,8 @@ export default function () {
data-tauri-drag-region
class={cx("absolute")}
style={style()}
width={latestFrame().width}
height={latestFrame().height}
width={latestFrame().data.width}
height={latestFrame().data.height}
ref={cameraCanvasRef!}
/>
);
Expand Down
84 changes: 83 additions & 1 deletion apps/desktop/src/routes/editor/ConfigSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ export function ConfigSidebar() {
return (
<div
data-visible={state.timelineSelection?.type === "zoom"}
class="absolute inset-0 p-[0.75rem] text-[0.875rem] space-y-4 bg-gray-50 z-50 animate-in slide-in-from-bottom-2 fade-in"
class="absolute inset-0 p-[0.75rem] text-[0.875rem] space-y-6 bg-gray-50 z-50 animate-in slide-in-from-bottom-2 fade-in"
>
<div class="flex flex-row justify-between items-center">
<div class="flex items-center gap-2">
Expand All @@ -638,6 +638,7 @@ export function ConfigSidebar() {
</EditorButton>
</div>
<EditorButton
variant="danger"
onClick={() => {
const index = value().selection.index;

Expand Down Expand Up @@ -678,6 +679,87 @@ export function ConfigSidebar() {
step={0.001}
/>
</Field>
<Field name="Zoom Mode" icon={<IconCapSettings />}>
<KTabs class="space-y-6">
<KTabs.List class="flex flex-row items-center rounded-[0.5rem] relative border">
<KTabs.Trigger
value="auto"
class="flex-1 text-gray-400 py-1 z-10 ui-selected:text-gray-500 peer outline-none transition-colors duration-100"
// onClick={() => setSelectedTab(item.id)}
disabled
>
Auto
</KTabs.Trigger>
<KTabs.Trigger
value="manual"
class="flex-1 text-gray-400 py-1 z-10 ui-selected:text-gray-500 peer outline-none transition-colors duration-100"
// onClick={() => setSelectedTab(item.id)}
>
Manual
</KTabs.Trigger>
<KTabs.Indicator class="absolute flex p-px inset-0 transition-transform peer-focus-visible:outline outline-2 outline-blue-300 outline-offset-2 rounded-[0.6rem] overflow-hidden">
<div class="bg-gray-100 flex-1" />
</KTabs.Indicator>
</KTabs.List>
<KTabs.Content value="manual">
<Show
when={(() => {
const m = value().segment.mode;
if (m === "auto") return;
return m.manual;
})()}
>
{(mode) => (
<div class="w-full h-52 bg-gray-100 rounded-xl p-1">
<div
class="w-full h-full bg-blue-400 rounded-lg relative"
onMouseMove={(e) => {
if (e.buttons === 1) {
const bounds =
e.currentTarget.getBoundingClientRect();

setProject(
"timeline",
"zoomSegments",
value().selection.index,
"mode",
"manual",
{
x: Math.max(
Math.min(
(e.clientX - bounds.left) /
bounds.width,
1
),
0
),
y: Math.max(
Math.min(
(e.clientY - bounds.top) /
bounds.height,
1
),
0
),
}
);
}
}}
>
<div
class="absolute w-6 h-6 rounded-full bg-gray-50 border border-gray-400 -translate-x-1/2 -translate-y-1/2"
style={{
left: `${mode().x * 100}%`,
top: `${mode().y * 100}%`,
}}
/>
</div>
</div>
)}
</Show>
</KTabs.Content>
</KTabs>
</Field>
</div>
);
}}
Expand Down
6 changes: 3 additions & 3 deletions apps/desktop/src/routes/editor/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function Player() {
const frame = latestFrame();
if (!frame) return;
const ctx = canvasRef.getContext("2d");
ctx?.putImageData(frame, 0, 0);
ctx?.putImageData(frame.data, 0, 0);
});

const [canvasContainerRef, setCanvasContainerRef] =
Expand Down Expand Up @@ -172,7 +172,7 @@ export function Player() {
};

const frameAspect = () =>
currentFrame().width / currentFrame().height;
currentFrame().width / currentFrame().data.height;

const size = () => {
if (frameAspect() < containerAspect()) {
Expand Down Expand Up @@ -210,7 +210,7 @@ export function Player() {
ref={canvasRef}
id="canvas"
width={currentFrame().width}
height={currentFrame().height}
height={currentFrame().data.height}
/>
);
}}
Expand Down
35 changes: 23 additions & 12 deletions apps/desktop/src/routes/editor/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
For,
Show,
createContext,
batch,
createRoot,
createSignal,
onMount,
Expand Down Expand Up @@ -404,18 +405,28 @@ export function Timeline() {
if (time === undefined) return;

e.stopPropagation();
setProject(
"timeline",
"zoomSegments",
produce((zoomSegments) => {
zoomSegments ??= [];
zoomSegments.push({
start: time,
end: time + 1,
amount: 1.5,
});
})
);
batch(() => {
setProject("timeline", "zoomSegments", (v) => v ?? []);
setProject(
"timeline",
"zoomSegments",
produce((zoomSegments) => {
zoomSegments ??= [];
zoomSegments.push({
start: time,
end: time + 1,
amount: 1.5,
mode: {
manual: {
x: 0.5,
y: 0.5,
},
},
});
console.log(zoomSegments);
})
);
});
}}
>
<Show
Expand Down
5 changes: 4 additions & 1 deletion apps/desktop/src/routes/editor/editorInstanceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export type FrameData = { width: number; height: number; data: ImageData };

export const [EditorInstanceContextProvider, useEditorInstanceContext] =
createContextProvider((props: { videoId: string }) => {
const [latestFrame, setLatestFrame] = createLazySignal<ImageData>();
const [latestFrame, setLatestFrame] = createLazySignal<{
width: number;
data: ImageData;
}>();

const [editorInstance] = createResource(async () => {
const instance = await commands.createEditorInstance(props.videoId);
Expand Down
4 changes: 3 additions & 1 deletion apps/desktop/src/routes/editor/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import {
} from "solid-js";
import { useEditorContext } from "./context";

export function Field(props: ParentProps<{ name: string; icon: JSX.Element }>) {
export function Field(
props: ParentProps<{ name: string; icon?: JSX.Element }>
) {
return (
<div class="flex flex-col gap-[0.75rem]">
<span class="flex flex-row items-center gap-[0.375rem] text-gray-500 text-[0.875rem]">
Expand Down
Loading

0 comments on commit 6c2a686

Please sign in to comment.