private void ConstrainingForce(ISnapper snapper, out float fingersConstraint, out float wristConstraint) { ISnapData snap = snapper.SnapData; fingersConstraint = wristConstraint = 0; if (snap == null) { return; } bool isSnapping = snapper.IsSnapping; if (isSnapping && snap.HandAlignment != HandAlignType.None) { fingersConstraint = snap.HandPose != null ? 1f : 0f; wristConstraint = 1f; } else if (snap.HandAlignment == HandAlignType.AttractOnHover) { fingersConstraint = snap.HandPose != null ? snapper.SnapStrength : 0f; wristConstraint = snapper.SnapStrength; } if (fingersConstraint >= 1f && !isSnapping) { fingersConstraint = 0; } if (wristConstraint >= 1f && !isSnapping) { wristConstraint = 0f; } }
private void UpdateHandPose(ISnapper snapper, float fingersConstraint, float wristConstraint) { ISnapData snap = snapper.SnapData; if (snap == null) { FreeFingers(); FreeWrist(); return; } if (fingersConstraint > 0f && snap.HandPose != null) { UpdateFingers(snap.HandPose, snapper.SnappingFingers(), fingersConstraint); _areFingersFree = false; } else { FreeFingers(); } if (wristConstraint > 0f) { Pose wristLocalPose = GetWristPose(snap.WorldSnapPose, snapper.WristToSnapOffset); Pose wristPose = Transformer.ToTrackingPose(wristLocalPose); _syntheticHand.LockWristPose(wristPose, wristConstraint); _isWristFree = false; } else { FreeWrist(); } }
/// <summary> /// This function is called for every FixedUpdate cycle. It moves the snapper /// position towards the target position, but allows it to rotate in the plane /// normal to the knob's rotation axis, which is assumed to be the local "up" direction /// of the snapping rotation reference transform. /// </summary> /// <param name="snapper">The snapper in snapping region.</param> public override void OnSnappedStay(ISnapper snapper) { SnapperDexmo snapperDexmo = snapper as SnapperDexmo; if (snapperDexmo == null) { base.OnSnappedStay(snapper); return; } Transform positionReference = GetSnappingPositionReference(snapper); Transform rotationReference = GetSnappingRotationReference(snapper); if (positionReference == null || rotationReference == null) { return; } Transform palmCenterTransform = snapperDexmo.PalmCenter; // Find the deltaRotation in world needed to align palmCenterTransform to rotationReference // in only y-axis (i.e. "up" direction). Quaternion deltaRotation = Quaternion.FromToRotation(palmCenterTransform.up, rotationReference.up); Quaternion palmCenterTargetRotation = deltaRotation * palmCenterTransform.rotation; // Now palm center's local "up" direction will coincide with rotationReference.up, // and can rotate around the "up" axis. // Move the entire hand root transform to the target position and rotation calculated from // the target position and rotation of the palm center. Miscellaneous.MoveParentTransformGivenChildTransform( snapperDexmo.HandRootTransform, palmCenterTransform, positionReference.position, palmCenterTargetRotation, HandRootPositionRelativeToPalmCenter, HandRootRotationRelativeToPalmCenter); }
public TestCaseCreator(IEventAggregator eventAggregator, SessionData sessionData, UiState uiState, ISnapper snapper) { this.sessionData = sessionData; this.uiState = uiState; this.snapper = snapper; eventAggregator.GetEvent <TestCaseEvent>().Subscribe(OnTestCase); }
public SketchView(SketchViewModel viewModel, UiState uiState, IEventAggregator eventAggregator, IUnityContainer container, ISnapper snapper, ILoggerFacade logger = null) : this() { this.logger = logger ?? new EmptyLogger(); DataContext = viewModel; viewModel.PropertyChanged += OnViewModelPropertyChanged; this.viewModel = viewModel; this.duplicateEditorFactory = new DuplicateEditorFactory(this); this.snapper = snapper; sketchModellingView = container.Resolve <SketchModellingView>( new DependencyOverride <SketchModellingViewModel>(viewModel.SketchModellingViewModel)); root3d.Children.Add(sketchModellingView); sketchImageView = container.Resolve <SketchImageView>( new DependencyOverride <SketchImageViewModel>(viewModel.SketchImageViewModel)); Grid.SetRow(sketchImageView, 2); sketchImageView.Margin = vpRoot.Margin; root.Children.Insert(2, sketchImageView); newPrimitiveDragStrategy = new PrimitiveDragStrategy(uiState, sketchModellingView, snapper); snappedDragStrategy = new SnappedDragStrategy(uiState, duplicateEditorFactory.Create(), eventAggregator); curveDragStrategy = new CurveDragStrategy(uiState, sketchImageView, selectionRectangle); assignDragStrategy = new AssignDragStrategy(uiState, primitiveCurvesRoot, sketchImageView, eventAggregator); eventAggregator.GetEvent <PrimitiveCurvesChangedEvent>().Subscribe(OnPrimitiveCurvesChanged); }
private void HandleSnapEnded(ISnapper snapper) { if (_currentSnapper == snapper) { _currentSnapper = null; } }
public SketchViewModel( IUnityContainer container, IEventAggregator eventAggregator, UiState uiState, SessionData sessionData, ISnapper snapper, IClassificationInference classificationInference, IUndoHistory undoHistory) { this.uiState = uiState; this.sessionData = sessionData; this.eventAggregator = eventAggregator; this.snapper = snapper; this.classificationInference = classificationInference; this.undoHistory = undoHistory; uiState.AddListener(this, () => uiState.SketchPlane); sessionData.AddListener(this, () => sessionData.SketchName); NewPrimitives = sessionData.NewPrimitives; SketchModellingViewModel = container.Resolve <SketchModellingViewModel>(); SketchImageViewModel = container.Resolve <SketchImageViewModel>(); eventAggregator.GetEvent <MarkFeatureEvent>().Subscribe(MarkFeatureHandler); eventAggregator.GetEvent <MarkSilhouetteEvent>().Subscribe(MarkSilhouetteHandler); }
public AnnotationsViewModel(IEventAggregator eventAggregator, SessionData sessionData, ILoggerFacade logger, ISnapper snapper, IUndoHistory undoHistory) : this() { this.eventAggregator = eventAggregator; this.sessionData = sessionData; this.logger = logger; this.snapper = snapper; this.undoHistory = undoHistory; Annotations = sessionData.Annotations; }
/// <summary> /// This function will be called for every FixedUpdate cycle when the snapper /// is in snapping region. It replaces the position and rotation of the snapper /// by the target position and rotation. /// </summary> /// <remarks> /// Even though snapper's transform is modified here, it will not "teleport" to /// the target position and rotation immediately. The UnityHandController will /// use Vector3.Lerp and Quaternion.Lerp whenever there is a sudden change /// of position and rotation of the hand models, so by setting the snapper transform /// position and rotation to target ones, the hand controller will handle the /// rest of transition movement. /// </remarks> /// <param name="snapper">The snapper in the snapping transition.</param> public virtual void OnSnappedStay(ISnapper snapper) { Transform positionReference = GetSnappingPositionReference(snapper); Transform rotationReference = GetSnappingRotationReference(snapper); if (positionReference == null || rotationReference == null) { return; } snapper.Transform.position = positionReference.position; snapper.Transform.rotation = rotationReference.rotation; }
/// <summary> /// Get the snapping rotation reference for hand models. /// </summary> /// <param name="snapper">The snapper in snapping region.</param> /// <returns>Transform of snapping position reference..</returns> public override Transform GetSnappingRotationReference(ISnapper snapper) { SnapperDexmo snapperDexmo = snapper as SnapperDexmo; if (snapperDexmo == null) { return(base.GetSnappingRotationReference(snapper)); } return(snapperDexmo.IsRight ? SnappingRotationReferenceList[0] : SnappingRotationReferenceList[1]); }
private void UpdateHand(ISnapper constrainingSnapper) { if (constrainingSnapper != null) { ConstrainingForce(constrainingSnapper, out float fingersConstraint, out float wristConstraint); UpdateHandPose(constrainingSnapper, fingersConstraint, wristConstraint); } else { FreeFingers(); FreeWrist(); } }
/// <summary> /// Check if the snapper is still moving towards the target snapping position. /// </summary> /// <remarks> /// The snapper is in transition when the distance between the snapper and /// the target snapping position is greater than _snappingTransitionDistanceMin. /// </remarks> /// <param name="snapper">The snapper that is in snapping region.</param> /// <returns>True if the snapper is still moving towards the target snapping /// position.</returns> public virtual bool CheckInSnappingTransition(ISnapper snapper) { Transform positionReference = GetSnappingPositionReference(snapper); if (positionReference == null) { return(false); } SnapperDexmo snapperDexmo = snapper as SnapperDexmo; Vector3 snapperPosition = snapperDexmo == null ? snapper.Transform.position : snapperDexmo.PalmCenter.position; float dist = Vector3.Distance(positionReference.position, snapperPosition); bool inSnappingTransition = dist >= _snappingTransitionDistanceMin; return(inSnappingTransition); }
/// <summary> /// This function will be called for every FixedUpdate cycle when snapper /// is in snapping region. It fixes the snapper's rotation to that of /// snapping rotation reference and allows the snapper to move between /// start position constraint and end position constraint. /// </summary> /// <param name="snapper">The snapper in the snapping region.</param> public override void OnSnappedStay(ISnapper snapper) { SnapperDexmo snapperDexmo = snapper as SnapperDexmo; if (snapperDexmo == null) { base.OnSnappedStay(snapper); return; } Transform positionReference = GetSnappingPositionReference(snapper); // Adjust snapping position according to the current position of the snapper. // Snapping position will only move in a linear section between start point and // end point. if (positionReference == null) { return; } float vectorProjectionValueNormalized = Miscellaneous.GetVectorProjectionValueNormalized( _startPositionConstraintTransform.position, _endPositionConstraintTransform.position, snapperDexmo.PalmCenter.position); positionReference.position = Vector3.Lerp( _startPositionConstraintTransform.position, _endPositionConstraintTransform.position, vectorProjectionValueNormalized); Transform rotationReference = GetSnappingRotationReference(snapper); if (rotationReference == null) { return; } // Move the entire hand root transform to the target position and rotation calculated from // the target position and rotation of the palm center. Miscellaneous.MoveParentTransformGivenChildTransform( snapperDexmo.HandRootTransform, snapperDexmo.PalmCenter, positionReference.position, rotationReference.rotation, HandRootPositionRelativeToPalmCenter, HandRootRotationRelativeToPalmCenter); }
/// <summary> /// This function will be called when snapper just enters the snapping /// region. It sets some variables for later use. /// </summary> /// <param name="snapper">The snapper that just enters the snapping region.</param> public override void OnSnappedEnter(ISnapper snapper) { SnapperDexmo snapperDexmo = snapper as SnapperDexmo; if (snapperDexmo == null) { base.OnSnappedEnter(snapper); return; } Transform handRootTransform = snapperDexmo.HandRootTransform; Transform palmCenter = snapperDexmo.PalmCenter; if (handRootTransform != null) { HandRootPositionRelativeToPalmCenter = palmCenter.InverseTransformPoint( handRootTransform.position); HandRootRotationRelativeToPalmCenter = Quaternion.Inverse(palmCenter.rotation) * handRootTransform.rotation; } }
/// <summary> /// Check if the snapper is still moving towards the target position. /// </summary> /// <remarks> /// The snapper is in transition when the distance between the snapper and /// the target snapping position is greater than _snappingTransitionDistanceMin. /// </remarks> /// <param name="snapper">The snapper in the snapping region.</param> /// <returns>True if snapper is still moving towards the target position.</returns> public override bool CheckInSnappingTransition(ISnapper snapper) { SnapperDexmo snapperDexmo = snapper as SnapperDexmo; if (snapperDexmo == null) { return(base.CheckInSnappingTransition(snapper)); } Transform positionReference = GetSnappingPositionReference(snapper); if (positionReference == null) { return(false); } float dist = Vector3.Distance(positionReference.position, snapperDexmo.PalmCenter.position); bool inSnappingTransition = dist >= SnappingTransitionDistanceMin; return(inSnappingTransition); }
/// <summary> /// This function will be called for every FixedUpdate cycle when snapper is /// in the snapping region. It finds the target snapping position and rotation /// for the hand model and move the hand towards it. /// </summary> /// <param name="snapper">The snapper in the snapping region.</param> public override void OnSnappedStay(ISnapper snapper) { SnapperDexmo snapperDexmo = snapper as SnapperDexmo; if (snapperDexmo == null) { base.OnSnappedStay(snapper); return; } Transform positionReference = GetSnappingPositionReference(snapper); Transform rotationReference = GetSnappingRotationReference(snapper); if (positionReference == null || rotationReference == null) { return; } Miscellaneous.MoveParentTransformGivenChildTransform( snapperDexmo.HandRootTransform, snapperDexmo.PalmCenter, positionReference.position, rotationReference.rotation, HandRootPositionRelativeToPalmCenter, HandRootRotationRelativeToPalmCenter); }
public PrimitiveDragStrategy(UiState uiState, SketchModellingView sketchModellingView, ISnapper snapper) : base(uiState) { this.sketchModellingView = sketchModellingView; this.snapper = snapper; }
/// <summary> /// Get the snapping rotation reference according to the snapper type. /// </summary> /// <remarks> /// Currently, snappers are for hands, but in the future, it may extend /// to other types, so this function can be overridden to support other /// types of snappers. /// </remarks> /// <param name="snapper">The snapper that is in the snapping region.</param> /// <returns>Transform of snapping rotation reference.</returns> public virtual Transform GetSnappingRotationReference(ISnapper snapper) { return(_snappingRotationReferenceList[0]); }
/// <summary> /// This function will be called when the snapper just enters the snapping /// region. It is empty now, but can be overridden by derived classes. /// </summary> /// <param name="snapper">The snapper in the snapping region.</param> public virtual void OnSnappedEnter(ISnapper snapper) { }
/// <summary> /// This function is called when the snapper exits the snapping region. /// It is empty now, but can be overridden by derived classes. /// </summary> /// <param name="snapper">The snapper in snapping region.</param> public virtual void OnSnappedExit(ISnapper snapper) { }
private void HandleSnapStarted(ISnapper snapper) { _currentSnapper = snapper; }
private async Task StartInternal(RecordSettings settings) { try { try { Thread.CurrentThread.Priority = ThreadPriority.Highest; } catch (Exception ex) { } _stopwatch = _stopwatch ?? new Stopwatch(); _stopwatch.Reset(); _stopwatch.Start(); _stopWaiter.Reset(); if (!Directory.Exists(settings.OutputPath)) { Directory.CreateDirectory(settings.OutputPath); } const double second = 1000; const double minute = second * 60; const int quant = 10; //timeouts don't include process switching var inputSnapInterval = (int)Math.Max((settings.Realtime ? second / settings.Fps : settings.Interval), 0); //snap every N ms var splitInterval = settings.SplitInterval * minute / inputSnapInterval; //split every N frames var inputExpectedFps = inputSnapInterval > 0 ? second / inputSnapInterval : 0; var sourceRect = settings.CaptureRectangle; var framesWritten = 0L; using (var tmr = new Timer()) { tmr.Interval = 100; tmr.Elapsed += (a, b) => settings.OnFrameWritten(_stopwatch.Elapsed); tmr.Start(); while (Recording) { var outfile = Path.Combine(settings.OutputPath, DateTime.Now.ToFileTime().ToString() + ".avi"); using (var outstream = new VideoFileWriter()) { outstream.Open(outfile, sourceRect.Width, sourceRect.Height, settings.Fps, settings.Codec, settings.Bitrate); using (ISnapper snapper = GetSnapper(settings)) { using (var processingSemaphore = new SemaphoreSlim(snapper.MaxProcessingThreads)) { using (var writeSemaphore = new SemaphoreSlim(1)) { snapper.SetSource(sourceRect); var dropNextNFrames = 0; var lastSyncFrames = framesWritten; double lastSyncTime = _stopwatch.ElapsedMilliseconds; var emptyFramesSinceLastSync = 0; var crashedFramesSinceLastSync = 0; var slowFramewsSinceLastSync = 0; for (var i = 0L; (splitInterval == null || i < splitInterval) && Recording; i++) { Task tsk = null; Bitmap currentFrame = null; try { framesWritten++; //drop frame if required if (dropNextNFrames > 0 && currentFrame != null) { dropNextNFrames--; lastSyncTime = _stopwatch.ElapsedMilliseconds; lastSyncFrames = framesWritten; outstream.WriteVideoFrame(currentFrame); continue; } tsk = Task.Delay(inputSnapInterval - quant); /* * these bitmaps are actually the same object or null -> we only have to dispose it once */ var elapsedBeforeCurrentSnap = _stopwatch.Elapsed.TotalMilliseconds; await processingSemaphore.WaitAsync().ConfigureAwait(false); var tmp = await snapper.Snap(inputSnapInterval).ConfigureAwait(false); var elapsedAfterCurrentSnap = _stopwatch.Elapsed.TotalMilliseconds; if (elapsedAfterCurrentSnap - elapsedBeforeCurrentSnap > inputSnapInterval) { //Debug.WriteLine($"[F**K] slow snap: {elapsedAfterCurrentSnap - elapsedBeforeCurrentSnap}ms"); slowFramewsSinceLastSync++; } if (tmp == null) { //Debug.WriteLine("[F**K] empty snap"); emptyFramesSinceLastSync++; } currentFrame = tmp ?? currentFrame; //Debug.WriteLine($"[SNAP] {_stopwatch.ElapsedMilliseconds} ms"); //settings.OnFrameWritten?.Invoke(_stopwatch.Elapsed); } catch (Exception ex) { //Debug.WriteLine($"[F**K] crashed on snap: {ex.Message}"); crashedFramesSinceLastSync++; } try { Task.Run(async() => { try { if (currentFrame != null) {//offload to separate thread PreprocessFrame(currentFrame, settings); await writeSemaphore.WaitAsync().ConfigureAwait(false); try { outstream.WriteVideoFrame(currentFrame); } finally { writeSemaphore.Release(); } } } finally { processingSemaphore.Release(); } }).ConfigureAwait(false); } catch (Exception ex) { //todo: crashed frame logging } double elapsedNow = _stopwatch.ElapsedMilliseconds; var elapsedSinceLastSync = elapsedNow - lastSyncTime; if (elapsedSinceLastSync >= second) { var framesSinceLastSync = framesWritten - lastSyncFrames; //only relevant for realtime+ recordings var recentFps = framesSinceLastSync * second / elapsedSinceLastSync; var recentFpsDelta = recentFps - inputExpectedFps; Console.WriteLine($"{framesSinceLastSync} frames({emptyFramesSinceLastSync} empty, {crashedFramesSinceLastSync} crashed, {slowFramewsSinceLastSync} slow) in last {elapsedSinceLastSync.ToString("F")} ms ({recentFps.ToString("F")} fps). Total FPS: {(framesWritten * second / elapsedNow).ToString("F")}. Expected: {inputExpectedFps.ToString("F")}"); #if !PERF if (recentFpsDelta > 1) //faster than expected && at least one actual frame { tsk.Wait(); //wait for the current loop //Debug.WriteLine($"[F**K] Slow down, fella: {recentFpsDelta} frames"); Task.Delay((int)(inputSnapInterval * recentFpsDelta - quant)).Wait(); } else if (recentFpsDelta < -1)//too slow { dropNextNFrames = -(int)recentFpsDelta; //Debug.WriteLine($"[F**K] dropping {dropNextNFrames} frames"); } #endif lastSyncFrames = framesWritten; lastSyncTime = elapsedNow; emptyFramesSinceLastSync = 0; crashedFramesSinceLastSync = 0; slowFramewsSinceLastSync = 0; } #if !PERF tsk?.Wait(); #endif } await writeSemaphore.WaitAsync().ConfigureAwait(false); } } } } } tmr.Stop(); } } catch (Exception ex) { //Debug.WriteLine(ex.Message); //global } finally { _stopwatch?.Stop(); _stopWaiter.Set(); } }