Procházet zdrojové kódy

修改定位流程,加入算法调整

slambb před 11 měsíci
rodič
revize
6ea9514782

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 109 - 28
Assets/BowArrow/Fonts/HarmonyOS_Sans_SC_Regular SDF.asset


+ 20 - 2
Assets/BowArrow/InfraredCamera/InfraredDemo.cs

@@ -980,8 +980,13 @@ public class InfraredDemo : JCUnityLib.ViewBase
         }
     }
 
-    // 提取转换四边形的方法
-    Vector2[] ConvertQuadToPoints(QuadrilateralInCamera quad, Vector2 texSize)
+    /// <summary>
+    /// 提取转换四边形的方法
+    /// </summary>
+    /// <param name="quad"></param>
+    /// <param name="texSize"></param>
+    /// <returns></returns>
+    public Vector2[] ConvertQuadToPoints(QuadrilateralInCamera quad, Vector2 texSize)
     {
         if (quad == null || quad.Quad == null || quad.Quad.Count < 4)
         {
@@ -1385,6 +1390,19 @@ public class InfraredDemo : JCUnityLib.ViewBase
     public void ResetCenterOffset() {
         infraredCameraHelper.ResetCenterOffset();
     }
+    /// <summary>
+    /// 撤销中心点
+    /// </summary>
+    public void RevokeCenterOffset()
+    {
+        infraredCameraHelper.RevokeCenterOffset();
+    }
+    /// <summary>
+    /// 重置定位数据
+    /// </summary>
+    public void ResetPositioningData(bool bSyncLocal = false) {
+        infraredCameraHelper.ClearInfraredPositioningData(bSyncLocal);
+    }
 
 
 

+ 1 - 1
Assets/BowArrow/Modules/InfraredGuider/InfraredGuider.prefab

@@ -1396,7 +1396,7 @@ MonoBehaviour:
     m_HorizontalOverflow: 0
     m_VerticalOverflow: 0
     m_LineSpacing: 2
-  m_Text: "\u8BF7\u79FB\u52A8\u5F13\u7BAD\u63A7\u5236\u51C6\u5FC3\uFF0C\u4F7F\u5176\u79FB\u52A8\u5230\u56DB\u4E2A\u5706\u70B9\u3002\n\u5F53\u51C6\u5FC3\u65E0\u6CD5\u79FB\u52A8\u5230\u56DB\u4E2A\u5706\u70B9\u65F6\uFF0C\u8BF7\u70B9\u51FB\u201C\u5F02\u5E38\u95EE\u9898\u5904\u7406\u201D\u3002"
+  m_Text: "\u8BF7\u79FB\u52A8\u8BBE\u5907\u63A7\u5236\u51C6\u5FC3\uFF0C\u4F7F\u5176\u79FB\u52A8\u5230\u56DB\u4E2A\u5706\u70B9\u3002\n\u5F53\u51C6\u5FC3\u65E0\u6CD5\u79FB\u52A8\u5230\u56DB\u4E2A\u5706\u70B9\u65F6\uFF0C\u8BF7\u70B9\u51FB\u201C\u5F02\u5E38\u95EE\u9898\u5904\u7406\u201D\u3002"
 --- !u!114 &1412163120633485320
 MonoBehaviour:
   m_ObjectHideFlags: 0

+ 25 - 11
Assets/BowArrow/Scripts/Components/TextAutoLanguage2/Resources/TextAutoLanguage2/cn.json

@@ -551,35 +551,49 @@
   /**
   手动识别部分
   **/
-  "TitleTip1": "调整激光定位器的角度和位置,\n使摄像头可以完整地拍摄到整个电视屏幕",
+  "TitleTip1": "调整激光定位器的角度和位置,\n使整个电视屏幕完整地位于梯形框内",
+  "TitleTipHasData": "如定位白框与电视屏幕匹配,可点击“确认”按键,跳过定位流程;否则调整激光定位器的角度和位置,使整个电视屏幕完整地位于梯形框内,点击“自动”按键",
   "TitleTip2": "如自动识别不准确,可调整激光定位器或调整图像对比度,使屏幕\n边缘更清晰后,再次自动识别;\n如多次自动识别不成功,可点击手动功能,并用手指拖动定位区域\n四个角进行调整,点击确认进入下一步。",
-  "Withdrawal": "撤",
+  "Withdrawal": "撤",
   "Automatic": "自动",
   "Manual": "手动",
   "Confirm": "确认",
   "Finish": "完成",
   "Reset": "重置",
   "EnterMarker": "进入屏幕标记",
+  "Marker": "标记",
   "TitleTipMarker": "使用{0}的红外激光瞄准屏幕的四个角,\n并单击准心键确认标记。待四个角都标记完成后,点击完成进入下一步",
   "TitleTipMarker-HOUYIPro": "HOUYI Pro瞄准模块",
   "TitleTipMarker-ArtemisPro": "Artemis Pro",
   "TitleTipMarker-M9": "操作M9手枪依次瞄准屏幕的四个角\n扣动扳机进行标记(四角位置标签)",
+  "TitleTipMarker2": "操作设备对准电视,按提示依次用光点瞄准梯形框中电视屏幕内定位框的四个角,并单击准心键进行标记。标记需位于定位框内。",
+  "TipMiddle": "请标记:{0}角的点",
+  "TipTopLeft": "左上",
+  "TipTopRight": "右上",
+  "TipBottomRight": "右下",
+  "TipBottomLeft": "左下",
+  "TipMarkComplete": "标记完成!",
+  "TipMarkerError": "定位失败,需按要求重新标记。",
+  "TipQuadError": "定位失败,不是有效的四边形。",
+  "TipAutoEnd": "① 如无法识别出屏幕,需屏蔽环境干扰光源后,点击“自动”按键重新识别\n② 如白框与电视屏幕匹配,点击“确认”按键,跳过定位流程 \n③ 如白框与电视屏幕不匹配,点击“环境测光”按键,继续定位流程",
+  "TipLightCtrl": "操作设备对准电视,将光点位于梯形框中电视屏幕,红色十字会在光点上,并随光点移动;如没有,需要找出干扰光源关闭它、调整激光定位器位置或调小亮度和对比度",
+
   "ScreenPositioningSuccessful-title": "屏幕定位成功,请选择最佳方案",
   "FirstResult": "结果一",
   "SecondResult": "结果二",
-  "FirstLineTip": "AI自动识别的结果",
-  "SecondLineTip": "通过四角位置标签,半自动识别的结果",
+  "FirstLineTip": "AI智能识别屏幕区域的结果",
+  "SecondLineTip": "增加位置标签(屏幕区域四个角)辅助识别的屏幕区域结果",
   "FittingFailed": "拟合失败,继续点击完成.",
   "Incomplete": "请完成屏幕四个角的标记后,再点击完成进入下一步",
   /**
    红外连接后游戏射击页面
   **/
-  "TopTip_CrossHair": "单击模块上的 <sprite=0> 按键,这是进入准心调整界面的开/关,\n请连续操作两次。",
-  "TopTip_CrossHairPro": "单击设备上的按键,这是进入准心调整界面的开/关,\n请连续操作两次。",
-  "TopTip_CrossHairGunPro": "取出弹夹模块,单击其上的按键,这是进入准心\n调整界面的开/关,请连接操作两次。",
-  "TopTip_ARTEMISPro": " 双击设备上的按键进行准心校准,之后请用正常的拉弓姿势,\n以箭筒头部为参考点瞄准靶心,{0}秒后完成准心校准。",
-  "TopTip_GunPro": "取出弹夹模块,双击其上的按键进行准心校准,快速装回弹夹后,\n用三点一线的射击姿势瞄准靶心,{0}秒后完成准心校准。",
-  "TopTip": " 长按模块上的按键 <sprite=0> 3秒进行准心校准,之后请用正常的拉弓姿势,以箭筒头部为参考点瞄准靶心,{0}秒后完成准心校准",
+  "TopTip_CrossHair": "单击模块上的 <sprite=0> 按键,这是进入准心调整界面的开/关,\n请单击两次(间隔一秒)。",
+  "TopTip_CrossHairPro": "单击设备上的按键,这是进入准心调整界面的开/关,\n请单击两次(间隔一秒)。",
+  "TopTip_CrossHairGunPro": "取出弹夹模块,单击其上的按键,这是进入准心\n调整界面的开/关,请单击两次(间隔一秒)。",
+  "TopTip_ARTEMISPro": " 双击设备上的按键进行准心校准,之后请用正常的拉弓姿势,\n以箭筒头部瞄准靶心,{0}秒后准心会移动到靶心,完成准心校准。",
+  "TopTip_GunPro": "取出弹夹模块,双击其上的按键进行准心校准,快速装回弹夹后,\n用三点一线的射击姿势瞄准靶心,{0}秒后准心会移动到靶心,完成准心校准。",
+  "TopTip": " 长按模块上的按键 <sprite=0> 3秒进行准心校准,之后请用正常的拉弓姿势,以箭筒头部瞄准靶心,{0}秒后准心会移动到靶心,完成准心校准",
   "TopTip2": "准心已校准,请射中靶子后,自动退出界面。",
   "LeftTitle": "异常问题检测",
   "LeftTip": "1、如发现光标不顺滑,或光标的移动范围不够大,请调整图像亮度或重新进行屏幕定位。\n\n2、如发现光标移动时有延时的现象,请调整分辨率或更换性能更好的手机后,再重新进行测试。\n\n3、如发现光标会随机闪烁不同的位置,或无法移动时,请点击环境测光寻找干扰源,并屏蔽干扰源。",
@@ -592,7 +606,7 @@
   "CameraSensitivity": "相机感光度",
   "AbnormalIssues": "异常问题处理",
 
-  "ErrorStepTip": "请移动弓箭控制准心,使其移动到四个圆点。\n当准心无法移动到四个圆点时,请点击“异常问题处理”。",
+  "ErrorStepTip": "请移动设备控制准心,使其移动到四个圆点。\n当准心无法移动到四个圆点时,请点击“异常问题处理”。",
   "ErrorTip1": "1.\n如发现光标移动时有延时的现象,请调整分辨率或更换性能更好的手机后,再重新进行测试。",
   "ErrorTip2": "2.\n如发现光标会随机闪烁不同的位置,或无法移动时,请点击环境测光寻找干扰源,并屏蔽干扰源。",
   "ErrorTip3": "3.\n如发现光标不顺滑,或光标的移动范围不够大,请调整图像亮度或对比度。",

+ 28 - 11
Assets/BowArrow/Scripts/Components/TextAutoLanguage2/Resources/TextAutoLanguage2/en.json

@@ -600,35 +600,52 @@
   /**
   手动识别部分
   **/
-  "TitleTip1": "Adjust the angle and position of the laser locator so \nthat the camera can capture the entire TV screen completely",
+  "TitleTip1": "Adjust the angle and position of the laser locator,so \nthat the entire TV screen fits completely within the trapezoidal frame.",
+  "TitleTipHasData": "If the white positioning frame matches the TV screen, you can click the 'Confirm' button to skip the positioning process; otherwise, adjust the angle and position of the laser locator so that the entire TV screen fits completely within the trapezoidal frame, then click the 'Auto' button.",
+
   "TitleTip2": "If the automatic recognition is not accurate, you can adjust the laser locator or adjust the \nimage contrast to make the screen edge clearer and then automatically recognize it again; \n If automatic recognition fails multiple times, click the manual function and drag the four \ncorners of the positioning area with your finger to adjust. Click OK to proceed to the next step. ",
-  "Withdrawal": "Withdrawal",
+  "Withdrawal": "Revoke",
   "Automatic": "Automatic",
   "Manual": "Manual",
   "Confirm": "Confirm",
   "Finish": "Finish",
   "Reset": "Reset",
   "EnterMarker": "Enter Screen Marker",
+  "Marker": "Marker",
   "TitleTipMarker": "Aim the infrared laser of {0} at the four corners of the screen, \nand click the reticle button to confirm each mark. Once all four corners are marked, click 'Finish' to proceed to the next step.",
   "TitleTipMarker-HOUYIPro": "HOUYI Pro Aiming Module",
   "TitleTipMarker-ArtemisPro": "Artemis Pro",
   "TitleTipMarker-M9": "Operate M9 pistol to aim at the four corners of the screen \nin sequence, pull the trigger to mark (four corner position label)",
+  "TitleTipMarker2": "Position the device facing the TV, and follow the instructions to aim the light point at the four corners of the target box within the TV screen one by one. Click the reticle button to mark each corner. The marks must be within the target box.",
+  "TipMiddle": "Please mark: Point at the {0} corner",
+  "TipTopLeft": "Top-Left",
+  "TipTopRight": "Top-Right",
+  "TipBottomRight": "Bottom-Right",
+  "TipBottomLeft": "Bottom-Left",
+  "TipMarkComplete": "Mark Complete!",
+  "TipMarkerError": "Positioning failed. You need to re-mark as instructed.",
+  "TipQuadError": "Positioning failed. The shape is not a valid quadrilateral.",
+  "TipAutoEnd": "① If the screen cannot be recognized, block the environmental light interference and click the 'Auto' button to re-recognize.\n② If the white frame matches the TV screen, click the 'Confirm' button to skip the positioning process.\n③ If the white frame does not match the TV screen, click the 'Environment Light Measurement' button to continue the positioning process.",
+  "TipLightCtrl": "Position the device facing the TV, placing the light point within the trapezoidal frame on the TV screen. A red cross will appear at the light point and move with it. If not, identify and turn off the interfering light source, adjust the position of the laser locator, or reduce the brightness and contrast.",
+
   "ScreenPositioningSuccessful-title": "Screen positioning successful, please choose the best solution",
   "FirstResult": "First Result",
   "SecondResult": "Second Result",
-  "FirstLineTip": "The result of AI automatic recognition",
-  "SecondLineTip": "The result of semi-automatic recognition through four corner position labels",
+  "FirstLineTip": "AI intelligently recognizes the results of screen regions",
+  "SecondLineTip": "Add position tags (the four corners of the screen) to assist in identifying screen region results",
   "FittingFailed": "Fitting failed, continue clicking to complete.",
   "Incomplete": "Please mark the four corners of the screen before clicking 'Finish' to proceed to the next step",
   /**
    红外连接后游戏射击页面
   **/
-  "TopTip_CrossHair": "Click the <sprite=0> button on the module to enter the center adjustment \n interface. Please operate it twice in a row.",
-  "TopTip_CrossHairPro": "Click the button on the device. This toggles the crosshair adjustment interface on/off.\nPlease perform this action twice in a row.",
-  "TopTip_CrossHairGunPro": "Remove the magazine module and click the button on it. This is the on/off button to enter the alignment adjustment interface. Please connect the operation twice",
-  "TopTip_ARTEMISPro": "Double click the button on the device to calibrate the center of gravity.\nThen, use the normal bow position and aim at the target with the arrow head \nas the reference point. After {0} seconds, complete the center of gravity calibration.",
-  "TopTip_GunPro": "Remove the magazine module, double-click the button on it to calibrate the center\n of sight, quickly reinstall the magazine, aim at the target with a three-point line\n shooting posture, and complete the center of sight calibration after {0} seconds.",
-  "TopTip": "Press and hold the button<sprite=0>on the module for 3 seconds to calibrate the center of sight. Then, use the normal bow position and aim at the target with the head of the arrow tube as the reference point. After {0} seconds, complete the center of sight calibration",
+  "TopTip_CrossHair": "Click the <sprite=0> button on the module to enter the center adjustment \n interface. Please click twice (with an interval of one second)",
+  "TopTip_CrossHairPro": "Click the button on the device. This toggles the crosshair adjustment interface on/off.\nPlease click twice (with an interval of one second).",
+  "TopTip_CrossHairGunPro": "Remove the magazine module and click the button on it. This is the on/off button to enter the alignment adjustment interface. Please click twice (with an interval of one second)",
+  "TopTip_ARTEMISPro": "Double-click the button on the device for reticle calibration. Then, use \nthe normal bow-drawing posture, aim the bow at the bullseye, and after {0} seconds, \nthe reticle will move to the bullseye, completing the calibration.",
+  "TopTip_GunPro": "Remove the magazine module, double-click the button on it for reticle calibration. \nAfter quickly reattaching the magazine, use a three-point alignment shooting posture \nto aim at the bullseye. After {0} seconds, the reticle will move to the bullseye, completing the calibration.",
+  "TopTip": "Press and hold the button on the module <sprite=0> for 3 seconds to calibrate the reticle. Then, use the normal bow-drawing posture, aim the bow at the bullseye, and after {0} seconds, the reticle will move to the bullseye, completing the calibration.",
+
+
   "TopTip2": "Accurate heart calibration, please hit the target and automatically exit the interface.",
   "LeftTitle": "Abnormal Problem Detection",
   "LeftTip": "1、If the cursor is not smooth or its movement range is insufficient, please adjust the image brightness or reposition the screen.\n\n2、If you notice a delay when the cursor moves, try adjusting the resolution or switching to a higher-performance phone, then retest.\n\n3、If the cursor randomly flickers to different positions or cannot move, please click on ambient light measurement to locate the interference source and block it.",
@@ -641,7 +658,7 @@
   "CameraSensitivity": "Camera Sensitivity",
   "AbnormalIssues": "Abnormal Issues",
 
-  "ErrorStepTip": "Please move the bow and arrow control center to the four dots.\nWhen the center cannot move to the four dots, please click on 'Exception Handling'.",
+  "ErrorStepTip": "Please move the device control center to the four dots.\nWhen the center cannot move to the four dots, please click on 'Exception Handling'.",
   "ErrorTip1": "1.\nIf there is a delay when the cursor moves, please adjust the resolution or replace it with a better performing phone before retesting.",
   "ErrorTip2": "2.\nIf you find that the cursor randomly flashes at different positions or cannot move, please click on the ambient metering to find the interference source and block it.",
   "ErrorTip3": "3.\nIf you find that the cursor is not smooth or the movement range of the cursor is not large enough, please adjust the brightness or contrast of the image.",

+ 19 - 0
Assets/InfraredProject/InfraredCamera/Scripts/InfraredCameraHelper.cs

@@ -252,6 +252,25 @@ namespace InfraredManager
 
             _screenLocate.ResetPointsOffest();
         }
+        /// <summary>
+        /// 撤销回退中心点的偏移量操作。
+        /// </summary>
+        public void RevokeCenterOffset()
+        {
+            _screenLocate.RevokePointsOffest();
+        }
+        
+        /// <summary>
+        /// 清除定位数据
+        /// </summary>
+        public void ClearInfraredPositioningData(bool bSyncLocal = false) {
+            //重置识别点
+            ScreenLocate.Main.ScreenIdentification.ClearQuadCache();
+            //清除一下记录的点
+            ScreenLocate.quadUnityVectorList.Clear();
+            //是否需要保存清除后的数据
+            if(bSyncLocal)  ScreenLocate.SaveScreenLocateVectorList();
+        }
 
         /// <summary>
         /// 初始化一次本地的记录点

+ 16 - 3
Assets/InfraredProject/WebCamera/Script/ZIM/ScreenLocate.cs

@@ -89,7 +89,11 @@ public partial class ScreenLocate : MonoBehaviour
     /// CameraLocation 的偏移量
     /// </summary>
     public Vector2 CameraLocationOffset { get; set; } = new Vector2(0, 0);
+    //用来记录最后一次更新的数据
+    Vector2 OldCameraLocationOffset { get; set; } = new Vector2(0, 0);
     public Vector2 UVOffset { get; set; } = new Vector2(0, 0);
+    //用来记录最后一次更新的数据
+    Vector2 OldUVOffset { get; set; } = new Vector2(0, 0);
 
     // public InfraredDemo InfraredDemoMain => FindObjectOfType<InfraredDemo>();
 
@@ -1041,7 +1045,9 @@ public partial class ScreenLocate : MonoBehaviour
     /// </summary>
     static public void SaveScreenLocateVectorList()
     {
-        string saveStr = string.Join(";", quadUnityVectorList.Select(v => $"{v.x},{v.y}")); //,{v.z}
+        //string saveStr = string.Join(";", quadUnityVectorList.Select(v => $"{v.x},{v.y}")); //,{v.z}
+        // 如果列表为空,保存空字符串或自定义标记
+        string saveStr = quadUnityVectorList.Count > 0 ? string.Join(";", quadUnityVectorList.Select(v => $"{v.x},{v.y}")) : "";
         Debug.Log("SaveScreenLocateVectorList:  " + saveStr);
         PlayerPrefs.SetString("ScreenLocateVectorList", saveStr);
     }
@@ -1075,13 +1081,13 @@ public partial class ScreenLocate : MonoBehaviour
         // 计算从原始中心到输入点的偏移量
         if (type == "CameraLocation")
         {
-            CameraLocationOffset = inputPoint - screenIdentification.Screen.TransformToCamera(new Vector2(0.5f, 0.5f) * screenIdentification.Screen.UVSize);
+            OldCameraLocationOffset = CameraLocationOffset = inputPoint - screenIdentification.Screen.TransformToCamera(new Vector2(0.5f, 0.5f) * screenIdentification.Screen.UVSize);
             return CameraLocationOffset;
         }
         else
         {
             //ScreenUV
-            UVOffset = inputPoint - new Vector2(0.5f, 0.5f);
+            OldUVOffset = UVOffset = inputPoint - new Vector2(0.5f, 0.5f);
             return UVOffset;
         }
     }
@@ -1093,6 +1099,13 @@ public partial class ScreenLocate : MonoBehaviour
         CameraLocationOffset = Vector2.zero;
         UVOffset = Vector2.zero;
     }
+    /// <summary>
+    /// 撤销操作,
+    /// </summary>
+    public void RevokePointsOffest() {
+        CameraLocationOffset = OldCameraLocationOffset;
+        UVOffset = OldUVOffset;
+    }
 
     /// <summary>
     /// 这里计算一个偏移后的cameraLocatoin位置

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 684 - 122
Assets/SmartBow/Resources/SmartBow/Prefabs/Views/Home/InfraredScreenPositioningView.prefab


+ 501 - 163
Assets/SmartBow/Scripts/Views/InfraredViewParts/InfraredScreenPositioningView.cs

@@ -13,6 +13,15 @@ using ZIM.Unity;
 using o0.Project;
 using UnityEngine.SceneManagement;
 
+public enum ScreenPositioningStep { 
+  None, //
+  Start,//开始界面
+  AutoEnd,//自动定位结束后界面
+  Marker,//进行标记界面
+  LightCtrl,//灯光调整界面
+  Successful,//结果选择界面
+}
+
 public class LinePosition
 {
     public int index;
@@ -25,8 +34,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     RectTransform canvasRectTransform;
     [SerializeField]
     RectTransform draggableParent;
-    [SerializeField]
-    GameObject mask;
+
     [SerializeField]
     GameObject cameraLight;
     [SerializeField]
@@ -51,14 +59,6 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     //相机感光部分
     [SerializeField]
     Slider slider;
-
-    [SerializeField]
-    RawImage rawImage;
-    [SerializeField]
-    RectTransform crosshair;
-    [SerializeField]
-    RectTransform crosshairSmall;
-
     [SerializeField]
     GameObject textTip1;
     [SerializeField]
@@ -76,15 +76,53 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     [SerializeField] Color normalTextColor = Color.black;
     [SerializeField] Color highlightedTextColor = Color.white;
 
+    [Header("Common Layout Group")]
+    [SerializeField]
+    GameObject mask;
+    [SerializeField]
+    Line maskLine;
+    [SerializeField]
+    RawImage rawImage;
+    [SerializeField]
+    RectTransform crosshair;
+    [Tooltip("存在校准数据时候显示")]
+    [SerializeField] LineGenerator CurrentUILineGenerator;//开始显示屏幕线条
 
-    [SerializeField] TextAutoLanguage2 markerTextAutoLanguage2;
+    [Header("Start Layout Group")]
     [SerializeField]
     GameObject LayoutStart;
     [SerializeField]
+    GameObject StartTextTip1;
+    [SerializeField]
+    GameObject StartTextTipHasData;
+    [Tooltip("没有存在校准数据时候显示")]
+    [SerializeField]
+    GameObject BottomAutoBtn;
+    [Tooltip("存在校准数据时候显示")]
+    [SerializeField]
+    GameObject BottomConfirmBtn;
+
+    [Header("AutoEnd Layout Group")]
+    [SerializeField]
+    GameObject LayoutAutoEnd;
+    [Header("Marker Layout Group")]
+    [SerializeField]
     GameObject LayoutMarker;
     [SerializeField]
-    GameObject LayoutSuccessful;
+    RectTransform crosshairSmall;
+    [Tooltip("标记页面标题提示")]
+    [SerializeField] TextAutoLanguage2 markerTextAutoLanguage2;
 
+    [Header("LightCtrl Layout Group")]
+    [SerializeField]
+    GameObject LayoutLightCtrl;
+    [SerializeField] Slider sliderContrast;
+    //亮度
+    [SerializeField] Slider sliderBrightness;
+
+    [Header("Successful Layout Group")]
+    [SerializeField]
+    GameObject LayoutSuccessful;
     [Tooltip("选择框的Line")]
     [SerializeField]
     Sprite[] ResultLines;// 0: 选中状态, 1: 未选中状态
@@ -103,49 +141,57 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     [SerializeField] LineGenerator FirstUILineGenerator;//第一个结果屏幕线条
     [SerializeField] LineGenerator SecondUILineGenerator;//第二个结果屏幕线条
 
+
+    //标记当前页面情况
+    ScreenPositioningStep curStepView = ScreenPositioningStep.Start;//设置一个开始页面 
+    InfraredDemo infraredDemo;
     bool doLocateAuto;
     int DefaultResolutionIndex;
     private void Awake()
     {
+        //设置一次显示相机的image 的颜色
+        rawImage.color = Color.white;
         offset = line.MyThickness;
-
     }
 
     void Start()
     {
+        //获取InfraredDemo
+        infraredDemo = InfraredDemo._ins;
+
         doLocateAuto = false;
 
         textTip1.SetActive(true);
         //设置btnAuto 高亮
-        OnButtonClick(btnAuto);
+        //OnButtonClick(btnAuto);
         textTip2.SetActive(false);
 
         //动态设置marker的提示语
-        if (BluetoothAim.ins!=null && markerTextAutoLanguage2 != null)
-        {
-            string name = "";
-            bool switchValue = false;
-            if (BluetoothAim.ins.isMainConnectToHOUYIPRO())
-            {
-                name = TextAutoLanguage2.GetTextByKey("TitleTipMarker-HOUYIPro");
-            }
-            else if (BluetoothAim.ins.isMainConnectToARTEMISPRO())
-            {
-                name = TextAutoLanguage2.GetTextByKey("TitleTipMarker-ArtemisPro");
-            }
-            else if (BluetoothAim.ins.isMainConnectToGun())
-            {
-                //name = TextAutoLanguage2.GetTextByKey("TitleTipMarker-M9");
-                switchValue = true;
-            }
-            if (switchValue)
-            {
-                markerTextAutoLanguage2.SetTextKey("TitleTipMarker-M9");
-            }
-            else {
-                markerTextAutoLanguage2.textFormatArgs = new object[] { name };
-            }
-        }
+        //if (BluetoothAim.ins!=null && markerTextAutoLanguage2 != null)
+        //{
+        //    string name = "";
+        //    bool switchValue = false;
+        //    if (BluetoothAim.ins.isMainConnectToHOUYIPRO())
+        //    {
+        //        name = TextAutoLanguage2.GetTextByKey("TitleTipMarker-HOUYIPro");
+        //    }
+        //    else if (BluetoothAim.ins.isMainConnectToARTEMISPRO())
+        //    {
+        //        name = TextAutoLanguage2.GetTextByKey("TitleTipMarker-ArtemisPro");
+        //    }
+        //    else if (BluetoothAim.ins.isMainConnectToGun())
+        //    {
+        //        //name = TextAutoLanguage2.GetTextByKey("TitleTipMarker-M9");
+        //        switchValue = true;
+        //    }
+        //    if (switchValue)
+        //    {
+        //        markerTextAutoLanguage2.SetTextKey("TitleTipMarker-M9");
+        //    }
+        //    else {
+        //        markerTextAutoLanguage2.textFormatArgs = new object[] { name };
+        //    }
+        //}
         //手动不高亮
         //ResetButton(btnHandMovement);
 
@@ -171,31 +217,52 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             //pos4.anchoredPosition = ScreenLocate.quadUnityVectorList[2].pixelToLocalPosition_AnchorCenter(Vector2.one, canvasRectTransform.rect);
             //pos3.anchoredPosition = ScreenLocate.quadUnityVectorList[3].pixelToLocalPosition_AnchorCenter(Vector2.one, canvasRectTransform.rect);
         }
-
+        //更新start相关ui
+        UpdateStartUI();
 
         //记录操作的位置信息
         oldLinePosition = new List<LinePosition>();
         SetLinePos();
 
         //相机感光度
-        if (InfraredDemo._ins)
+        if (infraredDemo)
         {
-            //重置偏移量
-            InfraredDemo._ins.ResetCenterOffset();
-
-            //重置识别点
-            ScreenLocate.Main.ScreenIdentification.ClearQuadCache();
-            //清除一下记录的点
-            ScreenLocate.quadUnityVectorList.Clear();
+            ////重置偏移量
+            //infraredDemo.ResetCenterOffset();
 
+            //////重置识别点
+            ////ScreenLocate.Main.ScreenIdentification.ClearQuadCache();
+            //////清除一下记录的点
+            ////ScreenLocate.quadUnityVectorList.Clear();
+            //infraredDemo.ResetPositioningData();
 
             slider.onValueChanged.AddListener((value) =>
             {
-                InfraredDemo._ins.onSliderEvent(value);
+                infraredDemo.onSliderEvent(value);
             });
-            InfraredDemo._ins.onSetSliderValue(slider);
+            infraredDemo.onSetSliderValue(slider);
+
+            //对比度
+            sliderContrast.onValueChanged.AddListener((value) =>
+            {
+                infraredDemo.onSliderCustomEvent(value, -20.0f, 20.0f);
+            });
+            infraredDemo.onSetSliderCustomValue(sliderContrast, -20.0f, 20.0f);
+
+            //亮度
+            sliderBrightness.onValueChanged.AddListener((value) =>
+            {
+                infraredDemo.onSliderCustomBrightnessEvent(value, -20.0f, 20.0f);
+            });
+            infraredDemo.onSetSliderCustomBrightnessValue(sliderBrightness, -20.0f, 20.0f);
+
 
-            offset = line.MyThickness = InfraredDemo._ins.lineWidth.Get();
+
+            offset = line.MyThickness = infraredDemo.lineWidth.Get();
+
+
+            ZIM.Unity.QuadrilateralInCamera screen = ScreenLocate.Main.ScreenIdentification.Screen.QuadInCamera;
+            CurrentUILineGenerator.Points = infraredDemo.ConvertQuadToPoints(screen, ScreenLocate.Main.getUVCCameraInfoSize);
 
         }
         else {
@@ -226,6 +293,10 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
 
                 Debug.Log("PC获取相机的感光度:" + v + " = " + v2);
                 slider.SetValueWithoutNotify((float)v2);
+
+                // 对 sliderContrast 和 sliderBrightness 调用统一的处理函数
+                HandleSliderValueChanged(sliderContrast, value => ScreenLocate.Main.pcContrast = value, ScreenLocate.Main.pcContrast);
+                HandleSliderValueChanged(sliderBrightness, value => ScreenLocate.Main.pcBrightness = value, ScreenLocate.Main.pcBrightness);
             }
 
 
@@ -240,13 +311,13 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         //}
         //slider.onValueChanged.AddListener((value) => {
         //    //onSliderEvent(value);
-        //    InfraredDemo._ins.SetBrightness(value);
+        //    infraredDemo.SetBrightness(value);
         //});
-        //slider.value = InfraredDemo._ins.brightness.Get();
+        //slider.value = infraredDemo.brightness.Get();
         //修改分辨率。是否清晰一点?
-        //DefaultResolutionIndex = InfraredDemo._ins?.ResolutionIndex ?? 0;
+        //DefaultResolutionIndex = infraredDemo?.ResolutionIndex ?? 0;
         //Debug.Log("[InfraredScreenPositioningView]开始记录进入时候的分辨率:" + DefaultResolutionIndex);
-        //InfraredDemo._ins?.SetResolutionNew(ScreenLocate.Main.HighScreenLocateResolutionIndex);
+        //infraredDemo?.SetResolutionNew(ScreenLocate.Main.HighScreenLocateResolutionIndex);
 
 
         //if (SB_EventSystem.ins && SB_EventSystem.ins.simulateMouseIsAwaked)
@@ -256,11 +327,28 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         //}
        
     }
-
+    // 两个滑块值处理
+    void HandleSliderValueChanged(UnityEngine.UI.Slider slider, System.Action<float> setValue, float pcValue)
+    {
+        double targetMin2 = -20.0;
+        double targetMax2 = 20.0;
+        double originalMin2 = -1;
+        double originalMax2 = 1;
+        slider.onValueChanged.AddListener((value) =>
+        {
+            double result = InfraredDemo.MapValue(value, targetMin2, targetMax2, originalMin2, originalMax2);
+            Debug.Log($"{slider.name}_current: {value}, result: {result}");
+            setValue((float)result);
+        });
+
+        double mappedValue = InfraredDemo.MapValue(pcValue, originalMin2, originalMax2, targetMin2, targetMax2);
+        Debug.Log($"{slider.name} PC获取相机的感光度: {pcValue} = {mappedValue}");
+        slider.SetValueWithoutNotify((float)mappedValue);
+    }
     private void OnDestroy()
     {
         //修改回进入手动调节页面时候的分辨率
-        //InfraredDemo._ins?.SetResolutionNew(DefaultResolutionIndex);
+        //infraredDemo?.SetResolutionNew(DefaultResolutionIndex);
         Debug.Log("OnDestroy*********************************AwakenSimulateMouse:" + SB_EventSystem.ins.simulateMouseIsAwaked);
         if (SB_EventSystem.ins && !SB_EventSystem.ins.simulateMouseIsAwaked)
         {
@@ -316,31 +404,132 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         }
     }
 
+
+    /// <summary>
+    /// 根据enum操作ui
+    /// </summary>
+    /// <param name="step"></param>
+    public void SetScreenPositioningStepState(ScreenPositioningStep step = ScreenPositioningStep.None)
+    {
+        curStepView = step;
+        AllScreenPositioningStepFalse();
+        switch (step)
+        {
+            case ScreenPositioningStep.Start:
+                mask.SetActive(true);
+                maskLine.SetDrawMask(true);
+                LayoutStart.SetActive(true);
+                UpdateStartUI();
+                break;
+            case ScreenPositioningStep.AutoEnd:
+                mask.SetActive(true);
+                maskLine.SetDrawMask(true);
+                LayoutAutoEnd.SetActive(true);
+                break;
+            case ScreenPositioningStep.Marker:
+                mask.SetActive(true);
+                maskLine.SetDrawMask(true);
+                LayoutMarker.SetActive(true);
+                break;
+            case ScreenPositioningStep.LightCtrl:
+                mask.SetActive(true);
+                maskLine.SetDrawMask(false);
+                LayoutLightCtrl.SetActive(true);
+                break;
+            case ScreenPositioningStep.Successful:
+                mask.SetActive(false);
+                LayoutSuccessful.SetActive(true);
+                break;
+        }
+    }
+ 
+    void AllScreenPositioningStepFalse()
+    {
+        LayoutStart.SetActive(false);
+        LayoutMarker.SetActive(false);
+        LayoutAutoEnd.SetActive(false);
+        LayoutLightCtrl.SetActive(false);
+        LayoutSuccessful.SetActive(false);
+    }
+    /// <summary>
+    /// 开始页面时候ui
+    /// </summary>
+    void UpdateStartUI() {
+        if (ScreenLocate.quadUnityVectorList.Count == 0)
+        {
+            //存在数据,显示确认按钮,确认按钮直接跳转到游戏
+            BottomConfirmBtn.SetActive(false);
+            StartTextTip1.SetActive(false);
+            StartTextTipHasData.SetActive(true);
+        }
+        else
+        {
+            BottomConfirmBtn.SetActive(true);
+            StartTextTip1.SetActive(true);
+            StartTextTipHasData.SetActive(false);
+        }
+    }
+
     public void OnClick_Back()
     {
         AudioMgr.ins.PlayBtn();
         ViewMgr.Instance.DestroyView<InfraredScreenPositioningView>();
     }
+    /// <summary>
+    /// 返回最开始页面
+    /// </summary>
+    public void OnClick_BackLayoutStart()
+    {
+        //返回上一步操作
+        infraredDemo.RevokeCenterOffset();
+        SetScreenPositioningStepState(ScreenPositioningStep.Start);
+    }
+    /// <summary>
+    /// 进入环境光测试
+    /// </summary>
+    public void OnClick_EnterLightCtrl() {
+        SetScreenPositioningStepState(ScreenPositioningStep.LightCtrl);
+    }
+
     /// <summary>
     /// 进入屏幕标记
     /// </summary>
     public void OnClick_EnterMarker() {
-        LayoutStart.SetActive(false);
-        LayoutMarker.SetActive(true);
+        SetScreenPositioningStepState(ScreenPositioningStep.Marker);
     }
+    /// <summary>
+    /// 直接进入游戏流程
+    /// </summary>
+    public void OnClick_EnterGame()
+    {
+        //对比确认效果。直接进入游戏引导页面
+        EnterGame();
+    }
+    /// <summary>
+    /// 进入自动流程校准操作
+    /// </summary>
+    public void OnClick_EnterAuto()
+    {
+        //自动按钮时候进入之前的校准流程
+        //重置偏移量
+        infraredDemo.ResetCenterOffset();
+        infraredDemo.ResetPositioningData();
 
+        OnClick_Auto();
+    }
     /// <summary>
     /// 自动识别
     /// </summary>
     public void OnClick_Auto()
     {
+
         bAuto = true;
         doLocateAuto = true;
 
-        textTip1.SetActive(false);
-        textTip2.SetActive(true);
+        //textTip1.SetActive(false);
+        //textTip2.SetActive(true);
 
-        ResetButton(btnAuto);
+        //ResetButton(btnAuto);
 
         if (enterFromZimWebCamera)
         {
@@ -376,119 +565,51 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     /// </summary>
     public void SyncScreenPosition()
     {
-        //Debug.Log("quadUnityVectorList[i]:" + quadUnityVectorList[i]); 
         Vector2 texSize = ScreenLocate.Main.getUVCCameraInfoSize;
         Debug.Log("texSize:" + texSize + " = " + canvasRectTransform.rect);
-        //自动识别时候四个点
-        //pos1.anchoredPosition = ScreenLocate.quadUnityVectorList[0].pixelToLocalPosition_AnchorCenter(Vector2.one, canvasRectTransform.rect);
-        //pos2.anchoredPosition = ScreenLocate.quadUnityVectorList[1].pixelToLocalPosition_AnchorCenter(Vector2.one, canvasRectTransform.rect);
-        //pos4.anchoredPosition = ScreenLocate.quadUnityVectorList[2].pixelToLocalPosition_AnchorCenter(Vector2.one, canvasRectTransform.rect);
-        //pos3.anchoredPosition = ScreenLocate.quadUnityVectorList[3].pixelToLocalPosition_AnchorCenter(Vector2.one, canvasRectTransform.rect);
         SyncQuadUnityVectorListToPos();
-
         SetRectanglePoints(linePosConversion(pos1.localPosition, pos2.localPosition, pos3.localPosition, pos4.localPosition));
+        if (curStepView == ScreenPositioningStep.Start || curStepView == ScreenPositioningStep.AutoEnd) {
+            //如果是开始页面进行自动定位的
+            if(curStepView != ScreenPositioningStep.AutoEnd) SetScreenPositioningStepState(ScreenPositioningStep.AutoEnd);
 
-        //自动识别后隐藏手动按钮
-        draggableParent.gameObject.SetActive(false);
-        pointsParent.gameObject.SetActive(true);
-        if (ScreenLocate.Main) {
-            ZIM.Unity.QuadrilateralInCamera screen = ScreenLocate.Main.ScreenIdentification.Screen.QuadInCamera;
-            //设置points点
-            for (int i = 0; i < 4; i++)
+            QuadrilateralInCamera screenAuto = ScreenLocate.Main.ScreenIdentification.QuadAuto;
+            if (screenAuto != null)
             {
-                RectTransform t = pointsParent.GetChild(i) as RectTransform;
-                t.anchoredPosition = screen.Quad[i].pixelToLocalPosition_AnchorCenter(screen.CameraSize, pointsParent.rect);
+                Debug.Log("[校准流程] AutoEnd 自动识别screenAuto信息  ------------ ");
+                CurrentUILineGenerator.Points = infraredDemo.ConvertQuadToPoints(screenAuto, texSize);
             }
-        }
-
-        //显示最后结果
-        LayoutMarker.SetActive(false);
-        LayoutSuccessful.SetActive(true);
-
-        //设置两个线段
-        QuadrilateralInCamera screenAuto = ScreenLocate.Main.ScreenIdentification.QuadAuto;
-        QuadrilateralInCamera screenSemiAuto = ScreenLocate.Main.ScreenIdentification.QuadSemiAuto;
-
-        var lo = new Vector2(-0.5f, -0.5f);
-        if (screenAuto != null)
-        {
-            Debug.Log("[校准流程]自动识别screenAuto信息  ------------ ");
-            FirstUILineGenerator.Points = new Vector2[4] {
-                2 * (new Vector2(screenAuto.Quad[0].x/texSize.x,screenAuto.Quad[0].y/texSize.y) + lo),
-                2 * (new Vector2(screenAuto.Quad[1].x/texSize.x,screenAuto.Quad[1].y/texSize.y) + lo),
-                2 * (new Vector2(screenAuto.Quad[3].x/texSize.x,screenAuto.Quad[3].y/texSize.y) + lo),
-                2 * (new Vector2(screenAuto.Quad[2].x/texSize.x,screenAuto.Quad[2].y/texSize.y) + lo) };
-
-            // 打印 Points 的值,合并为一个字符串
-            string pointsOutput = "1 Points: ";
-            for (int i = 0; i < FirstUILineGenerator.Points.Length; i++)
+            else
             {
-                pointsOutput += "[" + FirstUILineGenerator.Points[i].x + ", " + FirstUILineGenerator.Points[i].y + "]";
-                if (i < FirstUILineGenerator.Points.Length - 1)
-                {
-                    pointsOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
+                Debug.LogError("screenAuto 不存在!");
             }
-            Debug.Log(pointsOutput);
 
-            // 打印 Quad 的值,合并为一个字符串
-            int[] customOrder = new int[] { 0, 1, 3, 2 };
-            string quadOutput = "1 Quad Points: ";
-            for (int i = 0; i < customOrder.Length; i++)
+        }
+        else if (curStepView == ScreenPositioningStep.Marker) {
+            //显示最后结果
+            SetScreenPositioningStepState(ScreenPositioningStep.Successful);
+            //设置两个线段
+            QuadrilateralInCamera screenAuto = ScreenLocate.Main.ScreenIdentification.QuadAuto;
+            QuadrilateralInCamera screenSemiAuto = ScreenLocate.Main.ScreenIdentification.QuadSemiAuto;
+            if (screenAuto != null)
             {
-                quadOutput += "[" + screenAuto.Quad[customOrder[i]].x + ", " + screenAuto.Quad[customOrder[i]].y + "]";
-                if (i < customOrder.Length - 1)
-                {
-                    quadOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
+                Debug.Log("[校准流程]Successful自动识别screenAuto信息  ------------ ");
+                FirstUILineGenerator.Points = infraredDemo.ConvertQuadToPoints(screenAuto, texSize);
             }
-            Debug.Log(quadOutput);
-
-        }
-        else {
-            Debug.LogError("screenAuto 不存在!");
-        }
-        if (screenSemiAuto != null)
-        {
-            Debug.Log("[校准流程]半自动识别screenSemiAuto信息 ------------ ");
-            SecondUILineGenerator.Points = new Vector2[4] {
-                2 * (new Vector2(screenSemiAuto.Quad[0].x/texSize.x,screenSemiAuto.Quad[0].y/texSize.y) + lo),
-                2 * (new Vector2(screenSemiAuto.Quad[1].x/texSize.x,screenSemiAuto.Quad[1].y/texSize.y) + lo),
-                2 * (new Vector2(screenSemiAuto.Quad[3].x/texSize.x,screenSemiAuto.Quad[3].y/texSize.y) + lo),
-                2 * (new Vector2(screenSemiAuto.Quad[2].x/texSize.x,screenSemiAuto.Quad[2].y/texSize.y) + lo) };
-
-            // 打印 Points 的值,合并为一个字符串
-            string pointsOutput = "2 Points: ";
-            for (int i = 0; i < SecondUILineGenerator.Points.Length; i++)
+            else
             {
-                pointsOutput += "[" + SecondUILineGenerator.Points[i].x + ", " + SecondUILineGenerator.Points[i].y + "]";
-                if (i < SecondUILineGenerator.Points.Length - 1)
-                {
-                    pointsOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
+                Debug.LogError("screenAuto 不存在!");
             }
-            Debug.Log(pointsOutput);
-
-            // 打印 Quad 的值,合并为一个字符串
-            int[] customOrder = new int[] { 0, 1, 3, 2 };
-            string quadOutput = "2 Quad Points: ";
-            for (int i = 0; i < customOrder.Length; i++)
+            if (screenSemiAuto != null)
             {
-                quadOutput += "[" + screenSemiAuto.Quad[customOrder[i]].x + ", " + screenSemiAuto.Quad[customOrder[i]].y + "]";
-                if (i < customOrder.Length - 1)
-                {
-                    quadOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
+                Debug.Log("[校准流程]Successful半自动识别screenSemiAuto信息 ------------ ");
+                SecondUILineGenerator.Points = infraredDemo.ConvertQuadToPoints(screenSemiAuto, texSize);
+            }
+            else
+            {
+                Debug.LogError("screenSemiAuto 不存在!");
             }
-            Debug.Log(quadOutput);
-
-        }
-        else {
-            Debug.LogError("screenSemiAuto 不存在!");
         }
-
-
-
     }
 
     #region 绘制线段部分
@@ -645,7 +766,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             if (!enterFromInfraredDemo)
             {
                 //每次初始化重置一下引导
-                InfraredDemo._ins.resetInfraredPlayerPrefs();
+                infraredDemo.resetInfraredPlayerPrefs();
                 //GameObject connectGuidanceView = ViewManager2.getGameObjectAndShowView(ViewManager2.Path_ConnectGuidanceView);
                 //connectGuidanceView.GetComponent<ConnectGuidanceView>().showTextipInfrared();
 
@@ -1088,6 +1209,12 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             ViewManager2.HideView(ViewManager2.Path_InfraredScreenPositioningView);
             return;
         }
+        EnterGame();
+        //存储一次节点
+        SaveLocalPos();
+    }
+
+    void EnterGame() {
         if (InfraredDemo.running)
         {
             //跳转入界面
@@ -1096,7 +1223,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             if (!enterFromInfraredDemo)
             {
                 //每次初始化重置一下引导
-                InfraredDemo._ins.resetInfraredPlayerPrefs();
+                infraredDemo.resetInfraredPlayerPrefs();
                 //GameObject connectGuidanceView = ViewManager2.getGameObjectAndShowView(ViewManager2.Path_ConnectGuidanceView);
                 //connectGuidanceView.GetComponent<ConnectGuidanceView>().showTextipInfrared();
 
@@ -1113,8 +1240,6 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             }
 
         }
-        //存储一次节点
-        SaveLocalPos();
     }
 
     /// <summary>
@@ -1124,7 +1249,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
 
         ScreenLocate.Main.UpdateQuadUnityVectorList();
         SyncQuadUnityVectorListToPos();
-        InfraredDemo._ins?.SetLocatePointsToCameraRender(ScreenLocate.quadUnityVectorList, 1, 1);
+        infraredDemo?.SetLocatePointsToCameraRender(ScreenLocate.quadUnityVectorList, 1, 1);
         _locatePointList.Clear();
         
     }
@@ -1154,4 +1279,217 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     //}
 
     #endregion
+
+
+    #region 判断点是否在 quad 内部
+    //判断maskline
+    public bool IsPointInMaskLine(Vector2 point) {
+        return IsPointInQuad(point, maskLine.ScreenPositions);
+    }
+    //实现基于射线法,通过数交点的奇偶性来判断点是否在多边形内
+    bool IsPointInQuad(Vector2 point, List<Vector2> quadVertices)
+    {
+        if (quadVertices.Count != 4)
+        {
+            Debug.LogError("Quad must have exactly 4 vertices.");
+            return false;
+        }
+
+        bool isInside = false;
+        int vertexCount = quadVertices.Count;
+
+        for (int i = 0, j = vertexCount - 1; i < vertexCount; j = i++)
+        {
+            Vector2 vi = quadVertices[i];
+            Vector2 vj = quadVertices[j];
+
+            // 检查射线是否与边相交
+            if (((vi.y > point.y) != (vj.y > point.y)) &&
+                (point.x < (vj.x - vi.x) * (point.y - vi.y) / (vj.y - vi.y) + vi.x))
+            {
+                isInside = !isInside;
+            }
+        }
+
+        return isInside;
+    }
+    /// <summary>
+    /// 判断四个点是否能够形成有效的四边形,并根据面积阈值过滤小四边形
+    /// </summary>
+    /// <param name="points">四个点</param>
+    /// <param name="minArea">面积阈值,用于过滤面积太小的四边形</param>
+    /// <returns>是否是有效的四边形</returns>
+    public bool IsValidQuadrilateral(Vector2[] points, float minArea = 100000.0f)
+    {
+        if (points.Length != 4)
+        {
+            Debug.Log("四个点数不足,无法形成四边形。");
+            return false; // 如果点数不是4,直接返回false
+        }
+
+        // 计算四边形的面积
+        float area = CalculateArea(points);
+        Debug.Log($"四边形面积: {area}");
+        if (area < minArea)
+        {
+            Debug.Log($"面积小于阈值: {minArea},返回 false。");
+            return false; // 面积太小,返回 false
+        }
+
+        // 检查是否有三点共线
+        for (int i = 0; i < points.Length; i++)
+        {
+            Vector2 a = points[i];
+            Vector2 b = points[(i + 1) % points.Length];
+            Vector2 c = points[(i + 2) % points.Length];
+            if (ArePointsCollinear(a, b, c))
+            {
+                Debug.Log($"点 {a}, {b}, {c} 共线,返回 false。");
+                return false; // 存在共线点,返回 false
+            }
+        }
+
+        // 检查是否为矩形或接近矩形
+        if (!IsRectangle(points))
+        {
+            // 如果不是矩形,检查是否是梯形
+            if (!IsTrapezoid(points))
+            {
+                Debug.Log("既不是矩形,也不是梯形,返回 false。");
+                return false; // 不是矩形也不是梯形,返回 false
+            }
+            else
+            {
+                Debug.Log("是梯形。");
+            }
+        }
+        else
+        {
+            Debug.Log("是矩形。");
+        }
+
+        // 检查对角线是否相交
+        if (DoLinesIntersect(points[0], points[1], points[2], points[3]) ||
+            DoLinesIntersect(points[1], points[2], points[3], points[0]))
+        {
+            Debug.Log("对角线相交,返回 false。");
+            return false; // 对角线相交,返回 false
+        }
+
+        // 检查点的排列顺序,确保是顺时针或逆时针
+        if (!ArePointsClockwise(points) && !ArePointsClockwise(points.Reverse().ToArray()))
+        {
+            Debug.Log("点的排列顺序不正确,返回 false。");
+            return false; // 点的排列顺序不正确
+        }
+
+        Debug.Log("四边形有效,返回 true。");
+        return true; // 通过所有检查,返回 true
+    }
+
+    /// <summary>
+    /// 判断是否是矩形(近似90度角)
+    /// </summary>
+    private bool IsRectangle(Vector2[] points)
+    {
+        // 角度容差范围,允许夹角有小的偏差
+        const float dotProductThreshold = 0.1f;
+
+        for (int i = 0; i < points.Length; i++)
+        {
+            Vector2 a = points[i];
+            Vector2 b = points[(i + 1) % points.Length];
+            Vector2 c = points[(i + 2) % points.Length];
+
+            // 计算向量 AB 和 BC 的内积
+            Vector2 ab = b - a;
+            Vector2 bc = c - b;
+            float dotProduct = Vector2.Dot(ab.normalized, bc.normalized);
+
+            // 如果内积接近 0,表示夹角接近 90 度
+            if (Mathf.Abs(dotProduct) > dotProductThreshold)
+            {
+                Debug.Log($"点 {a}, {b}, {c} 的夹角不接近 90 度,返回 false。");
+                return false; // 角度不接近 90 度
+            }
+        }
+        return true;
+    }
+
+    /// <summary>
+    /// 判断是否是梯形
+    /// </summary>
+    private bool IsTrapezoid(Vector2[] points)
+    {
+        // 斜率容差范围,允许斜率差异较小
+        const float slopeThreshold = 0.2f;
+
+        // 计算对角线的斜率
+        float slope1 = (points[1].y - points[0].y) / (points[1].x - points[0].x);
+        float slope2 = (points[3].y - points[2].y) / (points[3].x - points[2].x);
+
+        // 如果对角线斜率差异小于容差范围,则认为是梯形
+        if (Mathf.Abs(slope1 - slope2) < slopeThreshold)
+        {
+            Debug.Log("对角线平行,判断为梯形。");
+            return true; // 对角线平行,返回 true
+        }
+        else
+        {
+            Debug.Log($"对角线斜率差异过大: {slope1} vs {slope2},不是梯形,返回 false。");
+            return false; // 斜率差异过大,不是梯形
+        }
+    }
+
+
+
+    //计算四边形面积
+    float CalculateArea(Vector2[] points)
+    {
+        float area = 0f;
+        int n = points.Length;
+        for (int i = 0; i < n; i++)
+        {
+            Vector2 current = points[i];
+            Vector2 next = points[(i + 1) % n];
+            area += current.x * next.y - current.y * next.x;
+        }
+        return Mathf.Abs(area) / 2f;
+    }
+    //确保点不共线
+    //判断点是否在同一条直线上(即共线),如果共线则无法形成四边形。可以通过计算任意三点的面积是否为 0 来验证是否共线:
+    bool ArePointsCollinear(Vector2 a, Vector2 b, Vector2 c)
+    {
+        float area = Mathf.Abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0f;
+        return Mathf.Approximately(area, 0f);
+    }
+    //确保边不相交
+    bool DoLinesIntersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
+    {
+        float cross(Vector2 p1, Vector2 p2, Vector2 p3)
+        {
+            return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
+        }
+
+        float d1 = cross(a, b, c);
+        float d2 = cross(a, b, d);
+        float d3 = cross(c, d, a);
+        float d4 = cross(c, d, b);
+
+        return d1 * d2 < 0 && d3 * d4 < 0;
+    }
+    //顺时针或逆时针排列顶点
+    bool ArePointsClockwise(Vector2[] points)
+    {
+        float sum = 0;
+        for (int i = 0; i < points.Length; i++)
+        {
+            Vector2 current = points[i];
+            Vector2 next = points[(i + 1) % points.Length];
+            sum += (next.x - current.x) * (next.y + current.y);
+        }
+        return sum < 0; // 小于0为顺时针,大于0为逆时针
+    }
+
+    #endregion
 }

+ 3 - 0
Assets/SmartBow/Scripts/Views/InfraredViewParts/Line.cs

@@ -14,6 +14,9 @@ namespace LineUI
         [SerializeField] private Vector2 quadrilateralSize = new Vector2(100, 100);
         [SerializeField] private Color quadColor = Color.red;
         [SerializeField] private List<Vector2> screenPositions = new List<Vector2>();
+        //获取当前的points
+        [HideInInspector]
+        public List<Vector2> ScreenPositions => screenPositions;
 
         [SerializeField] private Color maskColor = Color.red;
         //是否绘制内四边形

+ 39 - 12
Assets/SmartBow/Scripts/Views/InfraredViewParts/PointMarker.cs

@@ -7,7 +7,9 @@ using System.Linq;
 
 public class PointMarker : MonoBehaviour
 {
-    public Text instructionText; // 用于显示提示文字
+    //public Text instructionText; // 用于显示提示文字
+    [Tooltip("标记页面标题提示")]
+    [SerializeField] TextAutoLanguage2 instructionText;
     public Button undoButton;    // 撤回按钮
     public Button completeButton; // 完成按钮
     public bool bComplete = false;// 是否完成
@@ -18,8 +20,9 @@ public class PointMarker : MonoBehaviour
 
     public Image[] hintImages; // 依次存放左上、右上、右下、左下四个提示图片
 
-    private List<Vector2> markedPoints = new List<Vector2>();
-    private string[] directions = { "左上", "右上", "右下", "左下" };
+    //private List<Vector2> markedPoints = new List<Vector2>();
+    // { "左上", "右上", "右下", "左下" }
+    private string[] directions = { "TipTopLeft", "TipTopRight", "TipBottomRight", "TipBottomLeft" };
     private int currentPointIndex = 0; // 当前需要标记的点的索引
     private List<GameObject> markerObjects = new List<GameObject>(); // 记录标记物体
 
@@ -36,6 +39,7 @@ public class PointMarker : MonoBehaviour
 
         // 初始化时,显示第一个提示图片,其余隐藏
         ShowHintImage(0);
+
     }
     void OnDestroy()
     {
@@ -60,7 +64,7 @@ public class PointMarker : MonoBehaviour
                 v = Math.Clamp(v, 0, 1);
 
                 Vector2 pos = new Vector2(u * ScreenLocate.Main.getUVCTexture.width, v * ScreenLocate.Main.getUVCTexture.height);
-                markedPoints.Add(pos);
+                //markedPoints.Add(pos);
 
                 // 设置标记的位置
                 MarkPoint(new Vector2(u, v).pixelToLocalPosition_AnchorCenter(new Vector2(1, 1), markerParent.GetComponent<RectTransform>().rect));
@@ -96,13 +100,17 @@ public class PointMarker : MonoBehaviour
         if (location != null)
         {
             Vector2 localPosition = location.Value.pixelToLocalPosition_AnchorCenter(ScreenLocate.Main.CameraSize, infraredScreenPositioningView.Bg.rectTransform.rect);
-            markedPoints.Add(localPosition);
+            //markedPoints.Add(localPosition);
             MarkPoint(localPosition);
         }
     }
     void MarkPoint(Vector2 point)
     {
-        //markedPoints.Add(new Vector2(point.x * ScreenLocate.Main.getUVCTexture.width, point.y * ScreenLocate.Main.getUVCTexture.height));
+        if (!infraredScreenPositioningView.IsPointInMaskLine(point)) {
+            Debug.Log("不在梯形内部!");
+            instructionText.SetTextKey("TipMarkerError");
+            return;
+        }
         // 创建标记物体,并将其作为markerParent的子对象
         GameObject marker = Instantiate(markerPrefab, markerParent);
         marker.SetActive(true);
@@ -118,17 +126,35 @@ public class PointMarker : MonoBehaviour
         // 如果已经标记了四个点,显示完成按钮
         if (currentPointIndex == 4)
         {
-            instructionText.text = "标记完成!";
-            bComplete = true;
+            Vector2[] points = { 
+                markerObjects[0].transform.localPosition,
+                markerObjects[1].transform.localPosition,
+                markerObjects[2].transform.localPosition, 
+                markerObjects[3].transform.localPosition, 
+            };
+            bool result = infraredScreenPositioningView.IsValidQuadrilateral(points);
+            Debug.Log(result ? "是有效的四边形" : "不是有效的四边形");
+            if (result)
+            {
+                instructionText.SetTextKey("TipMarkComplete");
+                //PopupMgr.ins.ShowTipTop(TextAutoLanguage2.GetTextByKey("TipMarkComplete"));
+                bComplete = true;
+            }
+            else {
+                //不是有效是四边形时候,回退 一个
+                //instructionText.SetTextKey("TipMarkerError");
+                PopupMgr.ins.ShowTipTop(TextAutoLanguage2.GetTextByKey("TipQuadError"));
+                UndoLastPoint();
+            }
         }
     }
 
     void UndoLastPoint()
     {
-        if (markedPoints.Count > 0)
+        if (currentPointIndex > 0)
         {
             // 移除最后一个标记的点
-            markedPoints.RemoveAt(markedPoints.Count - 1);
+            //markedPoints.RemoveAt(markedPoints.Count - 1);
 
             // 删除最后一个标记的物体
             Destroy(markerObjects[markerObjects.Count - 1]);
@@ -149,7 +175,8 @@ public class PointMarker : MonoBehaviour
     {
         if (currentPointIndex < 4)
         {
-            instructionText.text = "请标记: " + directions[currentPointIndex] + "角的点";
+            instructionText.textFormatArgs = new object[] { TextAutoLanguage2.GetTextByKey(directions[currentPointIndex]) };
+            instructionText.SetTextKey("TipMiddle");
         }
     }
 
@@ -167,7 +194,7 @@ public class PointMarker : MonoBehaviour
 
     void CompletePoint() {
         if (!bComplete) {
-            Debug.Log("未完成");
+            //Debug.Log("未完成");
             //todo,加提示
             PopupMgr.ins.ShowTipTop(TextAutoLanguage2.GetTextByKey("Incomplete"));
             return;

binární
Assets/SmartBow/Textures/Guidance/Infrared/FrameLineBR.png


+ 123 - 0
Assets/SmartBow/Textures/Guidance/Infrared/FrameLineBR.png.meta

@@ -0,0 +1,123 @@
+fileFormatVersion: 2
+guid: c4de584750fb5904a9c7a265db1d19cc
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 12
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  vTOnly: 0
+  ignoreMasterTextureLimit: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 0
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 1
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 1
+  spriteTessellationDetail: -1
+  textureType: 8
+  textureShape: 1
+  singleChannelComponent: 0
+  flipbookRows: 1
+  flipbookColumns: 1
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  ignorePngGamma: 0
+  applyGammaDecoding: 0
+  cookieLightType: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 5e97eb03825dee720800000000000000
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+    nameFileIdTable: {}
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů