/// <summary> /// Method clears value of /// *<see cref="numberOfPointersPressed">numberOfPointersPressed</see> /// *<see cref="distanceBetweenPoints">distanceBetweenPoints</see> /// *<see cref="angleBetweenVectors">angleBetweenVectors</see> /// *<see cref="gestureDirection">gestureDirection</see> /// </summary> public void PointersReleased() { numberOfPointersPressed = 0; distanceBetweenPoints = 0; angleBetweenVectors = 0; gestureDirection = GestureDirection.None; }
public MobileGesture(dynamic jsonObject) { ImgID = 0; //No image, default Shape = jsonObject["Shape"]; switch ((string)jsonObject["Type"]) { case "Throw": Type = GestureType.Throw; break; case "Pinch": Type = GestureType.Pinch; break; case "Tilt": Type = GestureType.Tilt; break; case "Swipe": Type = GestureType.Swipe; break; default: break; } switch ((string)jsonObject["Direction"]) { case "Pull": case "pull": Direction = GestureDirection.Pull; break; case "Push": case "push": Direction = GestureDirection.Push; break; default: break; } Timestamp = DateTime.Now; //for field study if (jsonObject.Property("ImgID") != null) { ImgID = jsonObject["ImgID"]; } }
public VideoWindow(GestureDirection direction, GestureType type) { InitializeComponent(); this.Title = type + " " + direction; vlc = new AxVLCPlugin2(); formHost.Child = vlc; vlc.CreateControl(); var videoPath = CreateUriTo(type, direction); MoveScreen(true); GestureParser.Pause(true); vlc.playlist.add(videoPath); vlc.playlist.play(); EventHandler handler = null; handler = (sender, e) => { vlc.MediaPlayerEndReached -= handler; GestureParser.Pause(false); MoveScreen(false); canvasWindow.Activate(); vlc.playlist.play(); vlc.MediaPlayerEndReached += (senderI, eI) => { vlc.playlist.play(); }; }; vlc.MediaPlayerEndReached += handler; }
public AttemptInfo(List<Test> tests, GestureDirection direction) { foreach (var t in DataGenerator.AllTechniques) { HitPercentage.Add(t, new double[18]); TimeTaken.Add(t, new double[18]); Accuracy.Add(t, new double[18]); } foreach (var technique in DataGenerator.AllTechniques) { foreach (var test in tests) { var attempts = test.Attempts[technique].Where(x => x.Direction == direction).ToList(); attempts.Sort((x, y) => x.AttemptNumber.CompareTo(y.AttemptNumber)); int count = 0; foreach (var attempt in attempts) { HitPercentage[technique][count] += attempt.Hit ? 1 : 0; TimeTaken[technique][count] += attempt.Time.TotalSeconds; Accuracy[technique][count] += MathHelper.GetDistance(attempt); count++; } } } foreach (var t in DataGenerator.AllTechniques) { for (int i = 0; i < 18; i++) { HitPercentage[t][i] /= tests.Count; TimeTaken[t][i] /= tests.Count; Accuracy[t][i] /= tests.Count; } } }
private static string CreateUriTo(GestureType type, GestureDirection direction) { string videoDirectory = @"techniques/" + direction.ToString() + "_" + type.ToString() + ".mp4"; string path = Path.Combine(new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, videoDirectory); return(new Uri(path).AbsoluteUri); }
public static GestureDirection GetDirection(this GestureSample gesture) { GestureDirection direction = GestureDirection.None; if (gesture.Delta.Y != 0) { if (gesture.Delta.Y > 0) { direction = GestureDirection.Down; } else { direction = GestureDirection.Up; } } else if (gesture.Delta.X != 0) { if (gesture.Delta.X > 0) { direction = GestureDirection.Right; } else { direction = GestureDirection.Left; } } return(direction); }
private GestureDirection GetFingerEntryGestureDirection(FingerEntry fingerEntry, float minimumComponentValue) { GestureDirection gestureDirection = GestureDirection.None; Vector3 startEnd = fingerEntry.GetStartEndDifference(); if (startEnd.x > minimumComponentValue) { gestureDirection = GestureDirection.Right; } else if (startEnd.x < -minimumComponentValue) { gestureDirection = GestureDirection.Left; } else if (startEnd.y < -minimumComponentValue) { gestureDirection = GestureDirection.Down; } else if (startEnd.y > minimumComponentValue) { gestureDirection = GestureDirection.Up; } else if (startEnd.z > minimumComponentValue) { gestureDirection = GestureDirection.Forward; } else if (startEnd.z < -minimumComponentValue) { gestureDirection = GestureDirection.Back; } return(gestureDirection); }
public bool ChangeGesture() { if (gestureTypeList.Count == 0 && firstDirectionRun) { GestureDirection direction = GestureParser.GetDirectionContext() == GestureDirection.Pull ? GestureDirection.Push : GestureDirection.Pull; StartTest(direction); done = true; return(true); } practiceDone = false; targetSequence = Target.GetNextSequence(); practiceSequence = Target.GetPracticeTargets(); practiceSequence.Enqueue(targetSequence.Dequeue()); board.Clear(); board.StartNewGesture(); board.CreateTarget(practiceSequence.Dequeue()); GestureParser.SetTypeContext(gestureTypeList.Dequeue()); Logger.CurrentLogger.StartPracticeTime(GestureParser.GetTypeContext(), GestureParser.GetDirectionContext()); VideoWindow.PlayVideo(GestureParser.GetDirectionContext(), GestureParser.GetTypeContext()); Console.WriteLine($"Changed to gesture: {GestureParser.GetTypeContext()} {GestureParser.GetDirectionContext()}"); return(true); }
public void StartTest(GestureDirection direction) { String test = direction == GestureDirection.Pull ? "startpull" : "startpush"; sw.WriteLine(test); sw.Flush(); }
private void RunForceEvent(GestureDirection _gesture) { if (_gesture == GestureDirection.Forward) { RunEvent(onForceYankForward); } if (_gesture == GestureDirection.Backward) { RunEvent(onForceYankBackward); } if (_gesture == GestureDirection.Up) { RunEvent(onForceYankUp); } if (_gesture == GestureDirection.Down) { RunEvent(onForceYankDown); } if (_gesture == GestureDirection.Right) { RunEvent(onForceYankRight); } if (_gesture == GestureDirection.Left) { RunEvent(onForceYankLeft); } if (_gesture == GestureDirection.None) { Debug.LogError("Force Gesture did something wrong"); } }
/// <summary> /// Log that new gesture test has started /// </summary> /// <param name="gestureType"></param> /// <param name="gestureDirection"></param> public void StartNewgestureTest(GestureType gestureType, GestureDirection gestureDirection) { string message = "Started new gesture test." + " Type: " + gestureType.ToString() + " Direction: " + gestureDirection.ToString(); Log(message); }
public void StartPracticeTime(GestureType type, GestureDirection direction) { string message = "Started new gesture practice." + " Type: " + type.ToString() + " Direction: " + direction.ToString(); Log(message); }
public KinectGesture(string shape) { Shape = shape; Type = GestureParser.GetTypeContext(); Direction = GestureParser.GetDirectionContext(); Pointer = CanvasWindow.GetCurrentPoint(); Timestamp = DateTime.Now; }
private void RaiseGestureReceived(GestureDirection direction) { var handler = GestureReceived; if (handler == null) { return; } handler(this, direction); }
public bool CheckDirection(GestureDirection gestureDirectionToCheck) { if (GestureDirection == GestureDirection.TAP) { return(gestureDirectionToCheck == GestureDirection); } else { return(gestureDirectionToCheck.HasFlag(GestureDirection)); } }
/// <summary> /// Method calculates: /// *distance between start and end points /// *angle between (start and end points) vector and (start + [50, 0]) vector /// and sets value of <see cref="gestureDirection">gestureDirection</see> /// </summary> public void Calculate() { distanceBetweenPoints = CalculateDistance(); angleBetweenVectors = CalculateAngle(); if (numberOfPointersPressed >= 2) { gestureDirection = DetermineMotionDirectionDoubleTouch(); } else if (numberOfPointersPressed == 1) { gestureDirection = DetermineMotionDirectionSingleTouch(); } else { gestureDirection = GestureDirection.None; } }
public Attempt(string id, int attemptNumber, string attemptLine, TimeSpan time, GridSize size, GestureDirection direction, GestureType type, DataSource source) { Valid = true; Size = size; Direction = direction; Source = source; AttemptNumber = attemptNumber; // 0 1 2 3 4 5 6 7 8 9 //[15:59:47]: Target: Hit Shape: Correct TC: (07,02) CC: (07, 02) JL: Short Pointer position: (1054,1,384,9). ID = id; string[] para = attemptLine.Trim().Split('[', ']')[1].Split(':'); Time = time; string[] info = attemptLine.Split(':'); Hit = info[4].Split(' ')[1] == "Hit"; Shape = info[5].Split(' ')[1] == "Correct"; TargetCell = GetPoint(info[6]); CurrentCell = GetPoint(info[7]); Pointer = GetPoint(info[9]); Type = type; }
public void Init() { if (IsInit) { return; } IsInit = true; IsTouchDown = false; StartTouchPoint = Vector3.zero; FirstTouchPoint = Vector3.zero; CurGO = null; ButtonDownGO = null; HomeBtnDownTime = -1; TriggerDownTime = -1; SpaceTime = 1.2f; IsGesture = false; GestureThreshold = 0.1f; CurGestureDir = GestureDirection.Up; CurGestureDirSlip = GestureDirection.Up; }
public static float ComputeGestureDirectionLen(GestureDirection gestureDir, Vector2 v) { float len = 0; if (gestureDir == GestureDirection.Up) { len = GetProjLen(v, Vector3.up); } else if (gestureDir == GestureDirection.Down) { len = GetProjLen(v, Vector3.down); } else if (gestureDir == GestureDirection.Left) { len = GetProjLen(v, Vector3.left); } else if (gestureDir == GestureDirection.Right) { len = GetProjLen(v, Vector3.right); } return(len); }
/// <summary> /// Checks if any of finger entries has tip position that moved for a given /// </summary> private void CheckFingerEntries() { foreach (KeyValuePair <int, FingerEntry> keyValuePair in FingerEntries) { FingerEntry entry = keyValuePair.Value; GestureDirection gestureDirection = GetFingerEntryGestureDirection(entry, FixedAmountFromDetectionOneDirection); if (gestureDirection != GestureDirection.None) { List <GestureDirection> gestureDirections = new List <GestureDirection>() { gestureDirection }; entry.Clear(); // search for any other that have the smaller minimum component value and quit the search foreach (KeyValuePair <int, FingerEntry> keyValuePair2 in FingerEntries) { if (keyValuePair.Key != keyValuePair2.Key) { GestureDirection gestureDirection2 = GetFingerEntryGestureDirection(keyValuePair2.Value, FixedAmountFromDetectionOneDirection - FixedAmountFromDetectionSecondDirectionDifference); if (gestureDirection2 != GestureDirection.None) { gestureDirections.Add(gestureDirection2); keyValuePair2.Value.Clear(); } } } Debug.Log("First gesture: " + gestureDirections[0].ToString() + " of " + gestureDirections.Count + " fingers."); return; } } }
public AttemptInfo(List <Test> tests, GestureDirection direction) { foreach (var t in DataGenerator.AllTechniques) { HitPercentage.Add(t, new double[18]); TimeTaken.Add(t, new double[18]); Accuracy.Add(t, new double[18]); } foreach (var technique in DataGenerator.AllTechniques) { foreach (var test in tests) { var attempts = test.Attempts[technique].Where(x => x.Direction == direction).ToList(); attempts.Sort((x, y) => x.AttemptNumber.CompareTo(y.AttemptNumber)); int count = 0; foreach (var attempt in attempts) { HitPercentage[technique][count] += attempt.Hit ? 1 : 0; TimeTaken[technique][count] += attempt.Time.TotalSeconds; Accuracy[technique][count] += MathHelper.GetDistance(attempt); count++; } } } foreach (var t in DataGenerator.AllTechniques) { for (int i = 0; i < 18; i++) { HitPercentage[t][i] /= tests.Count; TimeTaken[t][i] /= tests.Count; Accuracy[t][i] /= tests.Count; } } }
public SwipeGestureVO(GestureDirection gestureDirection, Vector2D gesturePoint) { GestureDirection = gestureDirection; GesturePoint = gesturePoint; }
public void StartTest(GestureDirection direction) { GestureParser.SetDirectionContext(direction); gestureTypeList = GetRandomGestureList(); ChangeGesture(); }
public static void SetDirectionContext(GestureDirection direction) { connection?.StartTest(direction); directionContext = direction; }
public virtual void TouchPadSlipUnifiedDirEnd(GestureDirection gestureDir) { //LogTool.Log("滑动结束手势方向:" + gestureDir); }
public virtual void TouchPadEveryoneSlipUnifiedDirSlip(GestureDirection gestureDir, float f) { //LogTool.Log("滑动中手势方向:" + gestureDir + " f:" + f); }
public KinectGesture(GestureType type, GestureDirection direction) : this(null) { Type = type; Direction = direction; }
private static string CreateUriTo(GestureType type, GestureDirection direction) { string videoDirectory = @"techniques/" + direction.ToString() + "_" + type.ToString() + ".mp4"; string path = Path.Combine(new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, videoDirectory); return new Uri(path).AbsoluteUri; }
public void PointAt(double xFromMid, double yFromMid) { if (pointerFigure == null) { pointerFigure = ShapeFactory.CreatePointer(accuracyTest); canvas.Children.Add(pointerFigure); Canvas.SetZIndex(pointerFigure, 10000); xPoint = xFromMid; yPoint = yFromMid; } if (target != null) { target.GridCell.Fill = targetColor; } if (extraTarget != null) { extraTarget.GridCell.Fill = targetColor; } DrawNextTargets(); if (AttemptRepository.SaveStatus == DatabaseSaveStatus.Saving && !savingToDB) { savingToDB = true; ShowStatusMessage("Saving to database..."); } if (savingToDB && AttemptRepository.SaveStatus != DatabaseSaveStatus.Saving) { savingToDB = false; string status = AttemptRepository.SaveStatus == DatabaseSaveStatus.Failed ? "Failed!" : "Success!"; ShowStatusMessage(status); Background = AttemptRepository.SaveStatus == DatabaseSaveStatus.Failed ? Brushes.Red : Brushes.Blue; } Point currentGyroPoint = new Point(GyroPositionX, -GyroPositionY); if (currentGyroPoint != lastGyroPoint) { lastGyroPoint = new Point(GyroPositionX, -GyroPositionY); } xPoint = xFromMid; yPoint = yFromMid; if (!lockedPointer) { pointer = GetPoint(xPoint, yPoint); } MoveShape(pointerFigure, pointer); if (!accuracyTest) { ColorCell(pointer); } KinectGesture gesture = GestureParser.AwaitingGesture; if (runningTest && runningGesture) { if (gesture != null) { UnlockPointer(); GestureParser.Pause(true); Cell currCell = GetCell(pointer); bool hit = currCell == target; bool correctShape = true; string shape = target.Shape is Ellipse ? "circle" : "square"; GestureDirection direction = GestureParser.GetDirectionContext(); GestureType type = GestureParser.GetTypeContext(); if (direction == GestureDirection.Push) { correctShape = shape == gesture.Shape; } currentTest.TargetHit(hit, correctShape, target, pointer, currCell, currentLength); if (hit && !correctShape) { hit = false; } TargetHit(target, hit); } } ExtendedDraw(gesture); }
public SwipeEventArgs(GestureDirection direction) { Direction = direction; }
public AttemptInfo(Test test, GestureDirection direction) : this(new List<Test>() {test}, direction) { }
void GvrInputMessage() { //if (GvrPtInputModule == null) //{ // LogTool.Log("警告:GvrPtInputModule为空"); // return; //} GameObject hitGO = GvrPointerInputModule.CurrentRaycastResult.gameObject; //新版本为GvrPointerInputModule.CurrentRaycastResult.gameObject if (GvrControllerInput.AppButtonDown) //新版本为GvrControllerInput { IsTouchDown = false; if (AppButtonPressCallback != null) { AppButtonPressCallback(true); } } else if (GvrControllerInput.AppButtonUp) { if (AppButtonPressCallback != null) { AppButtonPressCallback(false); } } if (GvrControllerInput.TriggerButtonDown) { IsTouchDown = false; } if (GvrControllerInput.HomeButtonDown) { //LogTool.Log("GVR HomeBtn Down"); IsTouchDown = false; HomeBtnDownTime = Time.time; } if (HomeBtnDownTime != -1) { if (GvrControllerInput.HomeButtonState)//长按 { if (Time.time - HomeBtnDownTime >= SpaceTime) { LogTool.Log("GVR HomeBtn LongPress"); HomeBtnDownTime = -1; if (HoumeButtonLongPressCallback != null) { HoumeButtonLongPressCallback(Camera.main.transform.forward); } } } //else//这里松开 //{ // LogTool.Log("GVR HomeBtn Up"); // if (HomeButtonUpCallback != null) // HomeButtonUpCallback(); // HomeBtnDownTime = -1; //} } NoloRecenter(); if (GvrControllerInput.Recentered) { if (RecenteredCallback != null) { RecenteredCallback(Camera.main.transform.forward); } } if (GvrControllerInput.IsTouching) { if (TouchPadPosCenteredCallback != null) { Vector2 touchPosVec = new Vector2(GvrControllerInput.TouchPos.x, -GvrControllerInput.TouchPos.y); Vector2 offset = new Vector2(-0.5f, 0.5f); touchPosVec = touchPosVec + offset; TouchPadPosCenteredCallback(touchPosVec * 2);//GvrControllerInput.TouchPosCentered } } if (GvrControllerInput.TouchDown) { IsTouchDown = true; StartTouchPoint = GvrControllerInput.TouchPos; FirstTouchPoint = GvrControllerInput.TouchPos; if (TouchPadTouchCallback != null) { TouchPadTouchCallback(true); } } else if (GvrControllerInput.TouchUp) { IsTouchDown = false; StartTouchPoint = Vector2.zero; FirstTouchPoint = Vector2.zero; if (TouchPadTouchCallback != null) { TouchPadTouchCallback(false); } if (IsGesture) { IsGesture = false; if (TouchPadSlipUnifiedDirEndCallback != null) { TouchPadSlipUnifiedDirEndCallback(CurGestureDir); } } } if (IsTouchDown) { Vector3 oneFrameVector2 = GvrControllerInput.TouchPos - StartTouchPoint; StartTouchPoint = GvrControllerInput.TouchPos; oneFrameVector2 = new Vector2(oneFrameVector2.x, -oneFrameVector2.y);//y轴反向处理 if (TouchPadVector2Callback != null) { TouchPadVector2Callback(oneFrameVector2); } Vector3 stretchVector2 = GvrControllerInput.TouchPos - FirstTouchPoint; stretchVector2 = new Vector2(stretchVector2.x, -stretchVector2.y);//y轴反向处理 if (TouchPadStretchVector2Callback != null) { TouchPadStretchVector2Callback(stretchVector2); } //手势判断 if (!IsGesture && stretchVector2.sqrMagnitude > Mathf.Pow(GestureThreshold, 2)) { IsGesture = true; CurGestureDir = PreDefScrp.ComputeGestureDirection(stretchVector2); } if (IsGesture) { // firstPoint-by-point gesture float f = PreDefScrp.ComputeGestureDirectionLen(CurGestureDir, stretchVector2); if (TouchPadSlipUnifiedDirCallback != null) { TouchPadSlipUnifiedDirCallback(CurGestureDir, f); } // everyPoint-by-point gesture CurGestureDirSlip = PreDefScrp.ComputeGestureDirection(oneFrameVector2); float t = PreDefScrp.ComputeGestureDirectionLen(CurGestureDirSlip, oneFrameVector2); if (TouchPadEveryoneSlipUnifiedDirCallback != null) { TouchPadEveryoneSlipUnifiedDirCallback(CurGestureDirSlip, t); } } } if (CurGvrConnectionState == SvrControllerState.None) { // gaze版Recenter方法 Controller未连接的状态 if (GvrPointerInputModule.Pointer.TriggerDown) { if (TouchPadPressCallback != null) { TouchPadPressCallback(hitGO, true); } if (TouchPadPressDirCallback != null) { TouchPadPressDirCallback(Camera.main.transform.forward, true); } TriggerDownTime = Time.time; } if (TriggerDownTime != -1) { if (Time.time - TriggerDownTime >= SpaceTime)//长按 { TriggerDownTime = -1; //if (RecenteredCallback != null) // RecenteredCallback(Camera.main.transform.forward); } else { if (GvrPointerInputModule.Pointer.TriggerUp) { TriggerDownTime = -1; if (TouchPadPressCallback != null) { TouchPadPressCallback(hitGO, false); } if (TouchPadPressDirCallback != null) { TouchPadPressDirCallback(Camera.main.transform.forward, false); } } } } } else { if (GvrControllerInput.ClickButtonDown) { IsTouchDown = false; if (TouchPadPressCallback != null) { TouchPadPressCallback(hitGO, true); } if (TouchPadPressDirCallback != null && hitGO == null) { TouchPadPressDirCallback(Camera.main.transform.forward, true); } } else if (GvrControllerInput.ClickButtonUp) { if (TouchPadPressCallback != null) { TouchPadPressCallback(hitGO, false); } if (TouchPadPressDirCallback != null && hitGO == null) { TouchPadPressDirCallback(Camera.main.transform.forward, false); } } } if (CurGO != hitGO) { if (PointerExitCallback != null) { PointerExitCallback(CurGO); } if (PointerEnterCallback != null) { PointerEnterCallback(hitGO); } CurGO = hitGO; if (hitGO != null) { Cinema.IsPointerEnterVideoPlayerUI = true; } else { Cinema.IsPointerEnterVideoPlayerUI = false; } } }
public static void InvalidateTechniqueAttempts(DataSource source, int userId, GestureType type, GestureDirection direction) { var attempts = AttemptRepository.GetAttempts(source, false); var modified = from attempt in attempts where attempt.ID == userId.ToString() && attempt.Type == type && attempt.Direction == direction && attempt.Source == source select attempt; foreach (var attempt in modified) { attempt.Valid = false; } AttemptRepository.UpdateAttempt(modified); }
public static void InvalidateTechniqueAttempts(DataSource source, int userId, GestureType type, GestureDirection direction) { var attempts = AttemptRepository.GetAttempts(source, false); var modified = from attempt in attempts where attempt.ID == userId.ToString() && attempt.Type == type && attempt.Direction == direction && attempt.Source == source select attempt; foreach(var attempt in modified) { attempt.Valid = false; } AttemptRepository.UpdateAttempt(modified); }
public static void PlayVideo(GestureDirection direction, GestureType type) { videoWindow?.Close(); videoWindow = new VideoWindow(direction, type); }
public Test(int id, DataSource source) : this() { string path = DataGenerator.TestFileDirectory(source) + id + ".test"; StreamReader sr = new StreamReader(path); ID = path.Split('/').Last().Split('.')[0]; TimeSpan attemptTime = default(TimeSpan); using (sr) { string line = ""; GridSize size = GridSize.Large; GestureType type = GestureType.Pinch; GestureDirection direction = GestureDirection.Push; TimeSpan currentTime = TimeSpan.Zero; int count = 1; while ((line = sr.ReadLine()) != null) { if (line == "") { continue; } string[] time = line.Trim().Split('[', ']')[1].Split(':'); TimeSpan entryTime = new TimeSpan(Int32.Parse(time[0]), Int32.Parse(time[1]), Int32.Parse(time[2])); if (line.Contains("Started new gesture test.")) { string tobesearched = "Type: "; string toBefound = line.Substring(line.IndexOf(tobesearched) + tobesearched.Length).Split(' ')[0]; switch (toBefound) { case "Throw": type = GestureType.Throw; break; case "Tilt": type = GestureType.Tilt; break; case "Swipe": type = GestureType.Swipe; break; case "Pinch": type = GestureType.Pinch; break; } tobesearched = "Direction: "; toBefound = line.Substring(line.IndexOf(tobesearched) + tobesearched.Length).Split(' ')[0]; direction = toBefound == "Push" ? GestureDirection.Push : GestureDirection.Pull; if (!Attempts.ContainsKey(type)) { Attempts.Add(type, new List <Attempt>()); } currentTime = entryTime; } else if (line.Contains("Grid height: 10")) { size = GridSize.Small; } else if (line.Contains("Grid height: 5")) { size = GridSize.Large; } else if (line.Contains("Target")) { if (line.Contains("JL: NA")) { currentTime = entryTime; continue; } attemptTime = entryTime - currentTime; currentTime = entryTime; Attempt attempt = new Attempt(ID, count++, line, attemptTime, size, direction, type, source); Attempts[type].Add(attempt); } } } if (source == DataSource.Old) { FixTest(); } }