Compare commits

...

2 Commits

Author SHA1 Message Date
b30dbd01f5 Fetch shot paths and features for the Shot Lab picker
All checks were successful
Tests / Tests (pull_request) Successful in 11s
The recorded-shot picker now renders per-shot table diagrams, so
GetRecordedStreamShots needs serializedShotPaths plus the pocketing and
cue-object features that PlaneShotDiagram consumes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:13:17 -07:00
303261f2d7 Add recorded Shot Lab queries 2026-07-11 20:56:47 -07:00
4 changed files with 475 additions and 40 deletions

View File

@@ -2400,7 +2400,7 @@ export type HomographyInfoGql = {
crop: BoundingBoxGql;
destPoints: PocketPointsGql;
frameIndex: Scalars["Int"]["output"];
id: Scalars["Int"]["output"];
id?: Maybe<Scalars["Int"]["output"]>;
pockets: Array<BoundingBoxGql>;
sourcePoints: PocketPointsGql;
};
@@ -3080,6 +3080,7 @@ export type Query = {
getResolvedTier: ResolvedTierGql;
getRuns: GetRunsResult;
getShotAnnotationTypes: Array<ShotAnnotationTypeGql>;
getShotTableState: ShotTableStateGql;
getShots: Array<ShotGql>;
getShotsByIds: Array<ShotGql>;
getShotsWithMetadata: GetShotsResult;
@@ -3225,6 +3226,10 @@ export type QueryGetShotAnnotationTypesArgs = {
errorTypes?: InputMaybe<Scalars["Boolean"]["input"]>;
};
export type QueryGetShotTableStateArgs = {
shotId: Scalars["Int"]["input"];
};
export type QueryGetShotsArgs = {
countRespectsLimit?: Scalars["Boolean"]["input"];
filterInput: FilterInput;
@@ -3604,6 +3609,14 @@ export type ShotProjectionGql = {
trajectories: Array<BallTrajectoryGql>;
};
export type ShotTableStateGql = {
__typename?: "ShotTableStateGQL";
frameIndex: Scalars["Int"]["output"];
shotId: Scalars["Int"]["output"];
tableState: TableStateGql;
videoId: Scalars["Int"]["output"];
};
export type ShotsOrderingComponent =
| {
difficulty: FloatOrdering;
@@ -6593,6 +6606,79 @@ export type ReactToVideoMutation = {
reactToVideo: boolean;
};
export type GetRecordedStreamsQueryVariables = Exact<{
limit?: Scalars["Int"]["input"];
}>;
export type GetRecordedStreamsQuery = {
__typename?: "Query";
getFeedVideos: {
__typename?: "VideoHistoryGQL";
videos: Array<{
__typename?: "VideoGQL";
id: number;
name?: string | null;
createdAt?: any | null;
screenshotUri?: string | null;
framesPerSecond: number;
totalShots: number;
owner?: {
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
} | null;
}>;
};
};
export type GetRecordedStreamShotsQueryVariables = Exact<{
videoId: Scalars["Int"]["input"];
limit?: Scalars["Int"]["input"];
}>;
export type GetRecordedStreamShotsQuery = {
__typename?: "Query";
getOrderedShots: {
__typename?: "GetShotsResult";
shots: Array<{
__typename?: "ShotGQL";
id: number;
videoId: number;
startFrame: number;
endFrame: number;
serializedShotPaths?: {
__typename?: "SerializedShotPathsGQL";
b64EncodedBuffer?: string | null;
} | null;
cueObjectFeatures?: {
__typename?: "CueObjectFeaturesGQL";
cueObjectDistance?: number | null;
cueObjectAngle?: number | null;
cueBallSpeed?: number | null;
shotDirection?: ShotDirectionEnum | null;
spinType?: SpinTypeEnum | null;
} | null;
pocketingIntentionFeatures?: {
__typename?: "PocketingIntentionFeaturesGQL";
make?: boolean | null;
targetPocketDistance?: number | null;
targetPocketAngle?: number | null;
targetPocketAngleDirection?: ShotDirectionEnum | null;
marginOfErrorInDegrees?: number | null;
intendedPocketType?: PocketEnum | null;
difficulty?: number | null;
} | null;
pocketingIntentionInfo?: {
__typename?: "PocketingIntentionInfoGQL";
ballId: number;
pocketId: PocketIdentifier;
pathMetadataIndex: number;
} | null;
}>;
};
};
export type GetRunsForHighlightsQueryVariables = Exact<{
filterInput: RunFilterInput;
runIds?: InputMaybe<Array<Scalars["Int"]["input"]> | Scalars["Int"]["input"]>;
@@ -6824,34 +6910,28 @@ export type GetLiveTableStateQuery = {
__typename?: "RGBColorGQL";
hex: string;
} | null>;
homography?: {
__typename?: "HomographyInfoGQL";
id: number;
frameIndex: number;
crop: {
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
};
pockets: Array<{
__typename?: "BoundingBoxGQL";
left: number;
top: number;
width: number;
height: number;
};
};
export type GetShotTableStateQueryVariables = Exact<{
shotId: Scalars["Int"]["input"];
}>;
sourcePoints: {
__typename?: "PocketPointsGQL";
topLeft: { __typename?: "IntPoint2D"; x: number; y: number };
topSide: { __typename?: "IntPoint2D"; x: number; y: number };
topRight: { __typename?: "IntPoint2D"; x: number; y: number };
bottomLeft: { __typename?: "IntPoint2D"; x: number; y: number };
bottomSide: { __typename?: "IntPoint2D"; x: number; y: number };
bottomRight: { __typename?: "IntPoint2D"; x: number; y: number };
};
} | null;
export type GetShotTableStateQuery = {
__typename?: "Query";
getShotTableState: {
__typename?: "ShotTableStateGQL";
shotId: number;
videoId: number;
frameIndex: number;
tableState: {
__typename?: "TableStateGQL";
identifierToPosition: Array<Array<number>>;
identifierToColor: Array<{
__typename?: "RGBColorGQL";
hex: string;
} | null>;
};
};
};
@@ -7855,7 +7935,7 @@ export type GetStreamMonitoringDetailsQuery = {
elapsedTime?: number | null;
currentHomography?: {
__typename?: "HomographyInfoGQL";
id: number;
id?: number | null;
frameIndex: number;
crop: {
__typename?: "BoundingBoxGQL";
@@ -8269,7 +8349,7 @@ export type GetVideoQuery = {
} | null;
homographyHistory: Array<{
__typename?: "HomographyInfoGQL";
id: number;
id?: number | null;
frameIndex: number;
crop: {
__typename?: "BoundingBoxGQL";
@@ -8517,7 +8597,7 @@ export type FindPrerecordTableLayoutMutation = {
__typename?: "Mutation";
findPrerecordTableLayout?: {
__typename?: "HomographyInfoGQL";
id: number;
id?: number | null;
frameIndex: number;
crop: {
__typename?: "BoundingBoxGQL";
@@ -8547,7 +8627,7 @@ export type FindPrerecordTableLayoutMutation = {
export type HomographyInfoFragment = {
__typename?: "HomographyInfoGQL";
id: number;
id?: number | null;
frameIndex: number;
crop: {
__typename?: "BoundingBoxGQL";
@@ -14201,6 +14281,203 @@ export type ReactToVideoMutationOptions = Apollo.BaseMutationOptions<
ReactToVideoMutation,
ReactToVideoMutationVariables
>;
export const GetRecordedStreamsDocument = gql`
query GetRecordedStreams($limit: Int! = 25) {
getFeedVideos(
limit: $limit
filters: { isStreamCompleted: true, excludeVideosWithNoShots: true }
includePrivate: MINE
feedInput: { allUsers: true }
) {
videos {
id
name
createdAt
screenshotUri
framesPerSecond
totalShots
owner {
id
username
profileImageUri
}
}
}
}
`;
/**
* __useGetRecordedStreamsQuery__
*
* To run a query within a React component, call `useGetRecordedStreamsQuery` and pass it any options that fit your needs.
* When your component renders, `useGetRecordedStreamsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetRecordedStreamsQuery({
* variables: {
* limit: // value for 'limit'
* },
* });
*/
export function useGetRecordedStreamsQuery(
baseOptions?: Apollo.QueryHookOptions<
GetRecordedStreamsQuery,
GetRecordedStreamsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetRecordedStreamsQuery,
GetRecordedStreamsQueryVariables
>(GetRecordedStreamsDocument, options);
}
export function useGetRecordedStreamsLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetRecordedStreamsQuery,
GetRecordedStreamsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetRecordedStreamsQuery,
GetRecordedStreamsQueryVariables
>(GetRecordedStreamsDocument, options);
}
export function useGetRecordedStreamsSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetRecordedStreamsQuery,
GetRecordedStreamsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetRecordedStreamsQuery,
GetRecordedStreamsQueryVariables
>(GetRecordedStreamsDocument, options);
}
export type GetRecordedStreamsQueryHookResult = ReturnType<
typeof useGetRecordedStreamsQuery
>;
export type GetRecordedStreamsLazyQueryHookResult = ReturnType<
typeof useGetRecordedStreamsLazyQuery
>;
export type GetRecordedStreamsSuspenseQueryHookResult = ReturnType<
typeof useGetRecordedStreamsSuspenseQuery
>;
export type GetRecordedStreamsQueryResult = Apollo.QueryResult<
GetRecordedStreamsQuery,
GetRecordedStreamsQueryVariables
>;
export const GetRecordedStreamShotsDocument = gql`
query GetRecordedStreamShots($videoId: Int!, $limit: Int! = 200) {
getOrderedShots(
filterInput: { videoId: [$videoId] }
shotsOrdering: { orderings: [{ startFrame: { descending: false } }] }
limit: $limit
) {
shots {
id
videoId
startFrame
endFrame
serializedShotPaths {
b64EncodedBuffer
}
cueObjectFeatures {
cueObjectDistance
cueObjectAngle
cueBallSpeed
shotDirection
spinType
}
pocketingIntentionFeatures {
make
targetPocketDistance
targetPocketAngle
targetPocketAngleDirection
marginOfErrorInDegrees
intendedPocketType
difficulty
}
pocketingIntentionInfo {
ballId
pocketId
pathMetadataIndex
}
}
}
}
`;
/**
* __useGetRecordedStreamShotsQuery__
*
* To run a query within a React component, call `useGetRecordedStreamShotsQuery` and pass it any options that fit your needs.
* When your component renders, `useGetRecordedStreamShotsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetRecordedStreamShotsQuery({
* variables: {
* videoId: // value for 'videoId'
* limit: // value for 'limit'
* },
* });
*/
export function useGetRecordedStreamShotsQuery(
baseOptions: Apollo.QueryHookOptions<
GetRecordedStreamShotsQuery,
GetRecordedStreamShotsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetRecordedStreamShotsQuery,
GetRecordedStreamShotsQueryVariables
>(GetRecordedStreamShotsDocument, options);
}
export function useGetRecordedStreamShotsLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetRecordedStreamShotsQuery,
GetRecordedStreamShotsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetRecordedStreamShotsQuery,
GetRecordedStreamShotsQueryVariables
>(GetRecordedStreamShotsDocument, options);
}
export function useGetRecordedStreamShotsSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetRecordedStreamShotsQuery,
GetRecordedStreamShotsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetRecordedStreamShotsQuery,
GetRecordedStreamShotsQueryVariables
>(GetRecordedStreamShotsDocument, options);
}
export type GetRecordedStreamShotsQueryHookResult = ReturnType<
typeof useGetRecordedStreamShotsQuery
>;
export type GetRecordedStreamShotsLazyQueryHookResult = ReturnType<
typeof useGetRecordedStreamShotsLazyQuery
>;
export type GetRecordedStreamShotsSuspenseQueryHookResult = ReturnType<
typeof useGetRecordedStreamShotsSuspenseQuery
>;
export type GetRecordedStreamShotsQueryResult = Apollo.QueryResult<
GetRecordedStreamShotsQuery,
GetRecordedStreamShotsQueryVariables
>;
export const GetRunsForHighlightsDocument = gql`
query GetRunsForHighlights(
$filterInput: RunFilterInput!
@@ -14622,13 +14899,9 @@ export const GetLiveTableStateDocument = gql`
identifierToColor {
hex
}
homography {
...HomographyInfo
}
}
}
}
${HomographyInfoFragmentDoc}
`;
/**
@@ -14696,6 +14969,87 @@ export type GetLiveTableStateQueryResult = Apollo.QueryResult<
GetLiveTableStateQuery,
GetLiveTableStateQueryVariables
>;
export const GetShotTableStateDocument = gql`
query GetShotTableState($shotId: Int!) {
getShotTableState(shotId: $shotId) {
shotId
videoId
frameIndex
tableState {
identifierToPosition
identifierToColor {
hex
}
}
}
}
`;
/**
* __useGetShotTableStateQuery__
*
* To run a query within a React component, call `useGetShotTableStateQuery` and pass it any options that fit your needs.
* When your component renders, `useGetShotTableStateQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetShotTableStateQuery({
* variables: {
* shotId: // value for 'shotId'
* },
* });
*/
export function useGetShotTableStateQuery(
baseOptions: Apollo.QueryHookOptions<
GetShotTableStateQuery,
GetShotTableStateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetShotTableStateQuery,
GetShotTableStateQueryVariables
>(GetShotTableStateDocument, options);
}
export function useGetShotTableStateLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetShotTableStateQuery,
GetShotTableStateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetShotTableStateQuery,
GetShotTableStateQueryVariables
>(GetShotTableStateDocument, options);
}
export function useGetShotTableStateSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetShotTableStateQuery,
GetShotTableStateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetShotTableStateQuery,
GetShotTableStateQueryVariables
>(GetShotTableStateDocument, options);
}
export type GetShotTableStateQueryHookResult = ReturnType<
typeof useGetShotTableStateQuery
>;
export type GetShotTableStateLazyQueryHookResult = ReturnType<
typeof useGetShotTableStateLazyQuery
>;
export type GetShotTableStateSuspenseQueryHookResult = ReturnType<
typeof useGetShotTableStateSuspenseQuery
>;
export type GetShotTableStateQueryResult = Apollo.QueryResult<
GetShotTableStateQuery,
GetShotTableStateQueryVariables
>;
export const SimulateShotDocument = gql`
query SimulateShot($simulationInput: SimulateShotInputGQL!) {
simulateShot(simulationInput: $simulationInput) {

View File

@@ -0,0 +1,62 @@
# Lightweight two-step picker data for loading a recorded shot into Shot Lab.
query GetRecordedStreams($limit: Int! = 25) {
getFeedVideos(
limit: $limit
filters: { isStreamCompleted: true, excludeVideosWithNoShots: true }
includePrivate: MINE
feedInput: { allUsers: true }
) {
videos {
id
name
createdAt
screenshotUri
framesPerSecond
totalShots
owner {
id
username
profileImageUri
}
}
}
}
query GetRecordedStreamShots($videoId: Int!, $limit: Int! = 200) {
getOrderedShots(
filterInput: { videoId: [$videoId] }
shotsOrdering: { orderings: [{ startFrame: { descending: false } }] }
limit: $limit
) {
shots {
id
videoId
startFrame
endFrame
serializedShotPaths {
b64EncodedBuffer
}
cueObjectFeatures {
cueObjectDistance
cueObjectAngle
cueBallSpeed
shotDirection
spinType
}
pocketingIntentionFeatures {
make
targetPocketDistance
targetPocketAngle
targetPocketAngleDirection
marginOfErrorInDegrees
intendedPocketType
difficulty
}
pocketingIntentionInfo {
ballId
pocketId
pathMetadataIndex
}
}
}
}

View File

@@ -24,8 +24,19 @@ query GetLiveTableState($videoId: Int!) {
identifierToColor {
hex
}
homography {
...HomographyInfo
}
}
}
query GetShotTableState($shotId: Int!) {
getShotTableState(shotId: $shotId) {
shotId
videoId
frameIndex
tableState {
identifierToPosition
identifierToColor {
hex
}
}
}

View File

@@ -73,6 +73,7 @@ type Query {
useHomography: HomographyInputGQL = null
): TableStateGQL!
getLiveTableState(videoId: Int!): LiveTableStateGQL!
getShotTableState(shotId: Int!): ShotTableStateGQL!
simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL!
computePotAim(
simulationInput: SimulateShotInputGQL!
@@ -609,7 +610,7 @@ type VideoTagClass {
}
type HomographyInfoGQL {
id: Int!
id: Int
frameIndex: Int!
crop: BoundingBoxGQL!
pockets: [BoundingBoxGQL!]!
@@ -1065,6 +1066,13 @@ type LiveTableStateGQL {
tableState: TableStateGQL!
}
type ShotTableStateGQL {
shotId: Int!
videoId: Int!
frameIndex: Int!
tableState: TableStateGQL!
}
type ShotProjectionGQL {
trajectories: [BallTrajectoryGQL!]!
events: [SimulationEventGQL!]!