Skip to content

Commit

Permalink
performance fix for #48
Browse files Browse the repository at this point in the history
  • Loading branch information
jbilcke-hf committed Sep 3, 2024
1 parent 1a43aeb commit da22137
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 145 deletions.
7 changes: 4 additions & 3 deletions packages/timeline/src/components/cells/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Suspense } from "react"
import { Suspense, useMemo } from "react"
import { a } from "@react-spring/three"
import { ClapSegmentCategory } from "@aitube/clap"

Expand All @@ -17,6 +17,8 @@ import { useThree } from "@react-three/fiber"
import { SegmentArea } from "@/types/timeline"
import { SegmentIcon } from "../icons/SegmentIcon"

import { getSegmentColorScheme } from "@/utils/getSegmentColorScheme"

export function Cell({
segment: s
}: {
Expand All @@ -31,8 +33,7 @@ export function Cell({
// this is only used to react to changes in the segment
const segmentChanged = useSegmentChanges(s)

const getSegmentColorScheme = useTimeline(s => s.getSegmentColorScheme)
const colorScheme = getSegmentColorScheme(s)
const colorScheme = useMemo(() => getSegmentColorScheme(s), [segmentChanged])

const cellWidth = useTimeline((s) => s.cellWidth)
const getCellHeight = useTimeline((s) => s.getCellHeight)
Expand Down
42 changes: 14 additions & 28 deletions packages/timeline/src/components/scroller/HorizontalScroller.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { useMemo } from "react"

import { useTimeline } from "@/hooks/useTimeline"
import { getSegmentColorScheme } from "@/utils/getSegmentColorScheme"


import TimelineSlider from "../slider/TimelineSlider"

export function HorizontalScroller() {
const theme = useTimeline(s => s.theme)

const containerWidth = useTimeline(s => s.containerWidth)

const segments = useTimeline(s => s.segments)

const atLeastOneSegmentChanged = useTimeline(s => s.atLeastOneSegmentChanged)

const timelineCamera = useTimeline(s => s.timelineCamera)
const timelineControls = useTimeline(s => s.timelineControls)

Expand All @@ -26,33 +34,17 @@ export function HorizontalScroller() {
const setScrollX = useTimeline(s => s.setScrollX)
const contentWidth = useTimeline(s => s.contentWidth)

const getSegmentColorScheme = useTimeline(s => s.getSegmentColorScheme)

const cachedSegments = useMemo(() => segments, [atLeastOneSegmentChanged, containerWidth, contentWidth])
if (!timelineCamera || !timelineControls) { return null }

// TODO: we need to be able to change the zoom level from the horizontal scroller
const handleZoomChange = (newZoom: number) => {
setHorizontalZoomLevel(newZoom)
}

return (
<div className="flex flex-row items-center w-full">
{/*
PREVIOUS COMPONENT, NOW OBSOLETE:
<HorizontalSlider
defaultValue={[rangeStart, rangeEnd]}
min={0}
max={width}
step={1}
value={[rangeStart, rangeEnd]}
onValueChange={(newRange: number[]) => {
handleTimelinePositionChange(newRange[0])
}}
onWheel={(e) => {
// handleZoomChange(cellWidth + e.deltaY)
}}
/>
*/}

<TimelineSlider
minTimeInMs={0}
maxTimeInMs={durationInMs}
Expand All @@ -69,15 +61,9 @@ export function HorizontalScroller() {
slidingWindowRangeThumbBorderRadiusInPx={2}
slidingWindowRangeThumbBackgroundColor="rgba(0,123,123,0.2)"
className="w-full h-14"
events={segments.map(s => ({
id: s.id,
track: s.track,
startTimeInMs: s.startTimeInMs,
endTimeInMs: s.endTimeInMs,
color: getSegmentColorScheme(s).backgroundColor,
}))}
eventOpacityWhenInsideSlidingWindowRangeThumb={1.0}
eventOpacityWhenOutsideSlidingWindowRangeThumb={0.7}
segments={cachedSegments}
segmentOpacityWhenInsideSlidingWindowRangeThumb={1.0}
segmentOpacityWhenOutsideSlidingWindowRangeThumb={0.7}
onSlidingWindowRangeThumbUpdate={({
slidingWindowRangeThumbStartTimeInMs,
slidingWindowRangeThumbEndTimeInMs
Expand Down
44 changes: 18 additions & 26 deletions packages/timeline/src/components/slider/TimelineSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { useTimeline } from '@/index';
import { TimelineSegment, useTimeline } from '@/index';
import React, { useRef, useEffect, useState, useCallback, useMemo } from 'react';

export interface TimelineSliderEvent {
id: string;
track: number;
startTimeInMs: number;
endTimeInMs: number;
color: string;
}

export interface TimelineSliderProps {
minTimeInMs: number;
maxTimeInMs: number;
Expand All @@ -25,9 +17,9 @@ export interface TimelineSliderProps {
slidingWindowRangeThumbBorderRadiusInPx: number;
slidingWindowRangeThumbBackgroundColor: string;
className: string;
events?: TimelineSliderEvent[];
eventOpacityWhenInsideSlidingWindowRangeThumb: number;
eventOpacityWhenOutsideSlidingWindowRangeThumb: number;
segments?: TimelineSegment[];
segmentOpacityWhenInsideSlidingWindowRangeThumb: number;
segmentOpacityWhenOutsideSlidingWindowRangeThumb: number;
onSlidingWindowRangeThumbUpdate: (update: {
slidingWindowRangeThumbStartTimeInMs: number;
slidingWindowRangeThumbEndTimeInMs: number;
Expand Down Expand Up @@ -62,9 +54,9 @@ const TimelineSlider: React.FC<TimelineSliderProps> = ({
slidingWindowRangeThumbBorderRadiusInPx,
slidingWindowRangeThumbBackgroundColor,
className,
events = [],
eventOpacityWhenInsideSlidingWindowRangeThumb,
eventOpacityWhenOutsideSlidingWindowRangeThumb,
segments = [],
segmentOpacityWhenInsideSlidingWindowRangeThumb,
segmentOpacityWhenOutsideSlidingWindowRangeThumb,
onSlidingWindowRangeThumbUpdate,
onPlaybackCursorUpdate,
}) => {
Expand All @@ -90,25 +82,25 @@ const TimelineSlider: React.FC<TimelineSliderProps> = ({

ctx.clearRect(0, 0, width * dpr, height * dpr);

const totalTracks = Math.max(...events.map(e => e.track), 0) + 1;
const totalTracks = Math.max(...segments.map(e => e.track), 0) + 1;
const trackHeight = height / totalTracks;

events.forEach(event => {
const startX = ((event.startTimeInMs - minTimeInMs) / (maxTimeInMs - minTimeInMs)) * width;
const endX = ((event.endTimeInMs - minTimeInMs) / (maxTimeInMs - minTimeInMs)) * width;
const y = event.track * trackHeight;
segments.forEach(segment => {
const startX = ((segment.startTimeInMs - minTimeInMs) / (maxTimeInMs - minTimeInMs)) * width;
const endX = ((segment.endTimeInMs - minTimeInMs) / (maxTimeInMs - minTimeInMs)) * width;
const y = segment.track * trackHeight;

ctx.fillStyle = event.color;
ctx.fillStyle = segment.colors.backgroundColor;
ctx.globalAlpha =
(event.startTimeInMs >= windowStart && event.endTimeInMs <= windowEnd)
? eventOpacityWhenInsideSlidingWindowRangeThumb
: eventOpacityWhenOutsideSlidingWindowRangeThumb;
(segment.startTimeInMs >= windowStart && segment.endTimeInMs <= windowEnd)
? segmentOpacityWhenInsideSlidingWindowRangeThumb
: segmentOpacityWhenOutsideSlidingWindowRangeThumb;

ctx.fillRect(startX, y, endX - startX, trackHeight);
});
}, [events, minTimeInMs, maxTimeInMs, windowStart, windowEnd, eventOpacityWhenInsideSlidingWindowRangeThumb, eventOpacityWhenOutsideSlidingWindowRangeThumb]);
}, [segments, minTimeInMs, maxTimeInMs, windowStart, windowEnd, segmentOpacityWhenInsideSlidingWindowRangeThumb, segmentOpacityWhenOutsideSlidingWindowRangeThumb]);

const memoizedEvents = useMemo(() => events, [events]);
const memoizedEvents = useMemo(() => segments, [segments]);

const setCanvasSize = useCallback(() => {
const canvas = canvasRef.current;
Expand Down
46 changes: 34 additions & 12 deletions packages/timeline/src/components/timeline/TopBarTimeScale.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import React, { useEffect, useMemo, useRef } from "react"

import { useThree } from "@react-three/fiber"
import { Plane, Text } from "@react-three/drei"

import {
useTimeline
} from "@/hooks"

import { useTimeline } from "@/hooks"
import { useTimeScaleGraduations } from "@/hooks/useTimeScaleGraduations"
import { formatTimestamp } from "@/utils/formatTimestamp"

import { leftBarTrackScaleWidth, topBarTimeScaleHeight } from "@/constants/themes"
import { useThree } from "@react-three/fiber"

export function TopBarTimeScale() {
const containerRef = useRef<HTMLDivElement>(null);

const { size } = useThree()
const containerWidth = useTimeline(s => s.containerWidth)

const { size, camera } = useThree()

const jumpAt = useTimeline(s => s.jumpAt)
const togglePlayback = useTimeline(s => s.togglePlayback)
Expand Down Expand Up @@ -153,7 +151,26 @@ export function TopBarTimeScale() {
))}
</group>
<group position={[0, 0, 0]} visible={!isResizing}>
{timeScaleGraduations.filter((_, idx) => (idx * cellWidth) < maxWidth).map((lineGeometry, idx) => (
{timeScaleGraduations
.filter((_, idx) => (idx * cellWidth) < maxWidth)
.map((lineGeometry, idx) => {

if (
// Hide text if it's too close to others or out of view
(cellWidth <= 4 && idx % 10 !== 0) ||
(cellWidth <= 40 && idx % unit !== 0) ||
idx === 0 // Always hide the 0

// TODO: those need more work
// ||
// (idx * cellWidth) < camera.position.x - size.width / 2 - cellWidth || // Out of view on the left
// (idx * cellWidth) > camera.position.x + size.width / 2 + cellWidth // Out of view on the right
) {
return null
}


return (
<Text
key={idx}
position={[
Expand All @@ -179,6 +196,7 @@ export function TopBarTimeScale() {
// so you will have to change the "Arial" or "bold Arial"
// in the function which computes a character's width
fontWeight={200}
/*
visible={
// always hide the 0
idx === 0
Expand All @@ -194,6 +212,7 @@ export function TopBarTimeScale() {
: false
}
*/
>
{
formatTimestamp(
Expand All @@ -204,7 +223,8 @@ export function TopBarTimeScale() {
milliseconds: cellWidth > 20,
})}
</Text>
))}
)
}).filter(x => x)}
</group>
</>
), [
Expand All @@ -215,6 +235,7 @@ export function TopBarTimeScale() {
contentWidth,
cellWidth,
unit,
containerWidth,
formatTimestamp,
theme.topBarTimeScale.backgroundColor,
theme.topBarTimeScale.lineColor,
Expand All @@ -236,9 +257,10 @@ export function TopBarTimeScale() {

const disableWheel = true
if (disableWheel) {
console.log(`user tried to change the horizontal scale, but it is disabled due to rescaling bugs (@Julian fix this!)`)
e.stopPropagation()
return false
console.log(
`zoom in/out is currently disabled, we need to update the min and max values and check other redrawing routines (see https://github.com/jbilcke-hf/clapper/issues/47)`)
e.stopPropagation()
return false
}

const wheelFactor = 0.3
Expand Down
15 changes: 15 additions & 0 deletions packages/timeline/src/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ const segment: TimelineSegment = {
isGrabbedOnLeftHandle: false,
isGrabbedOnRightHandle: false,
editionStatus: SegmentEditionStatus.EDITABLE,
colors: {
baseHue: 0,
baseSaturation: 0,
baseLightness: 0,
backgroundColor: '',
backgroundColorHover: '',
backgroundColorDisabled: '',
foregroundColor: '',
borderColor: '',
textColor: '',
textColorHover: '',
waveformLineSpacing: 0,
waveformGradientStart: 0,
waveformGradientEnd: 0
},
}

useTimeline.setState({
Expand Down
1 change: 1 addition & 0 deletions packages/timeline/src/hooks/useTimeScaleGraduations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const useTimeScaleGraduations = ({
}: {
unit: number
}) => {
const containerWidth = useTimeline(s => s.containerWidth)
const cellWidth = useTimeline(s => s.cellWidth)
const nbMaxShots = useTimeline(s => s.nbMaxShots)
const contentWidth = useTimeline((s) => s.contentWidth)
Expand Down
62 changes: 0 additions & 62 deletions packages/timeline/src/hooks/useTimeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,68 +336,6 @@ export const useTimeline = create<TimelineStore>((set, get) => ({
}
return height
},

getSegmentColorScheme: (segment: TimelineSegment): ClapSegmentColorScheme => {

const { theme } = get()

let baseHue = 0

let baseSaturation = theme.cell.categoryColors.GENERIC.saturation
let baseLightness = theme.cell.categoryColors.GENERIC.lightness

let backgroundColorSaturation = (segment.isSelected ? 2.2 : 1.4) * baseSaturation
let backgroundColorHoverSaturation = (segment.isSelected ? 2.2 : 1.8) * baseSaturation

let colorScheme: ClapSegmentColorScheme = {
baseHue,
baseSaturation,
baseLightness,

backgroundColor: hslToHex(baseHue, backgroundColorSaturation, baseLightness),
backgroundColorHover: hslToHex(baseHue, backgroundColorHoverSaturation, baseLightness + 1),
backgroundColorDisabled: hslToHex(baseHue, baseSaturation - 15, baseLightness - 2),
foregroundColor: hslToHex(baseHue, baseSaturation + 40, baseLightness),
borderColor: hslToHex(baseHue, baseSaturation + 40, baseLightness + 10),
textColor: hslToHex(baseHue, baseSaturation + 55, baseLightness - 60),
textColorHover: hslToHex(baseHue, baseSaturation + 55, baseLightness - 50),

waveformLineSpacing: theme.cell.waveform.lineSpacing,
waveformGradientStart: theme.cell.waveform.gradientStart,
waveformGradientEnd: theme.cell.waveform.gradientEnd,
}

if (!segment) { return colorScheme }

const clapSegmentCategoryColors: ClapSegmentCategoryColors = theme.cell.categoryColors

const candidateHSL = clapSegmentCategoryColors[segment.category]
if (!candidateHSL) { return colorScheme }

baseHue = candidateHSL.hue
baseSaturation = candidateHSL.saturation
baseLightness = candidateHSL.lightness

colorScheme = {
baseHue,
baseSaturation,
baseLightness,

backgroundColor: hslToHex(baseHue, backgroundColorSaturation, baseLightness),
backgroundColorHover: hslToHex(baseHue, backgroundColorHoverSaturation, baseLightness + 1),
backgroundColorDisabled: hslToHex(baseHue, baseSaturation - 15, baseLightness - 2),
foregroundColor: hslToHex(baseHue, baseSaturation + 40, baseLightness),
borderColor: hslToHex(baseHue, baseSaturation + 40, baseLightness + 10),
textColor: hslToHex(baseHue, baseSaturation + 55, baseLightness - 60),
textColorHover: hslToHex(baseHue, baseSaturation + 55, baseLightness - 50),

waveformLineSpacing: theme.cell.waveform.lineSpacing,
waveformGradientStart: theme.cell.waveform.gradientStart,
waveformGradientEnd: theme.cell.waveform.gradientEnd,
}

return colorScheme
},
setHoveredSegment: ({
hoveredSegment,
area,
Expand Down
Loading

0 comments on commit da22137

Please sign in to comment.