1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package platform.test.motion.golden 18 19 /** 20 * Captures a time-series feature of an observed [T]. 21 * 22 * A [DataPoint] of type [V] is recorded at each frame. 23 */ 24 class FeatureCapture<T, V : Any>(val name: String, private val captureFn: (T) -> DataPoint<V>) { capturenull25 fun capture(observed: T) = captureFn(observed) 26 } 27 28 class TimeSeriesCaptureScope<T>( 29 private val observing: T?, 30 private val valueCollector: MutableMap<String, MutableList<DataPoint<*>>>, 31 ) { 32 33 /** 34 * Records a [DataPoint] from [observing], extracted [using] the specified [FeatureCapture] and 35 * stored in the time-series as [name]. 36 * 37 * If the backing [observing] object cannot be resolved during an animation frame, 38 * `DataPoint.notFound` is recorded in the time-series. 39 * 40 * @param using extracts a [DataPoint] from [observing] 41 * @param name unique, human-readable label under which the feature is stored in the time-series 42 */ 43 fun feature(using: FeatureCapture<in T, *>, name: String = using.name) { 44 val dataPoint = if (observing != null) using.capture(observing) else DataPoint.notFound() 45 valueCollector.computeIfAbsent(name) { mutableListOf() }.add(dataPoint) 46 } 47 48 /** 49 * Captures features on other, related objects. 50 * 51 * @param resolveRelated finds the related object on which to capture features, invoked once per 52 * animation frame. Can return null if the related object is not currently available in the 53 * scene. 54 * @param nestedTimeSeriesCapture captures features on the related object. 55 */ 56 fun <U> on( 57 resolveRelated: (T) -> U?, 58 nestedTimeSeriesCapture: TimeSeriesCaptureScope<U>.() -> Unit, 59 ) { 60 with(TimeSeriesCaptureScope(observing?.let(resolveRelated), valueCollector)) { 61 nestedTimeSeriesCapture() 62 } 63 } 64 } 65