Compare commits

..

2 Commits

Author SHA1 Message Date
19bdf69830 Add suggestPositionShot schema types and operation
Mirrors the backend's position-play search: SuggestPositionShotInputGQL
(pot lock + target-area polygon), PositionSuggestionGQL /
PositionShotCandidateGQL / CueStrikeGQL, the Query field, and a
SuggestPositionShot operation with a shared candidate fragment. The
regenerated hooks also provide the result types the mobile app uses to
shape local (billsim) physics results.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:28:45 -07:00
303261f2d7 Add recorded Shot Lab queries 2026-07-11 20:56:47 -07:00
5 changed files with 5815 additions and 14499 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
# 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
}
}
}

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
}
}
}
@@ -74,3 +85,53 @@ query ComputePotAim(
occludingBallIds
}
}
fragment PositionShotCandidate on PositionShotCandidateGQL {
strike {
v0
phi
theta
a
b
}
cueFinalPosition
inTargetArea
distanceToTargetArea
robustness
potted
scratched
projection {
trajectories {
ballId
points {
time
position
}
}
events {
eventType
time
ballIds
position
}
finalState {
ballId
position
}
pottedBallIds
}
}
query SuggestPositionShot($suggestionInput: SuggestPositionShotInputGQL!) {
suggestPositionShot(suggestionInput: $suggestionInput) {
achievable
evaluatedCount
successfulCount
best {
...PositionShotCandidate
}
alternates {
...PositionShotCandidate
}
}
}

View File

@@ -12,13 +12,6 @@ query getUsageStats($days: Int! = 30) {
retentionPrevActive
retentionReturned
}
cohorts {
weekStart
signups
activated7d
paid7d
activatedNotPaid7d
}
totalUsers
totalVideos
totalShots

View File

@@ -73,12 +73,16 @@ type Query {
useHomography: HomographyInputGQL = null
): TableStateGQL!
getLiveTableState(videoId: Int!): LiveTableStateGQL!
getShotTableState(shotId: Int!): ShotTableStateGQL!
simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL!
computePotAim(
simulationInput: SimulateShotInputGQL!
targetBallId: Int!
pocket: PocketIdentifier!
): PotAimGQL!
suggestPositionShot(
suggestionInput: SuggestPositionShotInputGQL!
): PositionSuggestionGQL!
getOrderedShots(
filterInput: FilterInput!
ids: [Int!] = null
@@ -609,7 +613,7 @@ type VideoTagClass {
}
type HomographyInfoGQL {
id: Int!
id: Int
frameIndex: Int!
crop: BoundingBoxGQL!
pockets: [BoundingBoxGQL!]!
@@ -1065,6 +1069,13 @@ type LiveTableStateGQL {
tableState: TableStateGQL!
}
type ShotTableStateGQL {
shotId: Int!
videoId: Int!
frameIndex: Int!
tableState: TableStateGQL!
}
type ShotProjectionGQL {
trajectories: [BallTrajectoryGQL!]!
events: [SimulationEventGQL!]!
@@ -1135,6 +1146,44 @@ type PotAimGQL {
occludingBallIds: [Int!]!
}
type PositionSuggestionGQL {
achievable: Boolean!
best: PositionShotCandidateGQL
alternates: [PositionShotCandidateGQL!]!
evaluatedCount: Int!
successfulCount: Int!
}
type PositionShotCandidateGQL {
strike: CueStrikeGQL!
cueFinalPosition: [Float!]!
inTargetArea: Boolean!
distanceToTargetArea: Float!
robustness: Int!
potted: Boolean!
scratched: Boolean!
projection: ShotProjectionGQL
}
type CueStrikeGQL {
v0: Float!
phi: Float!
theta: Float!
a: Float!
b: Float!
}
input SuggestPositionShotInputGQL {
cueBallId: Int!
targetBallId: Int!
pocket: PocketIdentifier!
targetArea: [[Float!]!]!
balls: [SimulationBallStateInputGQL!] = null
b64Image: String = null
useHomography: HomographyInputGQL = null
tableSize: Float = null
}
type GetShotsResult {
shots: [ShotGQL!]!
count: Int
@@ -1175,7 +1224,6 @@ type UsageStatsGQL {
totalVideos: Int!
totalShots: Int!
totalRuns: Int!
cohorts: [CohortStatsGQL!]!
windows: [UsageStatsWindowGQL!]!
daily: [UsageStatsDailyGQL!]!
processingLast24h: [ProcessingStatusCountGQL!]!
@@ -1194,14 +1242,6 @@ type FunnelStatsGQL {
retentionReturned: Int!
}
type CohortStatsGQL {
weekStart: Date!
signups: Int!
activated7d: Int!
paid7d: Int!
activatedNotPaid7d: Int!
}
type UsageStatsWindowGQL {
label: String!
newUsers: Int!