Implement Phase 6R-O qbr webcam shell
This commit is contained in:
parent
379f130930
commit
1237daa434
10 changed files with 1082 additions and 22 deletions
|
|
@ -239,6 +239,59 @@ namespace HyperTwistContractLibraryInternal
|
|||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistVisionShellProfile MakeDefaultClassicCubeWebcamShellProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistVisionShellProfile Profile;
|
||||
Profile.ShellProfileId = ProfileId;
|
||||
Profile.ShellKind = TEXT("webcam-overlay");
|
||||
Profile.LocaleRoutingMode = TEXT("first-party-locale-cycle");
|
||||
|
||||
auto AddPanel = [&Profile](
|
||||
const TCHAR* PanelId,
|
||||
const TCHAR* PanelKind,
|
||||
const TCHAR* AnchorId,
|
||||
const int32 GridRows,
|
||||
const int32 GridColumns,
|
||||
const bool bVisibleByDefault
|
||||
)
|
||||
{
|
||||
FHyperTwistVisionShellPanelLayout Panel;
|
||||
Panel.PanelId = PanelId;
|
||||
Panel.PanelKind = PanelKind;
|
||||
Panel.AnchorId = AnchorId;
|
||||
Panel.GridRows = GridRows;
|
||||
Panel.GridColumns = GridColumns;
|
||||
Panel.bVisibleByDefault = bVisibleByDefault;
|
||||
Profile.Panels.Add(Panel);
|
||||
};
|
||||
|
||||
auto AddAction = [&Profile](
|
||||
const TCHAR* ActionId,
|
||||
const TCHAR* InputBinding,
|
||||
const TCHAR* SurfaceId
|
||||
)
|
||||
{
|
||||
FHyperTwistVisionShellActionBinding ActionBinding;
|
||||
ActionBinding.ActionId = ActionId;
|
||||
ActionBinding.InputBinding = InputBinding;
|
||||
ActionBinding.SurfaceId = SurfaceId;
|
||||
Profile.ActionBindings.Add(ActionBinding);
|
||||
};
|
||||
|
||||
AddPanel(TEXT("preview-sticker-grid"), TEXT("sticker-grid"), TEXT("top-left"), 3, 3, true);
|
||||
AddPanel(TEXT("snapshot-sticker-grid"), TEXT("sticker-grid"), TEXT("left-stack-below-preview"), 3, 3, true);
|
||||
AddPanel(TEXT("scanned-side-counter"), TEXT("status-readout"), TEXT("bottom-left"), 0, 0, true);
|
||||
AddPanel(TEXT("classic-cube-net"), TEXT("cube-net"), TEXT("bottom-right"), 3, 4, true);
|
||||
AddPanel(TEXT("locale-indicator"), TEXT("status-readout"), TEXT("top-right"), 0, 0, true);
|
||||
AddPanel(TEXT("calibration-swatch-rail"), TEXT("calibration-rail"), TEXT("top-left-offset"), 6, 1, false);
|
||||
|
||||
AddAction(TEXT("capture-snapshot"), TEXT("Space"), TEXT("snapshot-sticker-grid"));
|
||||
AddAction(TEXT("toggle-calibration"), TEXT("C"), TEXT("calibration-swatch-rail"));
|
||||
AddAction(TEXT("cycle-locale"), TEXT("L"), TEXT("locale-indicator"));
|
||||
AddAction(TEXT("close-session"), TEXT("Escape"), TEXT("session-root"));
|
||||
return Profile;
|
||||
}
|
||||
|
||||
void ResolveMockClassicFaceSwatch(
|
||||
const FString& FaceId,
|
||||
FString& OutColorId,
|
||||
|
|
@ -402,6 +455,50 @@ namespace HyperTwistContractLibraryInternal
|
|||
Session.bHasCompleteClassicCubeNet = Session.MissingFaces.Num() == 0;
|
||||
return Session;
|
||||
}
|
||||
|
||||
FHyperTwistVisionShellState MakeClassicCubeWebcamShellState(
|
||||
const FHyperTwistVisionSessionConfig& SessionConfig,
|
||||
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
|
||||
const FString& PreferredTargetFaceHint,
|
||||
const FString& PreferredCommittedFaceId
|
||||
)
|
||||
{
|
||||
FHyperTwistVisionShellState ShellState;
|
||||
ShellState.ShellProfileId = !SessionConfig.ShellProfileId.IsEmpty()
|
||||
? SessionConfig.ShellProfileId
|
||||
: TEXT("classic-cube-webcam-shell-v1");
|
||||
ShellState.LocaleCode = TEXT("en");
|
||||
ShellState.TargetSideCount = 6;
|
||||
ShellState.TargetFaceHint = !PreferredTargetFaceHint.IsEmpty() ? PreferredTargetFaceHint : TEXT("F");
|
||||
ShellState.LastCommittedFaceId = PreferredCommittedFaceId;
|
||||
ShellState.CalibrationProfileId = !SessionConfig.CalibrationProfile.IsEmpty()
|
||||
? SessionConfig.CalibrationProfile
|
||||
: SessionConfig.CalibrationProfileDefinition.ProfileId;
|
||||
|
||||
if (ReconstructionSession.IsStructurallyValid())
|
||||
{
|
||||
ShellState.ScannedSideCount = FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount);
|
||||
for (const FHyperTwistVisionCommittedFaceState& FaceState : ReconstructionSession.CommittedFaces)
|
||||
{
|
||||
if (!FaceState.FaceId.IsEmpty())
|
||||
{
|
||||
ShellState.CapturedFaceIds.Add(FaceState.FaceId);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShellState.TargetFaceHint.IsEmpty() && ReconstructionSession.MissingFaces.Num() > 0)
|
||||
{
|
||||
ShellState.TargetFaceHint = ReconstructionSession.MissingFaces[0];
|
||||
}
|
||||
|
||||
if (ShellState.LastCommittedFaceId.IsEmpty() && ReconstructionSession.CommittedFaces.Num() > 0)
|
||||
{
|
||||
ShellState.LastCommittedFaceId = ReconstructionSession.CommittedFaces.Last().FaceId;
|
||||
}
|
||||
}
|
||||
|
||||
return ShellState;
|
||||
}
|
||||
}
|
||||
|
||||
FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition()
|
||||
|
|
@ -631,6 +728,11 @@ FHyperTwistVisionSessionConfig UHyperTwistContractLibrary::MakeSampleVisionSessi
|
|||
HyperTwistContractLibraryInternal::MakeDefaultClassicCubeCalibrationProfile(
|
||||
Config.CalibrationProfile
|
||||
);
|
||||
Config.ShellProfileId = TEXT("classic-cube-webcam-shell-v1");
|
||||
Config.ShellProfileDefinition =
|
||||
HyperTwistContractLibraryInternal::MakeDefaultClassicCubeWebcamShellProfile(
|
||||
Config.ShellProfileId
|
||||
);
|
||||
Config.FrameSize = FIntPoint(1280, 720);
|
||||
Config.MaxFrameRate = 30;
|
||||
return Config;
|
||||
|
|
@ -651,6 +753,12 @@ FHyperTwistVisionPreviewResult UHyperTwistContractLibrary::MakeMockVisionPreview
|
|||
0.84f,
|
||||
false
|
||||
);
|
||||
Result.ShellState = HyperTwistContractLibraryInternal::MakeClassicCubeWebcamShellState(
|
||||
UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(),
|
||||
FHyperTwistVisionReconstructionSession(),
|
||||
Result.ObservedFace.FaceId,
|
||||
FString()
|
||||
);
|
||||
Result.Guidance = TEXT("capture-ready");
|
||||
return Result;
|
||||
}
|
||||
|
|
@ -679,6 +787,12 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe
|
|||
{TEXT("F")},
|
||||
Result.Confidence
|
||||
);
|
||||
Result.ShellState = HyperTwistContractLibraryInternal::MakeClassicCubeWebcamShellState(
|
||||
UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(),
|
||||
Result.ReconstructionSession,
|
||||
Result.ObservedFace.FaceId,
|
||||
Result.CommittedUnit
|
||||
);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2383,6 +2383,110 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistVisionShellProfile BuildDefaultClassicCubeWebcamShellProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistVisionShellProfile Profile;
|
||||
Profile.ShellProfileId = ProfileId;
|
||||
Profile.ShellKind = TEXT("webcam-overlay");
|
||||
Profile.LocaleRoutingMode = TEXT("first-party-locale-cycle");
|
||||
|
||||
auto AddPanel = [&Profile](
|
||||
const TCHAR* PanelId,
|
||||
const TCHAR* PanelKind,
|
||||
const TCHAR* AnchorId,
|
||||
const int32 GridRows,
|
||||
const int32 GridColumns,
|
||||
const bool bVisibleByDefault
|
||||
)
|
||||
{
|
||||
FHyperTwistVisionShellPanelLayout Panel;
|
||||
Panel.PanelId = PanelId;
|
||||
Panel.PanelKind = PanelKind;
|
||||
Panel.AnchorId = AnchorId;
|
||||
Panel.GridRows = GridRows;
|
||||
Panel.GridColumns = GridColumns;
|
||||
Panel.bVisibleByDefault = bVisibleByDefault;
|
||||
Profile.Panels.Add(Panel);
|
||||
};
|
||||
|
||||
auto AddAction = [&Profile](
|
||||
const TCHAR* ActionId,
|
||||
const TCHAR* InputBinding,
|
||||
const TCHAR* SurfaceId
|
||||
)
|
||||
{
|
||||
FHyperTwistVisionShellActionBinding ActionBinding;
|
||||
ActionBinding.ActionId = ActionId;
|
||||
ActionBinding.InputBinding = InputBinding;
|
||||
ActionBinding.SurfaceId = SurfaceId;
|
||||
Profile.ActionBindings.Add(ActionBinding);
|
||||
};
|
||||
|
||||
AddPanel(TEXT("preview-sticker-grid"), TEXT("sticker-grid"), TEXT("top-left"), 3, 3, true);
|
||||
AddPanel(TEXT("snapshot-sticker-grid"), TEXT("sticker-grid"), TEXT("left-stack-below-preview"), 3, 3, true);
|
||||
AddPanel(TEXT("scanned-side-counter"), TEXT("status-readout"), TEXT("bottom-left"), 0, 0, true);
|
||||
AddPanel(TEXT("classic-cube-net"), TEXT("cube-net"), TEXT("bottom-right"), 3, 4, true);
|
||||
AddPanel(TEXT("locale-indicator"), TEXT("status-readout"), TEXT("top-right"), 0, 0, true);
|
||||
AddPanel(TEXT("calibration-swatch-rail"), TEXT("calibration-rail"), TEXT("top-left-offset"), 6, 1, false);
|
||||
|
||||
AddAction(TEXT("capture-snapshot"), TEXT("Space"), TEXT("snapshot-sticker-grid"));
|
||||
AddAction(TEXT("toggle-calibration"), TEXT("C"), TEXT("calibration-swatch-rail"));
|
||||
AddAction(TEXT("cycle-locale"), TEXT("L"), TEXT("locale-indicator"));
|
||||
AddAction(TEXT("close-session"), TEXT("Escape"), TEXT("session-root"));
|
||||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistVisionShellState BuildClassicCubeWebcamShellState(
|
||||
const FHyperTwistVisionSessionConfig& SessionConfig,
|
||||
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
|
||||
const FString& PreferredTargetFaceHint,
|
||||
const FString& PreferredCommittedFaceId
|
||||
)
|
||||
{
|
||||
FHyperTwistVisionShellState ShellState;
|
||||
ShellState.ShellProfileId = !SessionConfig.ShellProfileId.IsEmpty()
|
||||
? SessionConfig.ShellProfileId
|
||||
: TEXT("classic-cube-webcam-shell-v1");
|
||||
ShellState.LocaleCode = TEXT("en");
|
||||
ShellState.TargetSideCount = 6;
|
||||
ShellState.TargetFaceHint = !PreferredTargetFaceHint.IsEmpty()
|
||||
? NormalizeRecognitionFaceId(PreferredTargetFaceHint)
|
||||
: TEXT("F");
|
||||
ShellState.LastCommittedFaceId = !PreferredCommittedFaceId.IsEmpty()
|
||||
? NormalizeRecognitionFaceId(PreferredCommittedFaceId)
|
||||
: FString();
|
||||
ShellState.CalibrationProfileId = !SessionConfig.CalibrationProfile.IsEmpty()
|
||||
? SessionConfig.CalibrationProfile
|
||||
: SessionConfig.CalibrationProfileDefinition.ProfileId;
|
||||
|
||||
if (ReconstructionSession.IsStructurallyValid())
|
||||
{
|
||||
ShellState.ScannedSideCount = FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount);
|
||||
for (const FHyperTwistVisionCommittedFaceState& FaceState : ReconstructionSession.CommittedFaces)
|
||||
{
|
||||
const FString FaceId = NormalizeRecognitionFaceId(FaceState.FaceId);
|
||||
if (!FaceId.IsEmpty())
|
||||
{
|
||||
ShellState.CapturedFaceIds.Add(FaceId);
|
||||
}
|
||||
}
|
||||
|
||||
if ((PreferredTargetFaceHint.IsEmpty() || ShellState.TargetFaceHint.IsEmpty())
|
||||
&& ReconstructionSession.MissingFaces.Num() > 0)
|
||||
{
|
||||
ShellState.TargetFaceHint = NormalizeRecognitionFaceId(ReconstructionSession.MissingFaces[0]);
|
||||
}
|
||||
|
||||
if (ShellState.LastCommittedFaceId.IsEmpty() && ReconstructionSession.CommittedFaces.Num() > 0)
|
||||
{
|
||||
ShellState.LastCommittedFaceId =
|
||||
NormalizeRecognitionFaceId(ReconstructionSession.CommittedFaces.Last().FaceId);
|
||||
}
|
||||
}
|
||||
|
||||
return ShellState;
|
||||
}
|
||||
|
||||
EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId)
|
||||
{
|
||||
FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition;
|
||||
|
|
@ -6659,6 +6763,15 @@ FHyperTwistVisionPreviewResult UHyperTwistTrainingSubsystem::SubmitActiveRecogni
|
|||
{
|
||||
Result.PreviewState.State.SourceSessionId = NormalizedFrame.SessionId;
|
||||
}
|
||||
if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig))
|
||||
{
|
||||
Result.ShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeWebcamShellState(
|
||||
ActiveRecognitionSessionState.SessionConfig,
|
||||
ActiveRecognitionSessionState.ActiveReconstructionSession,
|
||||
!Result.ObservedFace.FaceId.IsEmpty() ? Result.ObservedFace.FaceId : NormalizedFrame.TargetFaceHint,
|
||||
FString()
|
||||
);
|
||||
}
|
||||
|
||||
ActiveRecognitionSessionState.bHasPreviewResult = true;
|
||||
ActiveRecognitionSessionState.LastPreviewResult = Result;
|
||||
|
|
@ -6790,6 +6903,15 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit
|
|||
Result.Snapshot = FHyperTwistStateSnapshot();
|
||||
}
|
||||
}
|
||||
if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig))
|
||||
{
|
||||
Result.ShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeWebcamShellState(
|
||||
ActiveRecognitionSessionState.SessionConfig,
|
||||
Result.ReconstructionSession,
|
||||
Result.ObservedFace.FaceId,
|
||||
Result.CommittedUnit
|
||||
);
|
||||
}
|
||||
|
||||
const FString ResolvedStage = !Result.CommittedUnit.IsEmpty()
|
||||
? Result.CommittedUnit
|
||||
|
|
@ -7719,6 +7841,26 @@ FHyperTwistVisionSessionConfig UHyperTwistTrainingSubsystem::BuildActiveRecognit
|
|||
{
|
||||
SessionConfig.CalibrationProfileDefinition.ProfileId = SessionConfig.CalibrationProfile;
|
||||
}
|
||||
if (SessionConfig.ShellProfileId.IsEmpty()
|
||||
&& SessionConfig.ShellProfileDefinition.IsStructurallyValid())
|
||||
{
|
||||
SessionConfig.ShellProfileId = SessionConfig.ShellProfileDefinition.ShellProfileId;
|
||||
}
|
||||
if (SessionConfig.ShellProfileId.IsEmpty())
|
||||
{
|
||||
SessionConfig.ShellProfileId = TEXT("classic-cube-webcam-shell-v1");
|
||||
}
|
||||
if (!SessionConfig.ShellProfileDefinition.IsStructurallyValid())
|
||||
{
|
||||
SessionConfig.ShellProfileDefinition =
|
||||
HyperTwistTrainingSubsystemInternal::BuildDefaultClassicCubeWebcamShellProfile(
|
||||
SessionConfig.ShellProfileId
|
||||
);
|
||||
}
|
||||
if (SessionConfig.ShellProfileDefinition.ShellProfileId.IsEmpty())
|
||||
{
|
||||
SessionConfig.ShellProfileDefinition.ShellProfileId = SessionConfig.ShellProfileId;
|
||||
}
|
||||
}
|
||||
|
||||
return SessionConfig;
|
||||
|
|
|
|||
|
|
@ -375,6 +375,162 @@ struct FHyperTwistVisionReconstructionSession
|
|||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionShellActionBinding
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ActionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString InputBinding;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SurfaceId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bEnabled = true;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !ActionId.IsEmpty() && !InputBinding.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionShellPanelLayout
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PanelId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PanelKind;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString AnchorId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 GridRows = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 GridColumns = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bVisibleByDefault = true;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !PanelId.IsEmpty() && !PanelKind.IsEmpty() && !AnchorId.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionShellProfile
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ShellProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ShellKind = TEXT("webcam-overlay");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LocaleRoutingMode = TEXT("first-party-locale-cycle");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistVisionShellPanelLayout> Panels;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistVisionShellActionBinding> ActionBindings;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (ShellProfileId.IsEmpty() || ShellKind.IsEmpty() || Panels.Num() <= 0 || ActionBindings.Num() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistVisionShellPanelLayout& Panel : Panels)
|
||||
{
|
||||
if (!Panel.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FHyperTwistVisionShellActionBinding& ActionBinding : ActionBindings)
|
||||
{
|
||||
if (!ActionBinding.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionShellState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ShellProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LocaleCode = TEXT("en");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ScannedSideCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TargetSideCount = 6;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString TargetFaceHint;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LastCommittedFaceId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CalibrationProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bPreviewPanelVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSnapshotPanelVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bScannedSideCounterVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCubeNetVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bLocaleIndicatorVisible = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCalibrationRailVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> CapturedFaceIds;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !ShellProfileId.IsEmpty()
|
||||
&& !LocaleCode.IsEmpty()
|
||||
&& !CalibrationProfileId.IsEmpty()
|
||||
&& ScannedSideCount >= 0
|
||||
&& TargetSideCount > 0;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionSessionConfig
|
||||
{
|
||||
|
|
@ -404,6 +560,12 @@ struct FHyperTwistVisionSessionConfig
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionCalibrationProfile CalibrationProfileDefinition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ShellProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionShellProfile ShellProfileDefinition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FIntPoint FrameSize = FIntPoint(1280, 720);
|
||||
|
||||
|
|
@ -467,6 +629,9 @@ struct FHyperTwistVisionPreviewResult
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionFaceObservation ObservedFace;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionShellState ShellState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Guidance;
|
||||
};
|
||||
|
|
@ -515,6 +680,9 @@ struct FHyperTwistVisionCommitResult
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionReconstructionSession ReconstructionSession;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionShellState ShellState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> Warnings;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,299 @@
|
|||
// Copyright HyperTwist, Inc. All Rights Reserved.
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
|
||||
#if WITH_AUTOMATION_TESTS
|
||||
|
||||
namespace HyperTwistQbrPhase6ROTestInternal
|
||||
{
|
||||
FHyperTwistTrainingDeck MakeRecognitionDeck()
|
||||
{
|
||||
FHyperTwistTrainingDeck Deck;
|
||||
Deck.DeckId = TEXT("phase6r-o/qbr-webcam-shell");
|
||||
Deck.Title = TEXT("Phase 6R-O Qbr Webcam Shell");
|
||||
Deck.DeliveryModes = {
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
};
|
||||
|
||||
FHyperTwistTrainingCase TrainingCase;
|
||||
TrainingCase.CaseId = TEXT("phase6r-o-case");
|
||||
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
|
||||
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition;
|
||||
TrainingCase.PromptLabel = TEXT("Phase 6R-O Recognition");
|
||||
TrainingCase.AllowedDeliveryModes = {
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
};
|
||||
Deck.Cases = {TrainingCase};
|
||||
return Deck;
|
||||
}
|
||||
|
||||
void ForceMockRecognitionClient(UHyperTwistTrainingSubsystem* TrainingSubsystem)
|
||||
{
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (FStrProperty* RecognitionClientKindProperty = FindFProperty<FStrProperty>(
|
||||
UHyperTwistTrainingSubsystem::StaticClass(),
|
||||
TEXT("RecognitionClientKind")
|
||||
))
|
||||
{
|
||||
RecognitionClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, TEXT("mock"));
|
||||
}
|
||||
}
|
||||
|
||||
const FHyperTwistVisionShellPanelLayout* FindPanel(
|
||||
const FHyperTwistVisionShellProfile& Profile,
|
||||
const FString& PanelId
|
||||
)
|
||||
{
|
||||
return Profile.Panels.FindByPredicate(
|
||||
[&PanelId](const FHyperTwistVisionShellPanelLayout& Candidate)
|
||||
{
|
||||
return Candidate.PanelId == PanelId;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const FHyperTwistVisionShellActionBinding* FindAction(
|
||||
const FHyperTwistVisionShellProfile& Profile,
|
||||
const FString& ActionId
|
||||
)
|
||||
{
|
||||
return Profile.ActionBindings.FindByPredicate(
|
||||
[&ActionId](const FHyperTwistVisionShellActionBinding& Candidate)
|
||||
{
|
||||
return Candidate.ActionId == ActionId;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistQbrPhase6ROWebcamShellSessionConfigTest,
|
||||
"HyperTwist.Permissive.Qbr.Phase6R.O.WebcamShellSessionConfig",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistQbrPhase6ROWebcamShellSessionConfigTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
UGameInstance* GameInstance = NewObject<UGameInstance>(GetTransientPackage());
|
||||
TestNotNull(TEXT("Game instance outer must exist for subsystem construction."), GameInstance);
|
||||
if (GameInstance == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
|
||||
TestNotNull(TEXT("Training subsystem must be constructed."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
HyperTwistQbrPhase6ROTestInternal::ForceMockRecognitionClient(TrainingSubsystem);
|
||||
|
||||
const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
|
||||
HyperTwistQbrPhase6ROTestInternal::MakeRecognitionDeck(),
|
||||
TEXT("phase6r-o-user"),
|
||||
TEXT("phase6r-o-session"),
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted
|
||||
);
|
||||
TestTrue(TEXT("The recognition-assisted run must start successfully."), RunState.IsStructurallyValid());
|
||||
if (!RunState.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString OpenError;
|
||||
TestTrue(TEXT("The recognition session must open for the qbr shell route."), TrainingSubsystem->OpenActiveRecognitionSession(OpenError));
|
||||
TestTrue(TEXT("Opening the recognition session must not report an error."), OpenError.IsEmpty());
|
||||
|
||||
const FHyperTwistTrainingRecognitionSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveRecognitionSessionState();
|
||||
TestEqual(
|
||||
TEXT("The qbr webcam shell route must normalize the first-party shell profile id."),
|
||||
SessionState.SessionConfig.ShellProfileId,
|
||||
TEXT("classic-cube-webcam-shell-v1")
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The qbr webcam shell route must provide a structurally valid shell profile definition."),
|
||||
SessionState.SessionConfig.ShellProfileDefinition.IsStructurallyValid()
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The shell profile must retain six bounded panels."),
|
||||
SessionState.SessionConfig.ShellProfileDefinition.Panels.Num(),
|
||||
6
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The shell profile must retain four bounded action bindings."),
|
||||
SessionState.SessionConfig.ShellProfileDefinition.ActionBindings.Num(),
|
||||
4
|
||||
);
|
||||
|
||||
const FHyperTwistVisionShellPanelLayout* PreviewPanel =
|
||||
HyperTwistQbrPhase6ROTestInternal::FindPanel(
|
||||
SessionState.SessionConfig.ShellProfileDefinition,
|
||||
TEXT("preview-sticker-grid")
|
||||
);
|
||||
TestNotNull(TEXT("The preview sticker grid panel must be present."), PreviewPanel);
|
||||
if (PreviewPanel != nullptr)
|
||||
{
|
||||
TestEqual(TEXT("The preview sticker grid must stay anchored top-left."), PreviewPanel->AnchorId, TEXT("top-left"));
|
||||
TestEqual(TEXT("The preview sticker grid must retain a 3x3 shape."), PreviewPanel->GridRows, 3);
|
||||
}
|
||||
|
||||
const FHyperTwistVisionShellPanelLayout* CubeNetPanel =
|
||||
HyperTwistQbrPhase6ROTestInternal::FindPanel(
|
||||
SessionState.SessionConfig.ShellProfileDefinition,
|
||||
TEXT("classic-cube-net")
|
||||
);
|
||||
TestNotNull(TEXT("The classic cube net panel must be present."), CubeNetPanel);
|
||||
if (CubeNetPanel != nullptr)
|
||||
{
|
||||
TestEqual(TEXT("The cube net must stay anchored bottom-right."), CubeNetPanel->AnchorId, TEXT("bottom-right"));
|
||||
TestEqual(TEXT("The cube net must retain the bounded 3x4 layout."), CubeNetPanel->GridColumns, 4);
|
||||
}
|
||||
|
||||
const FHyperTwistVisionShellActionBinding* SnapshotAction =
|
||||
HyperTwistQbrPhase6ROTestInternal::FindAction(
|
||||
SessionState.SessionConfig.ShellProfileDefinition,
|
||||
TEXT("capture-snapshot")
|
||||
);
|
||||
TestNotNull(TEXT("The snapshot action binding must be present."), SnapshotAction);
|
||||
if (SnapshotAction != nullptr)
|
||||
{
|
||||
TestEqual(TEXT("The snapshot action must keep the Space binding."), SnapshotAction->InputBinding, TEXT("Space"));
|
||||
}
|
||||
|
||||
const FHyperTwistVisionShellActionBinding* LocaleAction =
|
||||
HyperTwistQbrPhase6ROTestInternal::FindAction(
|
||||
SessionState.SessionConfig.ShellProfileDefinition,
|
||||
TEXT("cycle-locale")
|
||||
);
|
||||
TestNotNull(TEXT("The locale-cycle action binding must be present."), LocaleAction);
|
||||
if (LocaleAction != nullptr)
|
||||
{
|
||||
TestEqual(TEXT("The locale-cycle action must keep the L binding."), LocaleAction->InputBinding, TEXT("L"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistQbrPhase6ROWebcamShellPreviewStateTest,
|
||||
"HyperTwist.Permissive.Qbr.Phase6R.O.WebcamShellPreviewState",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistQbrPhase6ROWebcamShellPreviewStateTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
UGameInstance* GameInstance = NewObject<UGameInstance>(GetTransientPackage());
|
||||
TestNotNull(TEXT("Game instance outer must exist for subsystem construction."), GameInstance);
|
||||
if (GameInstance == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
|
||||
TestNotNull(TEXT("Training subsystem must be constructed."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
HyperTwistQbrPhase6ROTestInternal::ForceMockRecognitionClient(TrainingSubsystem);
|
||||
|
||||
const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
|
||||
HyperTwistQbrPhase6ROTestInternal::MakeRecognitionDeck(),
|
||||
TEXT("phase6r-o-user"),
|
||||
TEXT("phase6r-o-preview-session"),
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted
|
||||
);
|
||||
TestTrue(TEXT("The recognition-assisted run must start successfully."), RunState.IsStructurallyValid());
|
||||
if (!RunState.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FHyperTwistVisionFrameEnvelope Frame;
|
||||
Frame.TargetFaceHint = TEXT("F");
|
||||
const FHyperTwistVisionPreviewResult Result = TrainingSubsystem->SubmitActiveRecognitionFrame(Frame);
|
||||
|
||||
TestTrue(TEXT("The qbr preview shell state must be structurally valid."), Result.ShellState.IsStructurallyValid());
|
||||
TestEqual(TEXT("The preview shell must preserve the first-party shell profile id."), Result.ShellState.ShellProfileId, TEXT("classic-cube-webcam-shell-v1"));
|
||||
TestEqual(TEXT("The preview shell must start with no scanned sides."), Result.ShellState.ScannedSideCount, 0);
|
||||
TestEqual(TEXT("The preview shell must keep the six-side target."), Result.ShellState.TargetSideCount, 6);
|
||||
TestEqual(TEXT("The preview shell must preserve the current face hint."), Result.ShellState.TargetFaceHint, TEXT("F"));
|
||||
TestEqual(TEXT("The preview shell must preserve the classic default calibration profile id."), Result.ShellState.CalibrationProfileId, TEXT("classic-cube-default"));
|
||||
TestEqual(TEXT("The preview shell must preserve the default locale code."), Result.ShellState.LocaleCode, TEXT("en"));
|
||||
TestTrue(TEXT("The preview panel must remain visible."), Result.ShellState.bPreviewPanelVisible);
|
||||
TestTrue(TEXT("The snapshot panel must remain visible."), Result.ShellState.bSnapshotPanelVisible);
|
||||
TestTrue(TEXT("The cube net panel must remain visible."), Result.ShellState.bCubeNetVisible);
|
||||
TestTrue(TEXT("The locale indicator must remain visible."), Result.ShellState.bLocaleIndicatorVisible);
|
||||
TestFalse(TEXT("The calibration rail should stay hidden outside explicit calibration mode."), Result.ShellState.bCalibrationRailVisible);
|
||||
TestEqual(TEXT("The preview shell should not yet report captured faces."), Result.ShellState.CapturedFaceIds.Num(), 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistQbrPhase6ROWebcamShellCommitStateTest,
|
||||
"HyperTwist.Permissive.Qbr.Phase6R.O.WebcamShellCommitState",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistQbrPhase6ROWebcamShellCommitStateTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
UGameInstance* GameInstance = NewObject<UGameInstance>(GetTransientPackage());
|
||||
TestNotNull(TEXT("Game instance outer must exist for subsystem construction."), GameInstance);
|
||||
if (GameInstance == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
|
||||
TestNotNull(TEXT("Training subsystem must be constructed."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
HyperTwistQbrPhase6ROTestInternal::ForceMockRecognitionClient(TrainingSubsystem);
|
||||
|
||||
const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
|
||||
HyperTwistQbrPhase6ROTestInternal::MakeRecognitionDeck(),
|
||||
TEXT("phase6r-o-user"),
|
||||
TEXT("phase6r-o-commit-session"),
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted
|
||||
);
|
||||
TestTrue(TEXT("The recognition-assisted run must start successfully."), RunState.IsStructurallyValid());
|
||||
if (!RunState.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FHyperTwistVisionFrameEnvelope Frame;
|
||||
Frame.TargetFaceHint = TEXT("F");
|
||||
TrainingSubsystem->SubmitActiveRecognitionFrame(Frame);
|
||||
|
||||
FHyperTwistVisionCommitRequest Request;
|
||||
Request.TargetFace = TEXT("F");
|
||||
const FHyperTwistVisionCommitResult Result =
|
||||
TrainingSubsystem->CommitActiveRecognitionObservation(Request);
|
||||
|
||||
TestTrue(TEXT("The qbr commit shell state must be structurally valid."), Result.ShellState.IsStructurallyValid());
|
||||
TestEqual(TEXT("The committed shell must report one scanned side."), Result.ShellState.ScannedSideCount, 1);
|
||||
TestEqual(TEXT("The committed shell must preserve the six-side target."), Result.ShellState.TargetSideCount, 6);
|
||||
TestEqual(TEXT("The committed shell must preserve the last committed face."), Result.ShellState.LastCommittedFaceId, TEXT("F"));
|
||||
TestTrue(TEXT("The committed shell must retain the scanned-side counter surface."), Result.ShellState.bScannedSideCounterVisible);
|
||||
TestTrue(TEXT("The committed shell must retain the cube-net surface."), Result.ShellState.bCubeNetVisible);
|
||||
TestTrue(TEXT("The committed shell must track the committed face id."), Result.ShellState.CapturedFaceIds.Contains(TEXT("F")));
|
||||
TestEqual(TEXT("The committed shell must retain the classic default calibration profile id."), Result.ShellState.CalibrationProfileId, TEXT("classic-cube-default"));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -126,6 +126,8 @@ Status update on `2026-05-21`:
|
|||
- `HactarCE/Hyperspeedcube` remains partially incorporated
|
||||
- the bounded permissive `Phase 6R-B` `kkoomen/qbr` classic-cube recognition calibration and
|
||||
ordered face-observation packet is now landed in current code
|
||||
- the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in
|
||||
current code
|
||||
- `kkoomen/qbr` remains partially incorporated
|
||||
- the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face
|
||||
reconstruction and face-vote replacement packet is now landed in current code
|
||||
|
|
@ -149,8 +151,9 @@ Status update on `2026-05-21`:
|
|||
- the bounded permissive `Phase 6R-N` `HactarCE/Hyperspeedcube` puzzle-definition DSL packet is
|
||||
now landed in current code
|
||||
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families
|
||||
- the current next bounded move is a source-backed `Phase 6R-O` `kkoomen/qbr` webcam UI shell
|
||||
preparation/control pass, not a new restrictive packet by default
|
||||
- the current next bounded move is a source-backed `Phase 6R-P`
|
||||
`vivaansinghvi07/rubix-cube-solver` browser/webcam shell preparation/control pass, not a new
|
||||
restrictive packet by default
|
||||
- use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A`
|
||||
ownership contract for the current queue after that correction
|
||||
|
||||
|
|
@ -254,8 +257,9 @@ Current practical interpretation:
|
|||
- the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md`
|
||||
- the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md`
|
||||
- the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md`
|
||||
- the next bounded move is a source-backed `Phase 6R-O` `kkoomen/qbr` preparation/control pass
|
||||
for a bounded webcam UI shell seam
|
||||
- the next bounded move is a source-backed `Phase 6R-P`
|
||||
`vivaansinghvi07/rubix-cube-solver` preparation/control pass for a bounded browser/webcam shell
|
||||
seam
|
||||
|
||||
Companion docs:
|
||||
|
||||
|
|
@ -740,11 +744,11 @@ Approved working posture:
|
|||
- `Copyright (c) 2016 Kim Koomen`
|
||||
- keep the row as a partially incorporated recognition-substrate donor, not as a blanket promotion
|
||||
to full recognition-shell ownership
|
||||
- the landed packet is bounded to:
|
||||
- the landed packets are bounded to:
|
||||
- classic-cube recognition calibration contract
|
||||
- ordered face-observation contract
|
||||
- classic-cube webcam shell
|
||||
- keep the following families deferred to later queue decisions:
|
||||
- webcam UI shell
|
||||
- bundled-font redistribution and multilingual solve shell
|
||||
- multi-face reconstruction / correction / explanation
|
||||
- if later implementation copies bundled non-code assets, capture and preserve any asset-specific
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
# HyperTwist Phase 6R-O qbr webcam UI shell implementation packet
|
||||
|
||||
Created on `2026-05-22`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- bounded permissive `Phase 6R-O` implementation slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet lands the current retained `qbr` webcam shell family:
|
||||
|
||||
- first-party classic-cube webcam shell profile and shell-state composition
|
||||
|
||||
It is not:
|
||||
|
||||
- a full `qbr` row transplant
|
||||
- a multilingual solve shell packet
|
||||
- a bundled-font redistribution packet
|
||||
- a multi-face correction or explanation packet
|
||||
- a `rubix-cube-solver` browser-shell packet
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This implementation packet stands on:
|
||||
|
||||
- `docs/REPO_LICENSE_TRACKING.md`
|
||||
- `docs/ops/HYPERTWIST_CROSS_LANE_AUTHORITY_HIERARCHY_AND_RECONCILIATION_2026-05-20.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_PREPARATION_PACKET_2026-05-22.md`
|
||||
|
||||
The retained owner remains:
|
||||
|
||||
- `kkoomen/qbr`
|
||||
|
||||
The top-level product shell owner does not change here:
|
||||
|
||||
- first-party HyperTwist remains the broader recognition-session and product-shell owner
|
||||
- this packet keeps `kkoomen/qbr` bounded to the narrower `A1 / R1 / F2` classic-cube webcam
|
||||
shell slice
|
||||
|
||||
## Landed scope
|
||||
|
||||
The current code now owns a retained `qbr` webcam shell contract through:
|
||||
|
||||
- first-party `HyperTwistRecognition` contract types for:
|
||||
- shell action bindings
|
||||
- shell panel layout
|
||||
- shell profile definition
|
||||
- shell state readout
|
||||
- recognition session config defaults for:
|
||||
- first-party classic-cube webcam shell profile id
|
||||
- classic-cube webcam shell profile definition
|
||||
- first-party preview and commit result shaping for:
|
||||
- scanned-side count
|
||||
- captured-face ids
|
||||
- target-face hint
|
||||
- last committed face id
|
||||
- bounded shell panel visibility
|
||||
- sample contract routing in:
|
||||
- `UHyperTwistContractLibrary`
|
||||
- active subsystem normalization in:
|
||||
- `UHyperTwistTrainingSubsystem`
|
||||
- focused automation coverage in:
|
||||
- `HyperTwistQbrPhase6ROWebcamShellContractTest.cpp`
|
||||
|
||||
## Why this is still intentionally bounded
|
||||
|
||||
This packet lands the bounded webcam shell seam, but it does not widen into neighboring families or
|
||||
broader ownership.
|
||||
|
||||
Still excluded:
|
||||
|
||||
- bundled-font redistribution
|
||||
- multilingual solve shell ownership
|
||||
- multi-face reconstruction / correction / explanation ownership
|
||||
- broad browser shell or recommendation ownership
|
||||
- `rubix-cube-solver` browser/webcam shell ownership
|
||||
|
||||
## Validation
|
||||
|
||||
Build validation:
|
||||
|
||||
- `C:\Program Files\Epic Games\UE_5.7\Engine\Build\BatchFiles\Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE`
|
||||
|
||||
Focused automation validation:
|
||||
|
||||
- `C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject -unattended -nop4 -nosplash -NullRHI -log -stdout -FullStdOutLogOutput -AbsLog=C:\HyperTwist\UnrealHyperTwist\Saved\Logs\Phase6R-O-Qbr-Verify.log -ReportExportPath=C:\HyperTwist\UnrealHyperTwist\Saved\AutomationReports\Phase6R-O-Qbr-Verify -ExecCmds="Automation RunTests HyperTwist.Permissive.Qbr.Phase6R.O; Quit" -TestExit="Automation Test Queue Empty"`
|
||||
|
||||
Regression automation validation:
|
||||
|
||||
- `HyperTwist.Permissive.Qbr.Phase6R.B`
|
||||
- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.C`
|
||||
- `HyperTwist.Permissive.Hyperspeedcube.Phase6R.N`
|
||||
- `HyperTwist.CleanRoom.CubeDesk`
|
||||
|
||||
Expected covered tests:
|
||||
|
||||
- `WebcamShellSessionConfig`
|
||||
- `WebcamShellPreviewState`
|
||||
- `WebcamShellCommitState`
|
||||
- existing `qbr`, `rubix-cube-solver`, `Hyperspeedcube`, and `CubeDesk` regression suites
|
||||
|
||||
## Queue effect
|
||||
|
||||
This packet consumes the current `Phase 6R-O` implementation slice.
|
||||
|
||||
`kkoomen/qbr` remains partially incorporated:
|
||||
|
||||
- landed now:
|
||||
- classic-cube recognition calibration contract
|
||||
- ordered face-observation contract
|
||||
- classic-cube webcam shell
|
||||
- still deferred:
|
||||
- bundled-font redistribution and multilingual solve shell
|
||||
- multi-face correction / explanation ownership
|
||||
|
||||
The next clean move is:
|
||||
|
||||
- source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
|
||||
preparation/control pass
|
||||
|
||||
Keep the future sequencing guards visible:
|
||||
|
||||
- keep the `qbr` font guard explicit:
|
||||
- preserve separate confirmation or replacement before any shipping path redistributes
|
||||
`src/assets/arial-unicode-ms.ttf`
|
||||
- keep the `rubix-cube-solver` browser-shell widening narrow:
|
||||
- do not widen directly into bundled `twistysim.min.js` redistribution or broad solve
|
||||
explanation/recommendation ownership in the next packet
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
# HyperTwist Phase 6R-O qbr webcam UI shell preparation packet
|
||||
|
||||
Created on `2026-05-22`
|
||||
|
||||
## Status
|
||||
|
||||
- historical consumed preparation authority
|
||||
- bounded post-`Phase 6R-N` control slice
|
||||
- the first bounded implementation slice now lands separately after this packet
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet freezes the next widening order after the landed `Phase 6R-N` `HactarCE/Hyperspeedcube`
|
||||
puzzle-definition DSL slice.
|
||||
|
||||
The open task was:
|
||||
|
||||
- define the first bounded source-backed widening packet for `kkoomen/qbr` as the retained
|
||||
classic-cube webcam UI shell above the already landed calibration and ordered face-observation
|
||||
seam
|
||||
|
||||
It is not:
|
||||
|
||||
- a full `qbr` row transplant
|
||||
- a multilingual solve shell packet
|
||||
- a bundled-font redistribution packet
|
||||
- a multi-face reconstruction or correction packet
|
||||
- a browser-shell or recommendation-shell packet from `rubix-cube-solver`
|
||||
- a provider-backed recognition platform packet
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This preparation packet stands on already-closed authority:
|
||||
|
||||
- `docs/REPO_LICENSE_TRACKING.md`
|
||||
- `docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_IMPLEMENTATION_PACKET_2026-05-20.md`
|
||||
|
||||
The key accepted routing facts are:
|
||||
|
||||
- `kkoomen/qbr` remains the retained recognition-substrate donor for the bounded classic-cube
|
||||
webcam shell seam
|
||||
- the already landed `Phase 6R-B` slice owns:
|
||||
- classic-cube recognition calibration contract
|
||||
- ordered face-observation contract
|
||||
- the donor value now strongest for the next narrower slice is:
|
||||
- preview sticker-grid shell
|
||||
- snapshot sticker-grid shell
|
||||
- scanned-side counter shell
|
||||
- classic cube-net readout shell
|
||||
- locale-indicator and input-action shell semantics
|
||||
- ownership denied there is:
|
||||
- do not widen into bundled `arial-unicode-ms.ttf` redistribution
|
||||
- do not widen into multilingual solve text ownership
|
||||
- do not widen into multi-face reconstruction or correction ownership
|
||||
- do not let the row absorb the queued `rubix-cube-solver` browser/webcam shell
|
||||
|
||||
## Why this was the next packet
|
||||
|
||||
The earlier retained queue heads were already landed:
|
||||
|
||||
- `Phase 6R-A` `HactarCE/Hyperspeedcube`
|
||||
- `Phase 6R-B` `kkoomen/qbr`
|
||||
- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver`
|
||||
- `Phase 6R-D` `roice3/MagicTile`
|
||||
- `Phase 6R-E` `ggml-org/whisper.cpp`
|
||||
- `Phase 6R-F` `SYSTRAN/faster-whisper`
|
||||
- `Phase 6R-G` `rhasspy/piper`
|
||||
- `Phase 6R-H` `coqui-ai/TTS`
|
||||
- `Phase 6R-I` `roice3/Magic120Cell`
|
||||
- `Phase 6R-J` `roice3/MagicCube5D`
|
||||
- `Phase 6R-K` `HactarCE/Hyperspeedcube`
|
||||
- `Phase 6R-L` `HactarCE/Hyperspeedcube`
|
||||
- `Phase 6R-M` `HactarCE/Hyperspeedcube`
|
||||
- `Phase 6R-N` `HactarCE/Hyperspeedcube`
|
||||
|
||||
That advanced the live queue to:
|
||||
|
||||
- `Phase 6R-O` `kkoomen/qbr`
|
||||
|
||||
with the adjacent retained recognition row ordered behind it:
|
||||
|
||||
- `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver`
|
||||
|
||||
## Required result
|
||||
|
||||
The source-backed control pass for this packet is now complete.
|
||||
|
||||
The first actual `6R-O` implementation packet should:
|
||||
|
||||
- define one bounded retained shell slice from `qbr`
|
||||
- keep the slice inside the accepted classic-cube recognition-shell seam
|
||||
- widen only the first shell family that can stand on its own without dragging in bundled-font
|
||||
shipping, multilingual copy ownership, or browser-shell scope
|
||||
- explicitly state which neighboring retained rows stay closed in that packet
|
||||
|
||||
## Source-backed retained basis
|
||||
|
||||
The queue-head decision is now source-backed rather than README-only.
|
||||
|
||||
Inspected retained donor basis:
|
||||
|
||||
- `README.md`
|
||||
- `src/video.py`
|
||||
- `src/constants.py`
|
||||
- `src/helpers.py`
|
||||
- `src/config.py`
|
||||
|
||||
Inspected first-party receiving basis:
|
||||
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6RBCalibrationContractTest.cpp`
|
||||
|
||||
## Narrowed 6R-O slice decision
|
||||
|
||||
The first widening slice is fixed as:
|
||||
|
||||
1. classic-cube webcam shell profile and shell-state composition
|
||||
|
||||
not as the broader multilingual or correction shell.
|
||||
|
||||
That narrowed slice covers:
|
||||
|
||||
- first-party webcam shell profile for:
|
||||
- preview sticker grid
|
||||
- snapshot sticker grid
|
||||
- scanned-side counter
|
||||
- classic cube-net readout
|
||||
- locale indicator
|
||||
- calibration swatch rail as a bounded shell surface
|
||||
- first-party input-action contract for:
|
||||
- capture snapshot
|
||||
- toggle calibration
|
||||
- cycle locale
|
||||
- close session
|
||||
- first-party shell-state readout for:
|
||||
- scanned-side count
|
||||
- captured-face ids
|
||||
- target-face hint
|
||||
- last committed face id
|
||||
- bounded panel visibility
|
||||
- subsystem normalization and focused automation coverage
|
||||
|
||||
## Deferred neighboring families
|
||||
|
||||
The first `6R-O` implementation packet must keep these capability families closed:
|
||||
|
||||
- bundled-font redistribution
|
||||
- multilingual solve shell ownership
|
||||
- multi-face reconstruction / correction / explanation
|
||||
- browser/webcam shell ownership from `rubix-cube-solver`
|
||||
- bundled `twistysim.min.js` redistribution
|
||||
- broad recommendation or solve-explanation ownership
|
||||
|
||||
## Why this slice was first
|
||||
|
||||
- `qbr` is strongest for the compact webcam overlay shell that sits directly on top of the already
|
||||
landed calibration and ordered face-observation seam
|
||||
- current first-party recognition code already had the session, preview, and commit envelopes, but
|
||||
it did not yet own a richer shell profile and shell-state contract for that route
|
||||
- widening into multilingual copy or correction/explanation would prematurely absorb adjacent legal
|
||||
and routing concerns from `qbr` and `rubix-cube-solver`
|
||||
|
||||
## Out of scope for the first 6R-O packet
|
||||
|
||||
- bundled font asset shipping
|
||||
- multilingual solve guidance ownership
|
||||
- multi-face correction or explanation ownership
|
||||
- browser/webcam shell ownership beyond the bounded classic-cube webcam overlay
|
||||
- general recommendation shell widening
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- the packet records why the live queue advanced to `6R-O`
|
||||
- the packet records that the first `6R-O` widening slice is the classic-cube webcam shell only
|
||||
- the packet states exact out-of-scope families for the first `6R-O` pass
|
||||
- the packet keeps the `qbr` font-asset guard visible
|
||||
- the packet keeps the `rubix-cube-solver` browser-shell guard visible for the next packet
|
||||
|
||||
## Validation checklist
|
||||
|
||||
1. confirm the retained `qbr` seam narrows cleanly to a webcam shell profile and shell state
|
||||
2. confirm the next implementation packet is framed as bounded source-backed widening rather than a
|
||||
donor UI transplant
|
||||
3. confirm bundled-font and multilingual widening remain explicitly deferred
|
||||
|
||||
That is the packet.
|
||||
|
|
@ -94,10 +94,15 @@ The next bounded move is now:
|
|||
implementation packet is now landed in current code
|
||||
4. the bounded permissive `Phase 6R-N` `HactarCE/Hyperspeedcube` puzzle-definition DSL
|
||||
implementation packet is now landed in current code
|
||||
5. source-backed `Phase 6R-O` `kkoomen/qbr` webcam UI shell preparation/control pass is now the
|
||||
next bounded move
|
||||
6. keep it narrower than bundled-font redistribution, multilingual solve shell, and broader
|
||||
correction/explanation ownership in the same packet
|
||||
5. the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in
|
||||
current code
|
||||
6. source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
|
||||
preparation/control pass is now the next bounded move
|
||||
7. keep `Phase 6R-P` narrower than bundled `twistysim.min.js` redistribution and broader solve
|
||||
explanation/recommendation ownership in the same packet
|
||||
8. keep the `qbr` guard visible:
|
||||
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation
|
||||
or replacement in any future widening that would ship that asset
|
||||
|
||||
## Memory-specific sequencing rule
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice.
|
|||
| Publication/leaderboard projection and entitlement gating | Implemented now | landed `kash/cubedesk` bounded packets | Publication and entitlement are live first-party training surfaces. |
|
||||
| Local social challenge bundle | Implemented now | landed `kash/cubedesk` `Bound 5` | Challenge/social training slice is real, but bounded. |
|
||||
| Classic-cube calibration and ordered face observation | Implemented now | landed `qbr` packet | Calibration contract and ordered face-observation contract are live. |
|
||||
| Classic-cube webcam shell | Implemented now | landed `qbr` `Phase 6R-O` | First-party webcam shell profile, bounded overlay panels, and input-action contract are live. |
|
||||
| Committed-face reconstruction and final classic-net shaping | Implemented now | landed `rubix-cube-solver` packet | Reconstruction session boundary is live. |
|
||||
| Hyper puzzle catalog contract | Implemented now | landed `Hyperspeedcube` `Phase 6R-A` | Narrow retained hyper-puzzle catalog slice is live. |
|
||||
| Hyper notation and replay-log serialization boundary | Implemented now | landed `Hyperspeedcube` `Phase 6R-K` | Narrow retained notation and log-serialization slice is live. |
|
||||
|
|
@ -141,10 +142,12 @@ repo.
|
|||
| Feature | Status | Primary authority | Notes |
|
||||
|---|---|---|---|
|
||||
| Calibration profile and ordered face observation | Implemented now | `qbr` bounded packet | Live bounded recognition intake. |
|
||||
| Classic-cube webcam shell | Implemented now | `qbr` bounded packet | Live first-party webcam shell profile and shell-state composition above the bounded recognition intake. |
|
||||
| Committed-face reconstruction session | Implemented now | `rubix-cube-solver` bounded packet | Live bounded reconstruction seam. |
|
||||
| Provider-backed recognition service contract | Implemented now | first-party recognition client surfaces | First-party normalized recognition session boundary is real. |
|
||||
| Multilingual recognition guidance shell | Deep-source grounded retained | `qbr` retained remainder | Kept deferred until a font-safe and provenance-safe shipping path is explicit. |
|
||||
| Multi-face correction/explanation shell | Deep-source grounded retained | `qbr`, `rubix-cube-solver` retained remainder | Not yet widened into the live shell. |
|
||||
| Browser/webcam recognition shell | Deep-source grounded retained | `rubix-cube-solver` retained remainder | Retained, not shipped. |
|
||||
| Browser recognition shell | Deep-source grounded retained | `rubix-cube-solver` retained remainder | Retained, not shipped. |
|
||||
|
||||
### 3. Training, coaching, and progression cockpit
|
||||
|
||||
|
|
|
|||
|
|
@ -62,9 +62,10 @@ Canonical discovery surfaces for roadmap interpretation:
|
|||
- the generic source-backed `Phase 6R-B` `kkoomen/qbr` control pass is now consumed
|
||||
- the bounded permissive `Phase 6R-B` `kkoomen/qbr` classic-cube recognition calibration and
|
||||
ordered face-observation packet is now landed in current code
|
||||
- `kkoomen/qbr` remains partially incorporated; webcam UI shell, bundled-font redistribution,
|
||||
multilingual solve shell, and multi-face reconstruction/correction/explanation families stay
|
||||
deferred
|
||||
- the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in
|
||||
current code
|
||||
- `kkoomen/qbr` remains partially incorporated; bundled-font redistribution, multilingual solve
|
||||
shell, and multi-face reconstruction/correction/explanation families stay deferred
|
||||
- the generic source-backed `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` control pass is now
|
||||
consumed
|
||||
- the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face
|
||||
|
|
@ -115,8 +116,10 @@ Canonical discovery surfaces for roadmap interpretation:
|
|||
- the bounded permissive `Phase 6R-M` `HactarCE/Hyperspeedcube` stats-shape and solve-record
|
||||
packet is now landed in current code
|
||||
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families
|
||||
- the current next bounded move is a source-backed `Phase 6R-O` `kkoomen/qbr` webcam UI shell
|
||||
preparation/control pass for one bounded recognition-shell widening slice
|
||||
- the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in
|
||||
current code
|
||||
- the current next bounded move is a source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver`
|
||||
browser/webcam shell preparation/control pass for one bounded recognition-shell widening slice
|
||||
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
|
||||
waiting on another first-party packet
|
||||
|
||||
|
|
@ -178,8 +181,8 @@ Current routing truth:
|
|||
- active non-live implementation-board rows: `34`
|
||||
- retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9`
|
||||
|
||||
The next bounded move is a source-backed `Phase 6R-O`
|
||||
`kkoomen/qbr` webcam UI shell preparation/control pass.
|
||||
The next bounded move is a source-backed `Phase 6R-P`
|
||||
`vivaansinghvi07/rubix-cube-solver` browser/webcam shell preparation/control pass.
|
||||
|
||||
Queue interpretation after that packet:
|
||||
|
||||
|
|
@ -200,8 +203,10 @@ Queue interpretation after that packet:
|
|||
- landed:
|
||||
- classic-cube recognition calibration contract
|
||||
- ordered face-observation contract
|
||||
- classic-cube webcam UI shell
|
||||
- still deferred:
|
||||
- webcam UI shell
|
||||
- bundled-font redistribution and multilingual solve shell
|
||||
- multi-face correction / explanation ownership
|
||||
- `roice3/Magic120Cell` remains a partially landed row rather than a closed row:
|
||||
- landed:
|
||||
- dedicated `120-cell` family runtime profile
|
||||
|
|
@ -301,10 +306,10 @@ Queue interpretation after that packet:
|
|||
- viseme / gesture runtime integration
|
||||
- broad assistant-platform scope
|
||||
- the next queue shape is now:
|
||||
- `kkoomen/qbr` deferred recognition-shell remainder
|
||||
- `vivaansinghvi07/rubix-cube-solver` deferred browser/webcam shell remainder
|
||||
- the next bounded packet should stay narrow:
|
||||
- webcam UI shell preparation/control before widening into bundled-font redistribution,
|
||||
multilingual solve shell, or multi-face correction/explanation ownership
|
||||
- browser/webcam shell preparation/control before widening into bundled `twistysim.min.js`
|
||||
redistribution or broad solve explanation/recommendation ownership
|
||||
- keep the speech-input / voice sidecar legal sequencing guard visible:
|
||||
- keep code-license judgments separate from model, voice, and payload-license review
|
||||
- keep the provider-neutral speech-lane guard visible:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue