Переглянути джерело

Merge remote-tracking branch 'remotes/origin/master' into insertcoin

# Conflicts:
#	Assets/BowArrow/Scripts/CommonConfig.cs
合并算法修改
slambb 1 рік тому
батько
коміт
f156ad449e
30 змінених файлів з 3674 додано та 628 видалено
  1. 193 31
      Assets/BowArrow/Fonts/HarmonyOS_Sans_SC_Regular SDF.asset
  2. 436 27
      Assets/BowArrow/InfraredCamera/InfraredDemo.cs
  3. 699 162
      Assets/BowArrow/InfraredCamera/Resources/InfraredDemo.prefab
  4. 1 1
      Assets/BowArrow/Modules/InfraredGuider/InfraredGuider.prefab
  5. 26 12
      Assets/BowArrow/Scripts/Components/TextAutoLanguage2/Resources/TextAutoLanguage2/cn.json
  6. 31 14
      Assets/BowArrow/Scripts/Components/TextAutoLanguage2/Resources/TextAutoLanguage2/en.json
  7. 1 1
      Assets/BowArrow/Scripts/Manager/AudioMgr.cs
  8. 19 0
      Assets/InfraredProject/InfraredCamera/Scripts/InfraredCameraHelper.cs
  9. BIN
      Assets/InfraredProject/WebCamera/Image/Debug1.jpg
  10. 123 0
      Assets/InfraredProject/WebCamera/Image/Debug1.jpg.meta
  11. BIN
      Assets/InfraredProject/WebCamera/Image/Debug2.jpg
  12. 123 0
      Assets/InfraredProject/WebCamera/Image/Debug2.jpg.meta
  13. BIN
      Assets/InfraredProject/WebCamera/Image/Debug3.jpg
  14. 123 0
      Assets/InfraredProject/WebCamera/Image/Debug3.jpg.meta
  15. 8 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify.meta
  16. 163 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineGuess.cs
  17. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineGuess.cs.meta
  18. 6 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineIdentified.cs
  19. 0 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineIdentified.cs.meta
  20. 202 64
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/ScreenIdentification.cs
  21. 17 4
      Assets/InfraredProject/WebCamera/Script/ZIM/ScreenLocate.cs
  22. 1 1
      Assets/InfraredProject/WebCamera/zimWebCamera.unity
  23. 661 94
      Assets/SmartBow/Resources/SmartBow/Prefabs/Views/Home/InfraredScreenPositioningView.prefab
  24. 85 11
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/LineGenerator.cs
  25. 533 178
      Assets/SmartBow/Scripts/Views/InfraredViewParts/InfraredScreenPositioningView.cs
  26. 5 0
      Assets/SmartBow/Scripts/Views/InfraredViewParts/Line.cs
  27. 79 12
      Assets/SmartBow/Scripts/Views/InfraredViewParts/PointMarker.cs
  28. 5 16
      Assets/SmartBow/Scripts/Views/SettingsView.cs
  29. BIN
      Assets/SmartBow/Textures/Guidance/Infrared/FrameLineBR.png
  30. 123 0
      Assets/SmartBow/Textures/Guidance/Infrared/FrameLineBR.png.meta

Різницю між файлами не показано, бо вона завелика
+ 193 - 31
Assets/BowArrow/Fonts/HarmonyOS_Sans_SC_Regular SDF.asset


+ 436 - 27
Assets/BowArrow/InfraredCamera/InfraredDemo.cs

@@ -10,7 +10,7 @@ using System;
 using SmartBowSDK;
 using ZIM.Unity;
 
-public class InfraredDemo  : JCUnityLib.ViewBase
+public class InfraredDemo : JCUnityLib.ViewBase
 {
     public static bool DebugInEditor = true;
     public static InfraredDemo _ins;
@@ -61,11 +61,14 @@ public class InfraredDemo  : JCUnityLib.ViewBase
             mBtnSee.interactable = false;
             mBtnSee.transform.Find("Text").GetComponent<Text>().text = "等待初始化..";
         }
-   
+
         //直接初始化一次
         //InitInfraredCamera();
 
         Debug.Log("[InfraredDemo] Start Function!");
+        initToggle();
+        initExport();
+        InitFullScreen();
 
 #if UNITY_STANDALONE_WIN 
         _calibrationFixedText.transform.parent.gameObject.SetActive(false);
@@ -88,14 +91,14 @@ public class InfraredDemo  : JCUnityLib.ViewBase
     void Update()
     {
         UpdateInfraredCamera();
-       //UpdateDebugScreenPoint();
+        //UpdateDebugScreenPoint();
     }
 
     public void OnClick_See()
     {
         SetVisiable(!_visiable);
     }
-   
+
     //初始化一次 initScreenLocateManual
     bool hasCalled = false;
 
@@ -213,9 +216,9 @@ public class InfraredDemo  : JCUnityLib.ViewBase
     public ParamFloatValue captureValue = new ParamFloatValue("ic_captureValue", 30.0f);
     public ParamFloatValue delayValue = new ParamFloatValue("ic_delayValue", 30.0f);
 
-  
+
     //红外准心,默认打开吧
-    public ParamFloatValue crosshairValue = new ParamFloatValue("ic_crosshairValue",1);
+    public ParamFloatValue crosshairValue = new ParamFloatValue("ic_crosshairValue", 1);
 
     //野鸭测试存储
     public ParamFloatValue duckHunterLevel = new ParamFloatValue("test_duckHunterLevelValue", 1);
@@ -275,7 +278,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         for (int i = 0; i < sliderStrArray.Length; i++)
         {
             string typeStr = sliderStrArray[i];
-           // UInt64 _VALUE = cameraInfo.GetTypeByName(typeStr);
+            // UInt64 _VALUE = cameraInfo.GetTypeByName(typeStr);
             //不支持的,跳过
             bool bContains = cameraInfo.ContainsKey(typeStr);
             if (!bContains) continue;
@@ -365,14 +368,14 @@ public class InfraredDemo  : JCUnityLib.ViewBase
                         slider.value = _currentValue;
                         textObj.text = _currentValue + "";
                     }
-                
+
 
                     //如果是曝光slider
                     if (typeStr == "CTRL_AE_ABS")
                     {
                         CTRLAEABSSlider = slider;
                         slider.interactable = !bAutoAE;
-                    } 
+                    }
                     slider.onValueChanged.AddListener((newValue) =>
                     {
                         var _value = Mathf.FloorToInt(newValue);
@@ -417,7 +420,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         for (int i = 0; i < sliderStrArray.Length; i++)
         {
             string typeStr = sliderStrArray[i];
-           // UInt64 _VALUE = currentCameraInfo.GetTypeByName(typeStr);
+            // UInt64 _VALUE = currentCameraInfo.GetTypeByName(typeStr);
             bool bContains = currentCameraInfo.ContainsKey(typeStr);
             if (!bContains) continue;
 
@@ -474,7 +477,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
 
     //windows 相机
     string[] sliderNameArrayPC = new string[]{
-        "亮度", 
+        "亮度",
         "对比度",};
     string[] sliderStrArrayPC = new string[]{
         "PU_BRIGHTNESS",
@@ -532,7 +535,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
                         pcCONTRAST.Set(_value);
                     });
                 }
-               
+
             }
 
         }
@@ -563,7 +566,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         }
     }
 
-   #endregion
+    #endregion
     void InitInfraredCamera()
     {
         if (_inited) return;
@@ -601,7 +604,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
                 if (CameraToLook.ins != null) CameraToLook.ins.localRotation = quat;
                 if (SB_EventSystem.ins) SB_EventSystem.ins.MoveSimulateMouseByInfrared(point);
                 //移动目标游戏
-                if(GeneratingTarget.gm!=null)  GeneratingTarget.gm.shootingEvent.OnPositionUpdate(point);
+                if (GeneratingTarget.gm != null) GeneratingTarget.gm.shootingEvent.OnPositionUpdate(point);
             };
             infraredCameraHelper.OnUVCIsReady += (UVCManager.CameraInfo camera) =>
             {
@@ -612,7 +615,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
                 mBtnSee.transform.Find("Text").GetComponent<Text>().text = "调试红外";
                 //延迟重新设置一次分辨率
                 StartCoroutine(delayInitOhterInfo(camera));
-               
+
             };
 
             //屏幕变化时候
@@ -652,7 +655,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         _dropdownResolution2.value = index;
         _dropdownResolution2.RefreshShownValue();
         // 处理 Dropdown 的选择变化
-        _dropdownResolution2.onValueChanged.AddListener(index => { OnResolutionChanged(index);});
+        _dropdownResolution2.onValueChanged.AddListener(index => { OnResolutionChanged(index); });
 #endif
 
 #if UNITY_STANDALONE_WIN || UNITY_EDITOR
@@ -661,7 +664,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         _btnAdjusting.transform.Find("Text").GetComponent<Text>().text = "调整PC参数";
         _dropdownResolution.gameObject.SetActive(false);
         _dropdownResolution2.gameObject.SetActive(false);
-    
+
 #endif
 
         //震动阈值
@@ -678,7 +681,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         SetDelayValue(delayValue.Get());
         _sliderDelay.onValueChanged.AddListener(SetDelayValue);
 
-       
+
 
         //绘制线段
         SetLineWidth(lineWidth.Get());
@@ -701,7 +704,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
     /// </summary>
     /// <returns></returns>
     public bool bInitCrosshairShow() {
-       return UserSettings.ins.openCrossHair && getCrosshairValue() == 1 ? true : false;
+        return UserSettings.ins.openCrossHair && getCrosshairValue() == 1 ? true : false;
     }
 
     /// <summary>
@@ -709,11 +712,11 @@ public class InfraredDemo  : JCUnityLib.ViewBase
     /// </summary>
     /// <returns></returns>
     public int getCrosshairValue() {
-       return (int)crosshairValue.Get();
+        return (int)crosshairValue.Get();
     }
 
     public void setCrosshairValue(bool bshow) {
-        crosshairValue.Set(bshow?1:0);
+        crosshairValue.Set(bshow ? 1 : 0);
     }
 
     public void onStartPreview()
@@ -782,8 +785,35 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         //_cameraRender.material = infraredCameraHelper.GetCameraMaterial();
         if (ScreenLocate.Main.getUVCTexture)
         {
-            if(_cameraRender.texture ==null || ScreenLocate.Main.getUVCTexture.GetNativeTexturePtr() != _cameraRender.texture.GetNativeTexturePtr())
-               _cameraRender.texture = ScreenLocate.Main.getUVCTexture;
+            if (isFullscreen || ScreenLocate.Main.OutputTextures[4] == null)
+            {
+                if (_cameraRender.texture == null || ScreenLocate.Main.getUVCTexture.GetNativeTexturePtr() != _cameraRender.texture.GetNativeTexturePtr())
+                    _cameraRender.texture = ScreenLocate.Main.getUVCTexture;
+
+                //ScreenLocate.Main.OutputTextures[4] == null 并且 摄像机缩小的情况下。显示适配线段
+                if (!isFullscreen) SetAllToggle(true);
+            }
+            else if (ScreenLocate.Main.OutputTextures[4] != null)
+            {
+                //缩小的情况下。显示黑白色差的合成图
+                if (_cameraRender.texture == null || ScreenLocate.Main.OutputTextures[4] != null && ScreenLocate.Main.OutputTextures[4].GetNativeTexturePtr() != _cameraRender.texture.GetNativeTexturePtr())
+                    _cameraRender.texture = ScreenLocate.Main.OutputTextures[4];
+
+                //底图缩小的情况下。不显示适配线段,并且隐藏points
+                if (!isFullscreen) {
+                    if (showPoints) {
+                        Transform pointsTF2 = _cameraRender.transform.Find("Points");
+                        for (int i = 0; i < pointsTF2.childCount; i++)
+                        {
+                            Transform pointTF = pointsTF2.GetChild(i);
+                            pointTF.gameObject.SetActive(false);
+                            showPoints = false;
+                        }
+                    }
+                    SetAllToggle(false);
+                } 
+            }
+           
             //_cameraRender.SetNativeSize();
             // _MaintainAspectRatio.AdjustSize();
         }
@@ -813,13 +843,14 @@ public class InfraredDemo  : JCUnityLib.ViewBase
             //渲染固定摄像机分辨率
             SetCalibrationFixedText(_sl.mUVCCameraInfo.CurrentCalibrationResolution);
             //渲染摄像机大小
-            if(_sl.mUVCCameraInfo != null) SetCameraSizeText(_sl.mUVCCameraInfo.Size);
+            if (_sl.mUVCCameraInfo != null) SetCameraSizeText(_sl.mUVCCameraInfo.Size);
             //渲染摄像机识别点位置
             SetQuadUnityVectorListText(_sl.PrintVector2List(ScreenLocate.quadUnityVectorList));
 
         }
     }
 
+    bool showPoints = false;
     public void SetLocatePointsToCameraRender(List<Vector2> points, float w, float h)
     {
         Transform pointsTF2 = _cameraRender.transform.Find("Points");
@@ -833,6 +864,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
                 Vector2 pos = points[i];
                 pointTF.localPosition = pos.pixelToLocalPosition_AnchorCenter(texSize, _cameraRender.rectTransform.rect);
                 pointTF.gameObject.SetActive(true);
+                showPoints = true;
             }
         }
         else {
@@ -840,10 +872,374 @@ public class InfraredDemo  : JCUnityLib.ViewBase
             {
                 Transform pointTF = pointsTF2.GetChild(i);
                 pointTF.gameObject.SetActive(false);
+                showPoints = false;
+            }
+        }
+        //绘制数据
+        DrawTestLine();
+    }
+
+    #region 测试数据绘制
+    [SerializeField] private Toggle curToggle;
+    [SerializeField] private Toggle autoToggle; // 控制 AutoUILineGenerator 的 Toggle
+    [SerializeField] private Toggle semiAutoToggle; // 控制 SemiAutoUILineGenerator 的 Toggle
+    [SerializeField] private Toggle manualToggle; // 控制 ManualUILineGenerator 的 Toggle
+
+    [SerializeField] LineGenerator CurUILineGenerator;//当前结果屏幕线条
+    [SerializeField] LineGenerator AutoUILineGenerator;//第一个结果屏幕线条
+    [SerializeField] LineGenerator SemiAutoUILineGenerator;//第二个结果屏幕线条
+    [SerializeField] LineGenerator ManualUILineGenerator;//第三个结果屏幕线条
+    [SerializeField] private Dropdown dropdown; // 引用场景中的 Dropdown 组件
+
+    [SerializeField] RawImage _cameraRenderTest5;
+    [SerializeField] RawImage _cameraRenderTest6;
+
+    void DrawTestLine()
+    {
+        Vector2 texSize = ScreenLocate.Main.getUVCCameraInfoSize;
+
+        // 判断 texSize 是否为有效值
+        if (texSize == Vector2.zero)
+        {
+            Debug.LogWarning("Texture size is zero, cannot proceed with line drawing.");
+            return;
+        }
+
+        // 设置三个屏幕四边形和线条生成器
+        QuadrilateralInCamera[] screenQuads = new QuadrilateralInCamera[] {
+            ScreenLocate.Main.ScreenIdentification?.QuadAuto,
+            ScreenLocate.Main.ScreenIdentification?.QuadSemiAuto,
+            ScreenLocate.Main.ScreenIdentification?.QuadManual
+        };
+
+        LineGenerator[] lineGenerators = new LineGenerator[] {
+            AutoUILineGenerator,
+            SemiAutoUILineGenerator,
+            ManualUILineGenerator
+        };
+
+        for (int i = 0; i < screenQuads.Length; i++)
+        {
+            if (screenQuads[i] == null)
+            {
+                Debug.LogWarning($"Screen quadrilateral {i} is null, skipping this line.");
+                continue;
+            }
+
+            if (lineGenerators[i] == null)
+            {
+                Debug.LogWarning($"Line generator {i} is null, skipping this line.");
+                continue;
+            }
+
+            lineGenerators[i].Points = ConvertQuadToPoints(screenQuads[i], texSize);
+
+
+        }
+
+
+        // 清除旧选项并填充新的选项
+        dropdown.ClearOptions();
+        List<string> options = new List<string>();
+        //for (int i = 0; i < screenQuads.Length; i++)
+        //{
+        //    options.Add($"Quad {i + 1}");
+        //}
+        options.Add("自动识别");
+        options.Add("半自动识别");
+        options.Add("手动识别");
+        dropdown.AddOptions(options);
+
+        // 添加监听器来处理选择事件
+        dropdown.onValueChanged.AddListener((index) => {
+            OnDropdownValueChanged(index, screenQuads);
+        });
+
+        if (screenQuads[0] != null || screenQuads[1] != null) 
+        {
+            Texture texImage6 = ScreenLocate.Main.OutputTextures[5];
+            if (texImage6 != null) _cameraRenderTest6.texture = texImage6;
+        }
+        if (ScreenLocate.quadUnityVectorList.Count == 4) {
+            //绘制白色线段
+            var lo = new Vector2(-0.5f, -0.5f);
+            CurUILineGenerator.Points = new Vector2[4] {
+                2 * (ScreenLocate.quadUnityVectorList[0] + lo),
+                2 * (ScreenLocate.quadUnityVectorList[1] + lo),
+                2 * (ScreenLocate.quadUnityVectorList[3] + lo),
+                2 * (ScreenLocate.quadUnityVectorList[2] + lo) };
+        }
+    }
+    // 当下拉框选项更改时调用的方法
+    private void OnDropdownValueChanged(int index, QuadrilateralInCamera[] screenQuads)
+    {
+        if (index >= 0 && index < screenQuads.Length)
+        {
+            if (screenQuads[index] != null)
+            {
+                QuadrilateralInCamera selectedQuad = screenQuads[index];
+                Debug.Log($"Selected Quad {index + 1}: {selectedQuad}");
+                CurUILineGenerator.Points = ConvertQuadToPoints(selectedQuad, ScreenLocate.Main.getUVCCameraInfoSize);
+            }
+            else
+            {
+                Debug.LogWarning($"Selected Quad {index + 1} is null.");
             }
         }
+        else
+        {
+            Debug.LogWarning("所选索引超出范围!");
+        }
+    }
+
+    /// <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)
+        {
+            Debug.LogWarning("Invalid quadrilateral data provided.");
+            return new Vector2[0]; // 返回空数组以避免异常
+        }
+
+        Vector2 offset = new Vector2(-0.5f, -0.5f); // 假设 'lo' 是某个已定义的偏移量
+        return new Vector2[4] {
+        2 * (new Vector2(quad.Quad[0].x / texSize.x, quad.Quad[0].y / texSize.y) + offset),
+        2 * (new Vector2(quad.Quad[1].x / texSize.x, quad.Quad[1].y / texSize.y) + offset),
+        2 * (new Vector2(quad.Quad[3].x / texSize.x, quad.Quad[3].y / texSize.y) + offset),
+        2 * (new Vector2(quad.Quad[2].x / texSize.x, quad.Quad[2].y / texSize.y) + offset)
+         };
     }
 
+    public Vector2[] ConvertPointsToCoordinates(Vector2[] points, Vector2 texSize, Vector2 pivot)
+    {
+        if (points == null || points.Length < 1)
+        {
+            Debug.LogWarning("Invalid point data provided.");
+            return new Vector2[0]; // 返回空数组以避免异常
+        }
+
+        Vector2 offset = new Vector2(-0.5f, -0.5f); // 将坐标从中心对齐到左下角对齐
+
+        List<Vector2> transformedPoints = new List<Vector2>();
+
+        // 遍历所有传入的点进行转换
+        foreach (var point in points)
+        {
+            // 转换每个点,考虑 texSize 和 pivot 偏移
+            Vector2 transformedPoint = 2 * ((point / texSize) + offset + pivot);
+            transformedPoints.Add(transformedPoint);
+        }
+
+        return transformedPoints.ToArray();
+    }
+
+
+    void initToggle()
+    {
+        // 监听 Toggle 值的变化
+        if (curToggle != null)
+            curToggle.onValueChanged.AddListener(isOn => ToggleLineGenerator(CurUILineGenerator, isOn));
+
+        // 监听 Toggle 值的变化
+        if (autoToggle != null)
+            autoToggle.onValueChanged.AddListener(isOn => ToggleLineGenerator(AutoUILineGenerator, isOn));
+
+        if (semiAutoToggle != null)
+            semiAutoToggle.onValueChanged.AddListener(isOn => ToggleLineGenerator(SemiAutoUILineGenerator, isOn));
+
+        if (manualToggle != null)
+            manualToggle.onValueChanged.AddListener(isOn => ToggleLineGenerator(ManualUILineGenerator, isOn));
+    }
+
+    // 控制 LineGenerator 的启用/禁用
+    private void ToggleLineGenerator(LineGenerator lineGenerator, bool isOn)
+    {
+        if (lineGenerator != null && lineGenerator.enabled != isOn)
+        {
+            lineGenerator.enabled = isOn;
+            Debug.Log($"{lineGenerator.name} is now {(isOn ? "enabled" : "disabled")}");
+        }
+    }
+    /// <summary>
+    /// 设置全部显示
+    /// </summary>
+    /// <param name="value"></param>
+    private void SetAllToggle(bool value) {
+        ToggleLineGenerator(CurUILineGenerator, value);
+        ToggleLineGenerator(AutoUILineGenerator, value);
+        ToggleLineGenerator(SemiAutoUILineGenerator, value);
+        ToggleLineGenerator(ManualUILineGenerator, value);
+    }
+    /// <summary>
+    /// 放大时候同步一次toggle
+    /// </summary>
+    private void SyncAllToggle()
+    {
+        ToggleLineGenerator(CurUILineGenerator, curToggle.isOn);
+        ToggleLineGenerator(AutoUILineGenerator, autoToggle.isOn);
+        ToggleLineGenerator(SemiAutoUILineGenerator, semiAutoToggle.isOn);
+        ToggleLineGenerator(ManualUILineGenerator, manualToggle.isOn);
+    }
+
+    // 下拉菜单用于显示纹理名称
+    public Dropdown textureDropdown;
+    // 按钮用于导出选中的纹理
+    public Button exportSelectedButton;
+    // 按钮用于导出所有纹理
+    public Button exportAllButton;
+    // 定义纹理名称
+    string[] textureNames = {
+            "A屏幕原图",
+            "B半自动识别线段",
+            "C黑白色差",
+            "D识别结果",
+            "E色差叠加识别结果",
+            "F半自动备选线段"
+        };
+    void initExport() {
+     
+        // 设置下拉菜单的选项
+        textureDropdown.ClearOptions();
+        textureDropdown.AddOptions(new System.Collections.Generic.List<string>(textureNames));
+
+        // 添加导出选中纹理的按钮点击事件
+        exportSelectedButton.onClick.AddListener(() => SaveTextureToLocal(textureDropdown.value));
+
+        // 添加导出所有纹理的按钮点击事件
+        exportAllButton.onClick.AddListener(SaveAllTexturesToLocal);
+    }
+    /// <summary>
+    /// 导出所有纹理
+    /// </summary>
+    public void SaveAllTexturesToLocal()
+    {
+        for (int i = 0; i < textureNames.Length; i++)
+        {
+            SaveTextureToLocal(i);  // 调用之前定义的保存方法
+        }
+    }
+    /// <summary>
+    /// 导出图片
+    /// </summary>
+    /// <param name="index"></param>
+    public void SaveTextureToLocal(int index)
+    {
+        Texture2D[] outputTexture2D = ScreenLocate.Main.OutputTextures;
+        if (outputTexture2D == null || index >= outputTexture2D.Length || outputTexture2D[index] == null)
+        {
+            Debug.LogError("Invalid texture index or texture is null.index:"+ index);
+            return;
+        }
+
+        // 定义保存路径
+        string time = DateTime.Now.ToString("yyyyMMdd_HHmmss");
+        string baseName = $"{time}_Texture_{index}";
+        // 检查索引是否在命名数组内
+        string fileName = index < textureNames.Length ? textureNames[index] : $"Texture_{index}";
+#if UNITY_ANDROID && !UNITY_EDITOR_WIN
+        string path = System.IO.Path.Combine("/storage/emulated/0/Download", $"{baseName}_{fileName}.png"); //public
+#else
+        string path = System.IO.Path.Combine(Application.persistentDataPath, $"{baseName}_{fileName}.png");
+#endif
+
+
+        // 编码纹理为 PNG 格式
+        byte[] bytes = outputTexture2D[index].EncodeToPNG();
+
+        // 将字节写入文件
+        System.IO.File.WriteAllBytes(path, bytes);
+
+        Debug.Log($"Texture saved as: {path}");
+    }
+
+    //下面是处理对象放大缩小
+    public RectTransform cameraObj1; // 拖拽你的 CameraSize1 对象到此字段
+    public Button toggleButton;      // 缩小时使用的按钮
+    private Vector2 originalSize;
+    private Vector2 originalAnchoredPosition; // 使用 anchoredPosition 代替 localPosition
+    private Vector2 originalAnchorMin;
+    private Vector2 originalAnchorMax;
+    private int originalSiblingIndex; // 保存初始的层级索引
+    private bool isFullscreen = false;
+
+    /// <summary>
+    /// 测试效果屏幕数据初始化
+    /// </summary>
+    void InitFullScreen() {
+
+        if (cameraObj1 != null)
+        {
+            // 保存初始大小、位置和层级索引
+            originalSize = cameraObj1.sizeDelta;
+            originalAnchoredPosition = cameraObj1.anchoredPosition;
+            originalAnchorMin = cameraObj1.anchorMin; // 记录初始 anchorMin
+            originalAnchorMax = cameraObj1.anchorMax; // 记录初始 anchorMax
+            originalSiblingIndex = cameraObj1.GetSiblingIndex();
+
+            // 为 cameraObj1 添加点击事件
+            cameraObj1.GetComponent<Button>().onClick.AddListener(ToggleFullscreen);
+
+            toggleButton.transform.parent.gameObject.SetActive(false);
+        }
+
+        if (toggleButton != null)
+        {
+            // 为按钮添加点击事件以恢复大小
+            toggleButton.onClick.AddListener(RestoreSize);
+        }
+    }
+    void ToggleFullscreen()
+    {
+        if (cameraObj1 == null) return;
+
+        if (!isFullscreen)
+        {
+            // 设置锚点为全屏,调整位置和偏移量
+            cameraObj1.anchorMin = Vector2.zero;
+            cameraObj1.anchorMax = Vector2.one;
+            cameraObj1.offsetMin = Vector2.zero;
+            cameraObj1.offsetMax = Vector2.zero;
+            cameraObj1.anchoredPosition = Vector3.zero;
+            cameraObj1.SetSiblingIndex(cameraObj1.parent.childCount - 1); // 设置为最上层
+
+            //刷新一次点
+            SetLocatePointsToCameraRender(ScreenLocate.quadUnityVectorList, 1, 1);
+
+            toggleButton.transform.parent.gameObject.SetActive(true);
+            isFullscreen = true;
+
+            //放大情况根据toggle来显示
+            SyncAllToggle();
+
+        }
+    }
+
+    void RestoreSize()
+    {
+        if (cameraObj1 == null || !isFullscreen) return;
+
+        // 恢复锚点和偏移量
+        cameraObj1.anchorMin = originalAnchorMin; // 使用在 Start() 中记录的初始锚点
+        cameraObj1.anchorMax = originalAnchorMax; // 使用在 Start() 中记录的初始锚点
+        cameraObj1.sizeDelta = originalSize;
+        cameraObj1.anchoredPosition = originalAnchoredPosition; // 恢复为初始 anchoredPosition
+        cameraObj1.SetSiblingIndex(originalSiblingIndex); // 恢复到原本的层级索引
+
+        //刷新一次点
+        SetLocatePointsToCameraRender(ScreenLocate.quadUnityVectorList, 1, 1);
+
+        toggleButton.transform.parent.gameObject.SetActive(false);
+        isFullscreen = false;
+    }
+#endregion
+
+
+
     public void SetLocatePointsToCameraRenderTest(List<Vector2> points, float w, float h)
     {
         Transform pointsTF2 = _cameraRender.transform.Find("PointsTest");
@@ -1030,6 +1426,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);
+    }
 
 
 
@@ -1047,10 +1456,10 @@ public class InfraredDemo  : JCUnityLib.ViewBase
         _sliderDelay.transform.Find("Value").GetComponent<Text>().text = delayValue.Get().ToString("f1");
         infraredCameraHelper.SetDelay((int)delayValue.Get());
     }
-    #endregion
+#endregion
 
 
-    #region 相机感光度(默认修改对比度)
+#region 相机感光度(默认修改对比度)
     public void onSetSliderCustomValue(Slider _slider, float min = 0.0f, float max = 10.0f)
     {
         onSetSliderValue(_slider, "PU_CONTRAST", min, max);
@@ -1237,7 +1646,7 @@ public class InfraredDemo  : JCUnityLib.ViewBase
     {
         _quadUnityVectorListText.text = v;
     }
-    #endregion
+#endregion
 
 }
 public class ParamFloatValue

Різницю між файлами не показано, бо вона завелика
+ 699 - 162
Assets/BowArrow/InfraredCamera/Resources/InfraredDemo.prefab


+ 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

+ 26 - 12
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扣动扳机进行标记(四角位置标签)",
-  "ScreenPositioningSuccessful-title": "屏幕定位成功,请选择最佳方案",
+  "TitleTipMarker2": "操作设备对准电视,按提示依次用光点瞄准梯形框中电视屏幕内定位框的四个角,并单击准心键进行标记。标记需位于定位框内。",
+  "TipMiddle": "请标记:{0}角的点",
+  "TipTopLeft": "左上",
+  "TipTopRight": "右上",
+  "TipBottomRight": "右下",
+  "TipBottomLeft": "左下",
+  "TipMarkComplete": "标记完成!",
+  "TipMarkerError": "定位失败,需按要求重新标记。",
+  "TipQuadError": "定位失败,不是有效的四边形。",
+  "TipAutoEnd": "① 如无法识别出屏幕,需屏蔽环境干扰光源后,点击“自动”按键重新识别\n② 如白框与电视屏幕匹配,点击“确认”按键,跳过定位流程 \n③ 如白框与电视屏幕不匹配,点击“环境测光”按键,继续定位流程",
+  "TipLightCtrl": "操作设备对准电视,将光点位于梯形框中电视屏幕,红色十字会在光点上,并随光点移动;如没有,需要找出干扰光源关闭它、调整激光定位器位置或调小亮度和对比度",
+
+  "ScreenPositioningSuccessful-title": "\u3000\u3000请用手指点击选择最合适的屏幕区域结果。如未发现明显差异,请任选其一。如识别到的屏幕区域不合适,请点击“返回”重新识别。",
   "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如发现光标不顺滑,或光标的移动范围不够大,请调整图像亮度或对比度。",

+ 31 - 14
Assets/BowArrow/Scripts/Components/TextAutoLanguage2/Resources/TextAutoLanguage2/en.json

@@ -450,8 +450,8 @@
   "Connect_BLE_Break": "Break",
   "Connect_BLE_None": "None",
 
-  "Connect_Device_Archery": "Intelligent Archery",
-  "Connect_Device_Gun": "Intelligent Gun",
+  "Connect_Btn_Archery": "Intelligent Archery",
+  "Connect_Btn_Gun": "Intelligent Gun",
 
   "Connect_Btn_Screen": "Screen Positioning",
   "Connect_Btn": "Connect Device",
@@ -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 that the \nentire 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": "Screen Marking",
   "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)",
-  "ScreenPositioningSuccessful-title": "Screen positioning successful, please choose the best solution",
+  "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": "\u3000\u3000Please tap with your finger to select the most suitable screen region result. If no obvious difference is found, choose any one. If the identified screen region is not suitable, click 'Back' to re-recognize.",
   "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.Please 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.",

+ 1 - 1
Assets/BowArrow/Scripts/Manager/AudioMgr.cs

@@ -117,7 +117,7 @@ public class AudioMgr : MonoBehaviour
         if (name == "rabbit_injured") {
             name = "rabbit_injured2";
             audioSource.volume = 1;
-            Debug.Log("兔子音效 Volume =" + audioSource.volume);
+            //Debug.Log("兔子音效 Volume =" + audioSource.volume);
         }
         this.Play("Audios/Animal/" + name, audioSource);
     }

+ 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>
         /// 初始化一次本地的记录点

BIN
Assets/InfraredProject/WebCamera/Image/Debug1.jpg


+ 123 - 0
Assets/InfraredProject/WebCamera/Image/Debug1.jpg.meta

@@ -0,0 +1,123 @@
+fileFormatVersion: 2
+guid: 240a2a431fcaafc41955f8c508eb82a1
+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: 1
+  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: 

BIN
Assets/InfraredProject/WebCamera/Image/Debug2.jpg


+ 123 - 0
Assets/InfraredProject/WebCamera/Image/Debug2.jpg.meta

@@ -0,0 +1,123 @@
+fileFormatVersion: 2
+guid: cfeda3b3fd30c9743888012459064ca8
+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: 1
+  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: 

BIN
Assets/InfraredProject/WebCamera/Image/Debug3.jpg


+ 123 - 0
Assets/InfraredProject/WebCamera/Image/Debug3.jpg.meta

@@ -0,0 +1,123 @@
+fileFormatVersion: 2
+guid: a36d3e6d746b56c4980638b4970dae88
+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: 1
+  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: 

+ 8 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 84106b35ce9944345a6bb51787c57815
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 163 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineGuess.cs

@@ -0,0 +1,163 @@
+using o0.Geometry2D.Float;
+using o0.Num;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace o0.Project
+{
+    // 大于阈值则猜测是Line,但是这个类不考虑梯度,外部仍需结合梯度评估
+    public class LineGuess
+    {
+        public static float isLineThreshold = 1.0f;
+        // 记录分段的线段数据
+        class SubLineData
+        {
+            public List<float> DataList;    
+            public List<float> DiffList;    // 数据与平均值的差
+
+            // 幅度
+            float diffExtent = -1;
+            float dataExtent = -1;
+
+            public SubLineData(float data, float diff)
+            {
+                DataList = new List<float> { data };
+                DiffList = new List<float> { diff };
+            }
+
+            public void Add(float data, float diff)
+            {
+                DataList.Add(data);
+                DiffList.Add(diff);
+            }
+
+            // 得到差异的幅度
+            //public float GetDiffExtent()
+            //{
+            //    if (diffExtent == -1)
+            //    {
+            //        var sum = 0.0f;
+            //        foreach (var i in DiffList)
+            //            sum += i * i;
+            //        return diffExtent = sum / DiffList.Count;
+            //    }
+            //    else
+            //        return diffExtent;
+            //}
+
+            // 得到数据的幅度(和0比)
+            public float GetDataExtent()
+            {
+                if (dataExtent == -1)
+                {
+                    var sum = 0.0f;
+                    foreach (var i in DataList)
+                        sum += i * i;
+                    return dataExtent = sum / DataList.Count;
+                }
+                else
+                    return dataExtent;
+            }
+        }
+
+        public List<Matrix> ScreenLocateMatList;
+        public float GradientLength;
+        public float MinLineLength;
+
+        public LineGuess(List<Matrix> screenLocateMatList, float gradientLength, float minLineLength)
+        {
+            ScreenLocateMatList = screenLocateMatList;
+            GradientLength = gradientLength;
+            MinLineLength = minLineLength;
+        }
+
+        // 猜测是直线返回true
+        public bool GuessIsLine(LineIdentified line) => Guess(line) > isLineThreshold;
+
+        // 越大越可能是直线
+        public float Guess(LineIdentified line)
+        {
+            var dir = (line.Line.B - line.Line.A).Normalized;
+            var vertical = new Vector(-dir.y, dir.x) * (GradientLength / 2);
+            var verticalNormal = vertical.Normalized;
+            float lineABStep = MinLineLength / 15;
+
+            float lineGuessStart;
+            float lineGuessEnd;
+            if (line.Line.Length > 2 * MinLineLength)
+            {
+                lineGuessStart = 7.5f * lineABStep;
+                lineGuessEnd = line.Line.Length - 7.5f * lineABStep;        // 线的两端不计算
+            }
+            else
+            {
+                lineGuessStart = lineABStep;
+                lineGuessEnd = line.Line.Length - lineABStep;
+            }
+
+            var gradData = new List<float>();      // 直线两侧梯度的平均值,垂直于直线变化
+
+            var a0 = line.Line.A - vertical;
+            var gradStep = 1.5f;
+            for (float n = 0; n < GradientLength; n += gradStep)
+            {
+                var aN = a0 + verticalNormal * n;
+                var gradThisLine = new List<float>();
+                for (float i = lineGuessStart; i <= lineGuessEnd; i += lineABStep)
+                {
+                    var point = aN + dir * i;
+                    gradThisLine.Add(ScreenLocateMatList[line.Batch][(int)point.x, (int)point.y]);
+                }
+                gradData.Add(gradThisLine.Mean());
+            }
+            //Debug.Log(line.Gradient + ", " + line.GradientDegree + ", " + gradData.ToJson());
+            if (gradData.Count <= 3)
+                return 400;
+
+            var gradChangeData = new List<float>();
+            for (int j = 1; j < gradData.Count; j++)
+                gradChangeData.Add(Math.Abs(gradData[j] - gradData[j - 1]));
+            var meanChange = gradChangeData.Mean();
+            var diff = gradChangeData[0] - meanChange;
+
+            var subList = new List<SubLineData>() { new SubLineData(gradChangeData[0], diff) };
+
+            for (int i = 1; i < gradChangeData.Count; i++)
+            {
+                var newDiff = gradChangeData[i] - meanChange;
+                if ((newDiff <= 0 && diff <= 0) || (newDiff > 0 && diff > 0))   // 同号,是一个分段
+                    subList.Last().Add(gradChangeData[i], newDiff);
+                else
+                    subList.Add(new SubLineData(gradChangeData[i], newDiff));
+                diff = newDiff;
+            }
+            int maxSubDataIndex = subList.MaxIndex((a, b) => a.GetDataExtent().CompareTo(b.GetDataExtent()));  // 找到分段里和平均数差异最大的分段
+
+            //TODO 这里要不要考虑加个阈值,过滤掉梯度太小的线条? -----------------------------------
+
+            if (subList.Count < 3)
+                maxSubDataIndex = -1;  // ≥3的时候把SubDataExtent最高的这段剔除掉再计算
+
+            var lineGuessExtent = 0.0f;
+            for (int i = 0; i < subList.Count; i++)
+            {
+                if (i != maxSubDataIndex)
+                    lineGuessExtent += subList[i].GetDataExtent();
+            }
+            if (maxSubDataIndex == -1)
+                lineGuessExtent /= subList.Count;
+            else
+                lineGuessExtent /= subList.Count - 1;
+
+            lineGuessExtent = (float)Math.Sqrt(lineGuessExtent);
+            var countFactor = -0.002f * Math.Abs(subList.Count - 3) + 0.015f;  // 3时最大
+            if (ScreenLocate.Main.DebugOnZIMDemo)
+                Debug.Log("line.Index" + line.ScreenLineIndex + ", maxDiffIndex: " + maxSubDataIndex + ", sub count: " + subList.Count + ", lineGuess: " + (1 - lineGuessExtent + countFactor));
+            return 1 - lineGuessExtent + countFactor;
+        }
+    }
+
+
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineGuess.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8501a1c5db387574788194c39a469865
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 6 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentified.cs → Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineIdentified.cs

@@ -17,8 +17,14 @@ namespace o0.Project
         public float Gradient;
         // 线梯度方向, 0 - 180度
         public float GradientDegree;
+        // 筛线条时, 计算垂直直线的梯度值
+        public float ZIMGradient;
+        // 到屏幕中点的距离
+        public float DistanceToMiddle;
         // 用于绘制的线, 为null就是不绘制
         public Line DrawLine;
+        // 被认为是屏幕的哪条边,-1表示不是屏幕的边,0, 1, 2,3对应 下、右、上、左
+        public int ScreenLineIndex;
 
         public LineIdentified(int batch, Line line, float gradient, float gradientDegree, bool drawAll = false)
         {

+ 0 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentified.cs.meta → Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentify/LineIdentified.cs.meta


+ 202 - 64
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/ScreenIdentification.cs

@@ -9,6 +9,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using UnityEngine;
 using UnityEngine.UIElements;
+using UnityStandardAssets.Utility;
 using ZIM;
 using ZIM.Unity;
 
@@ -82,8 +83,8 @@ namespace o0.Project
         public ScreenIdentification()
         {
             Screen = new ScreenMap();
-            OnLocateScreenEnter += () => Application.targetFrameRate = 30;      // 固定识别的帧率,确保摄像机拍到正确的画面
-            OnLocateScreenEnd += () => Application.targetFrameRate = 60;
+            //OnLocateScreenEnter += () => Application.targetFrameRate = 30;      // 固定识别的帧率,确保摄像机拍到正确的画面
+            //OnLocateScreenEnd += () => Application.targetFrameRate = 60;
         }
 
         public void SetScreenQuad(QuadrilateralInCamera quad) => Screen.QuadInCamera = quad;
@@ -130,7 +131,7 @@ namespace o0.Project
                 areaSelected = -1;
                 quadTemp.Clear();
                 sumTemp.Clear();
-                ScreenLocate.Main.DebugScreenImages.Clear();
+                //ScreenLocate.Main.DebugScreenImages.Clear();
                 return;
             }
 
@@ -144,6 +145,8 @@ namespace o0.Project
 
             //bStartLocateScreen = false;
 
+            ScreenWhiteTexture = null;
+            ScreenBlackTexture = null;
             OnLocateScreenEnter?.Invoke();
         }
         /// <summary>
@@ -254,6 +257,9 @@ namespace o0.Project
         {
             if (ScreenBlackTexture == null)
                 ScreenBlackTexture = new Geometry.Vector<float>[Size.x * Size.y];
+            //Debug.Log($"采样屏幕黑色,Texture2D size: {cam.Size()}, ScreenLocate size: ({Size.x}, {Size.y})");
+            //Debug.Log($"ScreenBlackTexture.Length: ({ScreenBlackTexture.Length})");
+
             var pixel = cam.GetPixels();
             Parallel.For(0, Size.x * Size.y, i =>
             {
@@ -266,6 +272,9 @@ namespace o0.Project
         {
             if (ScreenWhiteTexture == null)
                 ScreenWhiteTexture = new Geometry.Vector<float>[Size.x * Size.y];
+            //Debug.Log($"采样屏幕白色,Texture2D size: {cam.Size()}, ScreenLocate size: ({Size.x}, {Size.y})");
+            //Debug.Log($"ScreenWhiteTexture.Length: ({ScreenWhiteTexture.Length})");
+
             var pixel = cam.GetPixels();
             Parallel.For(0, Size.x * Size.y, i =>
             {
@@ -667,30 +676,26 @@ namespace o0.Project
             // 屏幕黑白差值,存放多批次的图像用于识别, 该List数量不能等于 0 
             List<UnityEngine.Color[]> PixelsMultipleBatches = new List<UnityEngine.Color[]>();
 
-            var sw = new System.Diagnostics.Stopwatch();
-            sw.Start();
+            //var sw = new System.Diagnostics.Stopwatch();
+            //sw.Start();
 
             //读取数据
+            int batchCount;
             if (debugImages != null && debugImages.Count != 0)
             {
+                batchCount = debugImages.Count;
+                var dSize = debugImages.First().Size();
                 foreach (var i in debugImages)
                 {
                     Debug.Log($"<color=aqua>Debug {i.name}</color>");
-                    PixelsMultipleBatches.Add(i.GetPixels());
+                    if (i.Size() != dSize)
+                        throw new InvalidOperationException("Multiple Debug textures have different sizes");
+                    //PixelsMultipleBatches.Add(i.GetPixels());
                 }
 
             }
             else    // 获得屏幕差值
             {
-                var maxWhite = 0f;
-                foreach (var i in ScreenWhiteTexture)
-                {
-                    var m = i.x > i.y ? (i.x > i.z ? i.x : i.z) : (i.y > i.z ? i.y : i.z);
-                    if (maxWhite < m)
-                        maxWhite = m;
-                }
-                var scale = 1.0f / maxWhite;        // 放大对比度
-
                 var differPixel = new UnityEngine.Color[Size.x * Size.y];
                 var whitePixel = new UnityEngine.Color[Size.x * Size.y];
                 Parallel.For(0, Size.x, x =>
@@ -699,43 +704,75 @@ namespace o0.Project
                     {
                         var i = y * Size.x + x;
                         var d = ScreenWhiteTexture[i] - ScreenBlackTexture[i];
-                        differPixel[i] = new UnityEngine.Color(d.x, d.y, d.z) * scale;
-                        whitePixel[i] = new UnityEngine.Color(ScreenWhiteTexture[i].x, ScreenWhiteTexture[i].y, ScreenWhiteTexture[i].z) * scale;
+                        differPixel[i] = new UnityEngine.Color(d.x, d.y, d.z);
+                        whitePixel[i] = new UnityEngine.Color(ScreenWhiteTexture[i].x, ScreenWhiteTexture[i].y, ScreenWhiteTexture[i].z);
                     }
                 });
                 PixelsMultipleBatches.Add(differPixel);     // 色差图
                 PixelsMultipleBatches.Add(whitePixel);      // 原图
+                batchCount = PixelsMultipleBatches.Count;
             }
 
             int conSize = (int)Math.Ceiling(0.007f * Size.y) * 2 + 1;
             conSize = Math.Max(conSize, 7);     // 设置最小为7
 
             float minLength = conSize * 7.7f;
-            minLength = locateIndex == -1 ? minLength : minLength * areaPercent;    // minLength需要按比例缩小
+            minLength = locateIndex == -1 ? minLength : minLength * areaPercent;    // minLength需要按areaPercent比例缩小
             string log = $"[Log][ScreenLocate Auto] Size: ({Size.x},{Size.y}), 卷积核Size: {conSize}, 最小线段长度: {minLength}";
 
             var allLines = new List<LineIdentified>();
             List<Texture2D> LocateTexTemp = new List<Texture2D>();
             List<Matrix> ScreenLocateMatList = new List<Matrix>();
-            foreach (var batch in PixelsMultipleBatches.Index())
+            for (int batch = 0; batch < batchCount; batch++)
             {
-                var locateTex = ToLocateTex(PixelsMultipleBatches[batch]);
+                Texture2D locateTex;
+                if (debugImages != null && debugImages.Count != 0)
+                    locateTex = debugImages[batch];
+                else
+                    locateTex = ToLocateTex(PixelsMultipleBatches[batch]);
                 LocateTexTemp.Add(locateTex);
                 var ScreenLocateMat = locateTex.Too0Mat();        // 用于获取Lines的Matrix
-                var lineCount = ZIMIdentifyQuadLSD(ref allLines, batch, ScreenLocateMat.zimIdentifyEdgeGradientAny(conSize), minLength);
+                var lineCount = ZIMIdentifyQuadLSD(
+                    ref allLines,
+                    batch,
+                    ScreenLocateMat.zimIdentifyEdgeGradientAny(conSize),
+                    minLength,
+                    new Vector(minLength * 0.4f, conSize * 1.6f));
                 log += $"\r\n识别图片{batch}, 识别到的线段数量为: {lineCount}";
                 ScreenLocateMatList.Add(ScreenLocateMat);
             }
-            Texture2D ScreenLocateTexture = LocateTexTemp[0];       // for output
+            Texture2D ScreenLocateTexture = LocateTexTemp[0];       // 色差图,for output
+
+            // LSD计算得到的矩阵尺寸较小(因为卷积),这里必须进行位移
+            // 新增:根据阈值筛去梯度太低的线段
+            float minGradient = 0.08f;
+            var offset = new Vector((conSize - 1) / 2, (conSize - 1) / 2);
+            var tempList = new List<LineIdentified>();
+            for (int i = 0; i < allLines.Count; i++)
+            {
+                var l = allLines[i];
+                if (l.Gradient > minGradient * l.Line.Length) 
+                {
+                    l.Offset(offset);
+                    tempList.Add(l);
+                }
+            }
+            allLines = tempList;
+            log += $"\r\n根据梯度阈值筛选,最终线段数量为: {allLines.Count}";
 
             // 如果有手动数据,刷新一下Size
             QuadManual?.ReSize(new Vector(Size.x, Size.y), ScreenMap.ViewAspectRatioSetting);
             // 估算屏幕中点,如果已有手动定位数据,根据现有数据取平均即可,否则从色差计算,ScreenLocateMatList[0]默认是屏幕的黑白色差
             Vector AvgPoint = QuadManual != null ? QuadManual.Quad.Centroid : GetAvgPoint(ScreenLocateMatList[0]);
             // 过滤得到四边形的四条边,
-            var (quadLinesSemiAuto, quadLinesAuto) = FilterLines(ScreenLocateMatList, allLines, AvgPoint,
-                out LineIdentified[] manualLines, out List<LineIdentified> possibleLines,
-                conSize, conSize, minLength);
+            var (quadLinesSemiAuto, quadLinesAuto) = FilterLines(
+                ScreenLocateMatList, 
+                allLines, 
+                AvgPoint,
+                out LineIdentified[] manualLines, 
+                out List<LineIdentified> possibleLines,
+                conSize, 
+                minLength);
 
             #region 全自动识别的结果
             List<LineIdentified> LineIdentifiedAuto = new List<LineIdentified>();               // 线段顺序: 下、右、上、左
@@ -808,7 +845,7 @@ namespace o0.Project
                     o0Extension.DrawLine(ScreenQuadMap, LineIdentifiedSemiAuto[i].DrawLine, (x, y) => 5, new Geometry2D.Float.Vector(0, 10));
                 else
                     o0Extension.DrawLine(ScreenQuadMap, LineIdentifiedSemiAuto[i].DrawLine, (x, y) => 3, new Geometry2D.Float.Vector(0, 6), true);
-            }
+            } 
 
             // 绘制全自动
             foreach (var i in LineIdentifiedAuto.Index())
@@ -910,9 +947,9 @@ namespace o0.Project
 
         // 返回查找到的线段数量,0是查找失败
         int ZIMIdentifyQuadLSD(ref List<LineIdentified> allLines, int batch, (Matrix edgeMat, Matrix edgeDirMat) edgeGradient,
-            float minLength = 100)
+            float minLength, Vector LineCaptureSize)
         {
-            var l = edgeGradient.edgeMat.IdentifyLineLSD(edgeGradient.edgeDirMat, minLength, 50, LineCaptureSize: new Vector(10, 6));
+            var l = edgeGradient.edgeMat.IdentifyLineLSD(edgeGradient.edgeDirMat, minLength, 25, LineCaptureSize);
             if (l == null || l.Count == 0)
                 return 0;
             allLines.AddRange(l.Select((i) => new LineIdentified(batch, i)));
@@ -921,14 +958,8 @@ namespace o0.Project
 
         // 返回四边形的四条边(半自动、全自动),List长度一定是4 (如果没有识别到就是null),且线段顺序是: 下、右、上、左
         (List<LineIdentified>, List<LineIdentified>) FilterLines(List<Matrix> screenLocateMatList, List<LineIdentified> allLines, Vector avgPoint,
-            out LineIdentified[] manualLines, out List<LineIdentified> possibleLines, float conSize, float gradientLength, float minLength = 100)
+            out LineIdentified[] manualLines, out List<LineIdentified> possibleLines, float gradientLength, float minLength = 100)
         {
-            //Debug.Log("[IdentifyLineLSD] lines.Count: " + lines.Count);
-            var offset = new Vector((conSize - 1) / 2, (conSize - 1) / 2);
-            // LSD计算得到的矩阵尺寸较小(因为卷积),这里必须进行位移
-            for (int i = 0; i < allLines.Count; i++)
-                allLines[i].Offset(offset);
-
             // 筛掉椭圆框外的线段(超出一半会筛掉)
             var innerLines = new List<LineIdentified>();
             for (int i = 0; i < allLines.Count; i++)
@@ -955,12 +986,12 @@ namespace o0.Project
             // 角度阈值,用来判断线段的梯度方向是否指向屏幕中心(avgPoint)
             var avaAngleHalf = 75f;
 
-            // 沿直线计算综合梯度(梯度乘以长度系数,再乘以距离系数), distanceRatio是实际距离除以最大距离
-            float estimateGradient(LineIdentified line, float distanceRatio)
+            #region 内部函数
+            float ScreenGrad(LineIdentified line)
             {
                 var dir = (line.Line.B - line.Line.A).Normalized;
                 var vertical = new Vector(-dir.y, dir.x) * (gradientLength / 2);
-                var step = 2;
+                int step = (int)(minLength / 5);
                 var ll = line.Line.Length;
                 var lg = new List<float>();
                 for (int i = 0; i <= ll; i += step)
@@ -970,17 +1001,23 @@ namespace o0.Project
                     var gb = point - vertical;
                     lg.Add(screenLocateMatList[line.Batch][(int)ga.x, (int)ga.y] - screenLocateMatList[line.Batch][(int)gb.x, (int)gb.y]);
                 }
-
+                return Math.Abs(lg.Mean());
+            }
+            // 沿直线计算综合梯度(梯度乘以长度系数,再乘以距离系数), distanceRatio是实际距离除以最大距离
+            float estimateGradient(LineIdentified line, float distanceRatio)
+            {
+                var gM = ScreenGrad(line);
                 float e = (float)Math.Sqrt(Math.Ceiling(line.Line.Length / minLength));       // 长度系数,筛选时梯度更大、长度更长的线段更优
-                float d = (3 - distanceRatio) / 2;            // 距离系数,距离越近,系数越大
-                return e * d * Math.Abs(lg.Mean());
+                float d = (1.3f - distanceRatio) / 0.3f;             // 距离系数,距离越近,系数越大
+                line.ZIMGradient = e * d * gM;      // 记录一下综合梯度,新增的识别黑边功能会二次使用
+                return line.ZIMGradient;
             }
-            // 根据线段梯度的角度,判断是不是屏幕的边,out index代表是哪条边(顺序是: 下、右、上、左)
-            bool isScreenLine(LineIdentified line, out int index)
+            // 根据线段梯度的角度,判断是不是屏幕的边,index代表是哪条边(顺序是: 下、右、上、左)
+            void GetScreenLineIndex(LineIdentified line)
             {
                 var a = (avgPoint - (line.Line.A + line.Line.B) / 2).DegreeToXAxis();
                 //Debug.Log(a + ", " + gradient + ", " + sum);
-                index = -1;
+                var index = -1;
                 if (Math.Abs(a - line.GradientDegree) < avaAngleHalf || Math.Abs(a - 360 - line.GradientDegree) < avaAngleHalf || Math.Abs(a + 360 - line.GradientDegree) < avaAngleHalf)
                 {
                     if (line.GradientDegree > 45 && line.GradientDegree < 135)     // 下
@@ -991,17 +1028,21 @@ namespace o0.Project
                         index = 2;
                     else
                         index = 3;
-                    return true;
                 }
-                return false;
+                line.ScreenLineIndex = index;
             }
+            #endregion
 
+            // 根据梯度方向,判断是哪条边
+            foreach (var l in innerLines)
+                GetScreenLineIndex(l);
 
             // 下、右、上、左, 半自动和自动
             var quadLinesSemiAuto = new List<(float, LineIdentified)>[4] { new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>() };
             var quadLinesAuto = new List<(float, LineIdentified)>[4] { new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>() };
             possibleLines = new List<LineIdentified>();
 
+            #region 半自动(利用手动数据)
             // 如果已有手动定位数据,根据现有数据筛选线条(半自动)
             manualLines = null;
             if (QuadManual != null)
@@ -1015,48 +1056,145 @@ namespace o0.Project
                 foreach (var line in innerLines)
                 {
                     // 筛选条件:1-梯度方向匹配,2-垂足的距离足够近, 3-线段的AB点均在旧线段外部, 4-新的线段的中点,到旧线段的垂足,要在旧线段内
-                    if (isScreenLine(line, out int index))
+                    if (line.ScreenLineIndex >= 0)
                     {
-                        var distanceToOld = (o0Extension.PointPedal(line.Line, avgPoint, out _) - avgPointPedal[index]).Length;
+                        var distanceToOld = (o0Extension.PointPedal(line.Line, avgPoint, out _) - avgPointPedal[line.ScreenLineIndex]).Length;
                         if (distanceToOld < calibration &&
-                            manualLines[index].Line.LineCrossWithPoint(line.Line.A) * avgPointCross[index] <= 0 &&
-                            manualLines[index].Line.LineCrossWithPoint(line.Line.B) * avgPointCross[index] <= 0)
+                            manualLines[line.ScreenLineIndex].Line.LineCrossWithPoint(line.Line.A) * avgPointCross[line.ScreenLineIndex] <= 0 &&
+                            manualLines[line.ScreenLineIndex].Line.LineCrossWithPoint(line.Line.B) * avgPointCross[line.ScreenLineIndex] <= 0)
                         {
-                            var middleToOldLine = o0Extension.PointPedal(manualLines[index].Line, (line.Line.A + line.Line.B) / 2, out bool inLineSegment);
+                            var middleToOldLine = o0Extension.PointPedal(manualLines[line.ScreenLineIndex].Line, (line.Line.A + line.Line.B) / 2, out bool inLineSegment);
                             if (inLineSegment)
                             {
-                                quadLinesSemiAuto[index].Add((estimateGradient(line, distanceToOld / calibration), line));
+                                quadLinesSemiAuto[line.ScreenLineIndex].Add((estimateGradient(line, distanceToOld / calibration), line));
                                 possibleLines.Add(line);
                             }
                         }
                     }
                 }
             }
+            // 获得结果
+            var resultSemiAuto = new LineIdentified[4];
+            var resultSemiAutoPedal = new Vector[4];       // 用于找平行线
+            for (int i = 0; i < 4; i++)
+            {
+                resultSemiAuto[i] = quadLinesSemiAuto[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
+                if (resultSemiAuto[i] != null)
+                    resultSemiAutoPedal[i] = o0Extension.PointPedal(resultSemiAuto[i].Line, avgPoint, out _);
+            }
+            // 新增功能(解决黑边问题):根据 result 再找平行线,判断是否替换(1-在 result 内部,且离中点最近,2-接近平行)
+            UpdateResultlines(resultSemiAuto, FindInterLinePair(
+                (i) => true,
+                GetInterSelectableLines(quadLinesSemiAuto, resultSemiAuto, resultSemiAutoPedal, avgPoint)));
+
+            #endregion
 
+            #region 全自动
             // 全自动
-            foreach (var line in allLines)
+            foreach (var line in innerLines)
             {
-                if (isScreenLine(line, out int index))
+                if (line.ScreenLineIndex >= 0 && line.Batch < 1)    // 全自动只处理第一张图,默认是色差图
                 {
-                    if (line.Batch < 1)     // 全自动只处理第一张图,默认是色差图
-                    {
-                        quadLinesAuto[index].Add((estimateGradient(line, 1), line));
-                    }
+                    quadLinesAuto[line.ScreenLineIndex].Add((estimateGradient(line, 1), line));
                 }
             }
-
-            var resultSemiAuto = new LineIdentified[4];
+            // 获得结果
             var resultAuto = new LineIdentified[4];
+            var resultAutoPedal = new Vector[4];       // 用于找平行线
             for (int i = 0; i < 4; i++)
             {
-                if (quadLinesSemiAuto[i].Count > 0)
-                    resultSemiAuto[i] = quadLinesSemiAuto[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
-                if (quadLinesAuto[i].Count > 0)
-                    resultAuto[i] = quadLinesAuto[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
+                resultAuto[i] = quadLinesAuto[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
+                if (resultAuto[i] != null)
+                    resultAutoPedal[i] = o0Extension.PointPedal(resultAuto[i].Line, avgPoint, out _);
             }
+            // 新增功能(解决黑边问题):根据 resultAuto 再找平行线,判断是否替换(1-在 result 内部,且离中点最近,2-接近平行,3-LineGuess判断是直线)
+            UpdateResultlines(resultAuto, FindInterLinePair(
+                new LineGuess(screenLocateMatList, gradientLength * 2, minLength).GuessIsLine,
+                GetInterSelectableLines(quadLinesAuto, resultAuto, resultAutoPedal, avgPoint)));
+            #endregion
+
             return (resultSemiAuto.ToList(), resultAuto.ToList());
         }
 
+        List<LineIdentified> GetInterSelectableLines(List<(float, LineIdentified)>[] quadLines, LineIdentified[] resultLines, Vector[] resultPedal, Vector avgPoint)
+        {
+            var result = new List<LineIdentified>();
+            foreach (var ql in quadLines)
+            {
+                foreach (var (_, line) in ql)
+                {
+                    if (line != resultLines[line.ScreenLineIndex])
+                    {
+                        var pedal = o0Extension.PointPedal(line.Line, avgPoint, out _);
+                        var a0 = pedal - avgPoint;
+                        var a0L = a0.Length;
+                        line.DistanceToMiddle = a0L;
+                        var a1 = resultPedal[line.ScreenLineIndex] - avgPoint;
+                        var a1L = a1.Length;
+                        if (a0L < a1L)
+                        {
+                            var dotN = a0.Dot(a1) / a0L / a1L;
+                            if (Math.Abs(dotN - 1) < 0.002) // 接近平行即可
+                                result.Add(line);
+                        }
+                    }
+                }
+            }
+            return result;
+        }
+
+        void UpdateResultlines(LineIdentified[] result, (LineIdentified a, LineIdentified b) inter)
+        {
+            if (inter.a != null)   // 替换上一步筛选的结果中的部分边,得到最终的结果
+                result[inter.a.ScreenLineIndex] = inter.a;
+            if (inter.b != null)
+                result[inter.b.ScreenLineIndex] = inter.b;
+        }
+
+        (LineIdentified a, LineIdentified b) FindInterLinePair(Func<LineIdentified, bool> guessIsLine, List<LineIdentified> interSelectable, int maxCountToSelect = 8)
+        {
+            Debug.Log("[ScreenIdentification] selectable inter line count: " + interSelectable.Count);
+            interSelectable.Sort((a, b) => b.ZIMGradient.CompareTo(a.ZIMGradient));
+            int count = 0;
+            LineIdentified[] selected = new LineIdentified[4];
+            foreach (var line in interSelectable)
+            {
+                if (guessIsLine(line))        // 评价是不是Line, 并且找到离中心点最近的
+                {
+                    if (ScreenLocate.Main.DebugOnZIMDemo)
+                        Debug.Log($"[ScreenIdentification] {interSelectable.IndexOf(line)}, guess is line: (index)" + line.ScreenLineIndex);
+                    if (selected[line.ScreenLineIndex] == null || selected[line.ScreenLineIndex].DistanceToMiddle > line.DistanceToMiddle)
+                        selected[line.ScreenLineIndex] = line;
+                }
+                if (count++ >= maxCountToSelect)
+                    break;
+            }
+            var selectedList = new List<LineIdentified>();
+            foreach (var i in selected)
+            {
+                if (i != null)
+                    selectedList.Add(i);
+            }
+
+            if (selectedList.Count == 4)
+            {
+                if (selected[0].ZIMGradient + selected[2].ZIMGradient > selected[1].ZIMGradient + selected[3].ZIMGradient)
+                    return (selected[0], selected[2]);
+                else
+                    return (selected[1], selected[3]);
+            }
+            else if (selected[0] != null && selected[2] != null)
+                return (selected[0], selected[2]);
+            else if (selected[1] != null && selected[3] != null)
+                return (selected[1], selected[3]);
+            else if (selectedList.Count == 2)
+                return selectedList[0].ZIMGradient > selectedList[1].ZIMGradient ? (selectedList[0], null) : (selectedList[1], null);
+            else if (selectedList.Count == 1)
+                return (selectedList[0], null);
+            else
+                return (null, null);
+        }
+
         void SaveImages(string FileDirectory, string log,
             Texture2D ScreenLocateTex, Texture2D allLinesTex, Texture2D ChoosableLineTex, Texture2D ScreenQuadTex)
         {

+ 17 - 4
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>();
 
@@ -795,7 +799,7 @@ public partial class ScreenLocate : MonoBehaviour
     {
         if (DebugScreenImages.Count != 0)
         {
-            screenIdentification = new o0.Project.ScreenIdentification();
+            //screenIdentification = new o0.Project.ScreenIdentification();
             CameraSize = new o0.Geometry2D.Vector<int>(DebugScreenImages[0].width, DebugScreenImages[0].height);
             WebCamIsReady(DebugScreenImages[0]);
 
@@ -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位置

+ 1 - 1
Assets/InfraredProject/WebCamera/zimWebCamera.unity

@@ -6444,7 +6444,7 @@ PrefabInstance:
       objectReference: {fileID: 0}
     - target: {fileID: 7518895720462305555, guid: 742f117969807f147b562a0be2d52864, type: 3}
       propertyPath: m_RootOrder
-      value: 22
+      value: 20
       objectReference: {fileID: 0}
     - target: {fileID: 7518895720462305555, guid: 742f117969807f147b562a0be2d52864, type: 3}
       propertyPath: m_AnchorMax.x

Різницю між файлами не показано, бо вона завелика
+ 661 - 94
Assets/SmartBow/Resources/SmartBow/Prefabs/Views/Home/InfraredScreenPositioningView.prefab


+ 85 - 11
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/LineGenerator.cs

@@ -12,6 +12,14 @@ namespace ZIM
         [Tooltip("线段的粗细")]
         public float LineThickness = 2f;
         public bool Loop;
+        [Tooltip("是否绘制为虚线")]
+        public bool IsDashed = false;
+        [Tooltip("虚线每段的长度")]
+        public float DashLength = 10f;
+        [Tooltip("虚线每段的间隔")]
+        public float GapLength = 5f;
+        [Tooltip("是否每次绘制两点之间的线段")]
+        public bool DrawTwoPointsAtATime = false;
 
         [SerializeField]
         private Vector2[] _points;
@@ -41,15 +49,66 @@ namespace ZIM
             float pivotX = rectTransform.pivot.x * rectTransform.rect.width - width;
             float pivotY = rectTransform.pivot.y * rectTransform.rect.height - height;
 
-            for (int i = Loop ? -1 : 0; i < Points.Length - 1; i++)
+            if (DrawTwoPointsAtATime)
             {
-                Vector2 start = Points[i < 0 ? i + Points.Length : i];
-                Vector2 end = Points[i + 1];
-
-                start = new Vector2(start.x * width - pivotX, start.y * height - pivotY);
-                end = new Vector2(end.x * width - pivotX, end.y * height - pivotY);
-
-                DrawLine(vh, start, end, LineThickness);
+                // 确保每两个点就绘制一次线段
+                for (int i = 0; i < Points.Length - 1; i++)
+                {
+                    Vector2 start = Points[i];
+                    Vector2 end = Points[i + 1];
+
+                    start = new Vector2(start.x * width - pivotX, start.y * height - pivotY);
+                    end = new Vector2(end.x * width - pivotX, end.y * height - pivotY);
+
+                    if (IsDashed)
+                    {
+                        DrawDashedLine(vh, start, end, LineThickness, DashLength, GapLength);
+                    }
+                    else
+                    {
+                        DrawLine(vh, start, end, LineThickness);
+                    }
+                }
+
+                // 如果 Loop 为 true, 添加闭环
+                if (Loop && Points.Length >= 4)
+                {
+                    Vector2 start = Points[Points.Length - 1];
+                    Vector2 end = Points[0];
+
+                    start = new Vector2(start.x * width - pivotX, start.y * height - pivotY);
+                    end = new Vector2(end.x * width - pivotX, end.y * height - pivotY);
+
+                    if (IsDashed)
+                    {
+                        DrawDashedLine(vh, start, end, LineThickness, DashLength, GapLength);
+                    }
+                    else
+                    {
+                        DrawLine(vh, start, end, LineThickness);
+                    }
+                }
+            }
+            else
+            {
+                for (int i = Loop ? -1 : 0; i < Points.Length - 1; i++)
+                {
+                    Vector2 start = Points[i < 0 ? i + Points.Length : i];
+                    Vector2 end = Points[i + 1];
+
+                    start = new Vector2(start.x * width - pivotX, start.y * height - pivotY);
+                    end = new Vector2(end.x * width - pivotX, end.y * height - pivotY);
+
+                    // DrawLine(vh, start, end, LineThickness);
+                    if (IsDashed)
+                    {
+                        DrawDashedLine(vh, start, end, LineThickness, DashLength, GapLength);
+                    }
+                    else
+                    {
+                        DrawLine(vh, start, end, LineThickness);
+                    }
+                }
             }
         }
 
@@ -69,11 +128,26 @@ namespace ZIM
 
             vh.AddUIVertexQuad(verts);
         }
-
-        void Update()
+        private void DrawDashedLine(VertexHelper vh, Vector2 start, Vector2 end, float thickness, float dashLength, float gapLength)
         {
-            //SetVerticesDirty();
+            float totalLength = Vector2.Distance(start, end);
+            Vector2 direction = (end - start).normalized;
+            float drawnLength = 0;
+
+            while (drawnLength < totalLength)
+            {
+                float currentDashLength = Mathf.Min(dashLength, totalLength - drawnLength);
+                Vector2 segmentStart = start + direction * drawnLength;
+                Vector2 segmentEnd = segmentStart + direction * currentDashLength;
+
+                DrawLine(vh, segmentStart, segmentEnd, thickness);
+                drawnLength += currentDashLength + gapLength;
+            }
         }
+        //void Update()
+        //{
+        //    //SetVerticesDirty();
+        //}
 
     }
 }

+ 533 - 178
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,56 @@ 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;
+    [Tooltip("Demo测试时候显示")]
+    [SerializeField]
+    RectTransform crosshair;
+    [SerializeField]
+    RectTransform crosshairRed;
+    [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 +144,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 +220,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.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._ins.onSetSliderValue(slider);
+            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 +296,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 +314,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 +330,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)
         {
@@ -313,6 +404,96 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
                 crosshairSmall.gameObject.SetActive(false);
 
             //rawImage.material = InfraredDemo.infraredCameraHelper.GetCameraMaterial();
+
+            //可见光调节页面出现红十字光标
+            if (curStepView == ScreenPositioningStep.LightCtrl)
+            {
+                if (ScreenLocate.Main.infraredSpotBuffer[0].CameraLocation.HasValue)
+                {
+                    // 检测到光点
+                    var posInCanvas = ScreenLocate.Main.infraredSpotBuffer[0].CameraLocation.Value.pixelToLocalPosition_AnchorCenter(ScreenLocate.Main.CameraSize, rawImage.rectTransform.rect);
+                    crosshairRed.gameObject.SetActive(true);
+                    crosshairRed.anchoredPosition = posInCanvas;
+                }
+                else
+                    crosshairRed.gameObject.SetActive(false);
+            }
+            else if (crosshairRed.gameObject.activeSelf) {
+                crosshairRed.gameObject.SetActive(false);
+            }
+        }
+    }
+
+
+    /// <summary>
+    /// 根据enum操作ui
+    /// </summary>
+    /// <param name="step"></param>
+    public void SetScreenPositioningStepState(ScreenPositioningStep step = ScreenPositioningStep.None)
+    {
+        curStepView = step;
+        AllScreenPositioningStepFalse();
+        switch (step)
+        {
+            case ScreenPositioningStep.Start:
+                maskLine.SetDrawMask(true);
+                LayoutStart.SetActive(true);
+                UpdateStartUI();
+                break;
+            case ScreenPositioningStep.AutoEnd:
+                maskLine.SetDrawMask(true);
+                LayoutAutoEnd.SetActive(true);
+                pointsParent.gameObject.SetActive(false);
+                CurrentUILineGenerator.enabled = true;
+                break;
+            case ScreenPositioningStep.Marker:
+                maskLine.SetDrawMask(true);
+                LayoutMarker.SetActive(true);
+                CurrentUILineGenerator.enabled = false;
+                pointsParent.gameObject.SetActive(false);
+                setPointsLocation(ScreenLocate.quadUnityVectorList, pointsParent.gameObject, true);
+                break;
+            case ScreenPositioningStep.LightCtrl:
+                maskLine.SetDrawMask(false);
+                LayoutLightCtrl.SetActive(true);
+                CurrentUILineGenerator.enabled = false;
+                pointsParent.gameObject.SetActive(true);
+                setPointsLocation(ScreenLocate.quadUnityVectorList, pointsParent.gameObject, true);
+                break;
+            case ScreenPositioningStep.Successful:
+                LayoutSuccessful.SetActive(true);
+                pointsParent.gameObject.SetActive(false);
+                CurrentUILineGenerator.enabled = false;
+                break;
+        }
+    }
+ 
+    void AllScreenPositioningStepFalse()
+    {
+        LayoutStart.SetActive(false);
+        LayoutMarker.SetActive(false);
+        LayoutAutoEnd.SetActive(false);
+        LayoutLightCtrl.SetActive(false);
+        LayoutSuccessful.SetActive(false);
+    }
+    /// <summary>
+    /// 开始页面时候ui
+    /// </summary>
+    void UpdateStartUI() {
+        pointsParent.gameObject.SetActive(false);
+        CurrentUILineGenerator.enabled = true;
+        if (ScreenLocate.quadUnityVectorList.Count == 0)
+        {
+            BottomConfirmBtn.SetActive(false);
+            StartTextTip1.SetActive(true);
+            StartTextTipHasData.SetActive(false);
+        }
+        else
+        {
+            //存在数据,显示确认按钮,确认按钮直接跳转到游戏
+            BottomConfirmBtn.SetActive(true);
+            StartTextTip1.SetActive(false);
+            StartTextTipHasData.SetActive(true);
         }
     }
 
@@ -321,26 +502,61 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         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 +592,41 @@ 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));
-
-        //自动识别后隐藏手动按钮
-        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++)
-            {
-                RectTransform t = pointsParent.GetChild(i) as RectTransform;
-                t.anchoredPosition = screen.Quad[i].pixelToLocalPosition_AnchorCenter(screen.CameraSize, pointsParent.rect);
-            }
+        if (curStepView == ScreenPositioningStep.Start || curStepView == ScreenPositioningStep.AutoEnd) {
+            //如果是开始页面进行自动定位的
+            if(curStepView != ScreenPositioningStep.AutoEnd) SetScreenPositioningStepState(ScreenPositioningStep.AutoEnd);
         }
-
-        //显示最后结果
-        LayoutMarker.SetActive(false);
-        LayoutSuccessful.SetActive(true);
-
+        else if (curStepView == ScreenPositioningStep.Marker) {
+            //显示最后结果
+            SetScreenPositioningStepState(ScreenPositioningStep.Successful);
+        }
+        //屏幕变化情况下。同步修改对应的line
         //设置两个线段
         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++)
-            {
-                pointsOutput += "[" + FirstUILineGenerator.Points[i].x + ", " + FirstUILineGenerator.Points[i].y + "]";
-                if (i < FirstUILineGenerator.Points.Length - 1)
-                {
-                    pointsOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
-            }
-            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++)
-            {
-                quadOutput += "[" + screenAuto.Quad[customOrder[i]].x + ", " + screenAuto.Quad[customOrder[i]].y + "]";
-                if (i < customOrder.Length - 1)
-                {
-                    quadOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
-            }
-            Debug.Log(quadOutput);
-
+            Debug.Log("[校准流程]Successful自动识别screenAuto信息  ------------ ");
+            CurrentUILineGenerator.Points = infraredDemo.ConvertQuadToPoints(screenAuto, texSize);
+            FirstUILineGenerator.Points = infraredDemo.ConvertQuadToPoints(screenAuto, texSize);
         }
-        else {
+        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++)
-            {
-                pointsOutput += "[" + SecondUILineGenerator.Points[i].x + ", " + SecondUILineGenerator.Points[i].y + "]";
-                if (i < SecondUILineGenerator.Points.Length - 1)
-                {
-                    pointsOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
-            }
-            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++)
-            {
-                quadOutput += "[" + screenSemiAuto.Quad[customOrder[i]].x + ", " + screenSemiAuto.Quad[customOrder[i]].y + "]";
-                if (i < customOrder.Length - 1)
-                {
-                    quadOutput += ", "; // 添加逗号分隔符,最后一个元素后不加
-                }
-            }
-            Debug.Log(quadOutput);
-
+            Debug.Log("[校准流程]Successful半自动识别screenSemiAuto信息 ------------ ");
+            SecondUILineGenerator.Points = infraredDemo.ConvertQuadToPoints(screenSemiAuto, texSize);
         }
-        else {
+        else
+        {
             Debug.LogError("screenSemiAuto 不存在!");
         }
-
-
-
     }
 
     #region 绘制线段部分
@@ -645,7 +783,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             if (!enterFromInfraredDemo)
             {
                 //每次初始化重置一下引导
-                InfraredDemo._ins.resetInfraredPlayerPrefs();
+                infraredDemo.resetInfraredPlayerPrefs();
                 //GameObject connectGuidanceView = ViewManager2.getGameObjectAndShowView(ViewManager2.Path_ConnectGuidanceView);
                 //connectGuidanceView.GetComponent<ConnectGuidanceView>().showTextipInfrared();
 
@@ -1045,22 +1183,22 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             return;
         }
         // 遍历所有图片
-
-        for (int i = 0; i < ResultImages.Length; i++)
-        {
-            // 如果是选中的 index,赋值为 ResultLines[0],否则赋值为 ResultLines[1]
-            if (i == selectedIndex)
-            {
-                selected = i;
-                ResultImages[i].sprite = ResultLines[0];
-                ResultButtons[i].interactable = true;
-            }
-            else
-            {
-                ResultImages[i].sprite = ResultLines[1];
-                ResultButtons[i].interactable = false;
-            }
-        }
+        selected = selectedIndex;
+        //for (int i = 0; i < ResultImages.Length; i++)
+        //{
+        //    // 如果是选中的 index,赋值为 ResultLines[0],否则赋值为 ResultLines[1]
+        //    if (i == selectedIndex)
+        //    {
+        //        selected = i;
+        //        ResultImages[i].sprite = ResultLines[0];
+        //        ResultButtons[i].interactable = true;
+        //    }
+        //    else
+        //    {
+        //        ResultImages[i].sprite = ResultLines[1];
+        //        ResultButtons[i].interactable = false;
+        //    }
+        //}
     }
 
     //选择全自动结果后进入游戏
@@ -1088,6 +1226,12 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             ViewManager2.HideView(ViewManager2.Path_InfraredScreenPositioningView);
             return;
         }
+        EnterGame();
+        //存储一次节点
+        SaveLocalPos();
+    }
+
+    void EnterGame() {
         if (InfraredDemo.running)
         {
             //跳转入界面
@@ -1096,7 +1240,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 +1257,6 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
             }
 
         }
-        //存储一次节点
-        SaveLocalPos();
     }
 
     /// <summary>
@@ -1124,7 +1266,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 +1296,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
 }

+ 5 - 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;
         //是否绘制内四边形
@@ -53,10 +56,12 @@ namespace LineUI
 
         public void SetDrawQuad(bool value) {
             bDrawQuad = value;
+            SetVerticesDirty();
         }
         public void SetDrawMask(bool value)
         {
             bDrawMask = value;
+            SetVerticesDirty();
         }
         protected override void OnPopulateMesh(VertexHelper vh)
         {

+ 79 - 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,13 +20,19 @@ public class PointMarker : MonoBehaviour
 
     public Image[] hintImages; // 依次存放左上、右上、右下、左下四个提示图片
 
+    Vector2 oldPoint = Vector2.zero;
     private List<Vector2> markedPoints = new List<Vector2>();
-    private string[] directions = { "左上", "右上", "右下", "左下" };
+    // { "左上", "右上", "右下", "左下" }
+    private string[] directions = { "TipTopLeft", "TipTopRight", "TipBottomRight", "TipBottomLeft" };
     private int currentPointIndex = 0; // 当前需要标记的点的索引
     private List<GameObject> markerObjects = new List<GameObject>(); // 记录标记物体
 
     [SerializeField] InfraredScreenPositioningView infraredScreenPositioningView;
 
+    [Tooltip("存在校准数据时候显示")]
+    [SerializeField] ZIM.LineGenerator line;//开始显示屏幕线条
+
+
     void Start()
     {
         if (AimHandler.ins) AimHandler.ins.OnCrossBtnEvent += OnRecordInfrared;
@@ -36,6 +44,7 @@ public class PointMarker : MonoBehaviour
 
         // 初始化时,显示第一个提示图片,其余隐藏
         ShowHintImage(0);
+
     }
     void OnDestroy()
     {
@@ -60,8 +69,8 @@ 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);
+                oldPoint = new Vector2(u, v);
                 // 设置标记的位置
                 MarkPoint(new Vector2(u, v).pixelToLocalPosition_AnchorCenter(new Vector2(1, 1), markerParent.GetComponent<RectTransform>().rect));
             }
@@ -95,19 +104,27 @@ public class PointMarker : MonoBehaviour
         var location = ScreenLocate.Main.infraredSpotBuffer.FirstOrDefault()?.CameraLocation;
         if (location != null)
         {
+            oldPoint = location.Value;
             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);
         marker.transform.localPosition = point;
         markerObjects.Add(marker);
+        //绘制连线
+        markedPoints.Add(point);
+        UpdateLine();
 
         // 隐藏当前的提示图片,显示下一个提示图片
         currentPointIndex++;
@@ -118,18 +135,36 @@ 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);
-
+            UpdateLine();
             // 删除最后一个标记的物体
             Destroy(markerObjects[markerObjects.Count - 1]);
             markerObjects.RemoveAt(markerObjects.Count - 1);
@@ -144,12 +179,44 @@ public class PointMarker : MonoBehaviour
         // 隐藏完成按钮
         bComplete = false;
     }
+    /// <summary>
+    /// 移除全部标记,选择自动和半自动数据界面返回时候调用
+    /// </summary>
+    public void UndoAllPoints()
+    {
+        // 移除所有标记物体
+        foreach (var marker in markerObjects)
+        {
+            Destroy(marker);
+        }
+        markerObjects.Clear();
+
+        markedPoints.Clear();
+        UpdateLine();
+        // 重置索引
+        currentPointIndex = 0;
+
+        // 更新提示图片和说明
+        ShowHintImage(currentPointIndex);
+        UpdateInstruction();
+
+        // 隐藏完成按钮
+        bComplete = false;
+    }
+
+    void UpdateLine() {
+        RectTransform rectTransform = markerParent as RectTransform;
+        Vector2 pivot = rectTransform.pivot; // 获取父物体的 pivot 值
+        Vector2 texSize = rectTransform.rect.size;
+        line.Points = InfraredDemo._ins.ConvertPointsToCoordinates(markedPoints.ToArray(), texSize, pivot);
+    }
 
     void UpdateInstruction()
     {
         if (currentPointIndex < 4)
         {
-            instructionText.text = "请标记: " + directions[currentPointIndex] + "角的点";
+            instructionText.textFormatArgs = new object[] { TextAutoLanguage2.GetTextByKey(directions[currentPointIndex]) };
+            instructionText.SetTextKey("TipMiddle");
         }
     }
 
@@ -167,7 +234,7 @@ public class PointMarker : MonoBehaviour
 
     void CompletePoint() {
         if (!bComplete) {
-            Debug.Log("未完成");
+            //Debug.Log("未完成");
             //todo,加提示
             PopupMgr.ins.ShowTipTop(TextAutoLanguage2.GetTextByKey("Incomplete"));
             return;

+ 5 - 16
Assets/SmartBow/Scripts/Views/SettingsView.cs

@@ -20,6 +20,7 @@ namespace SmartBow
             switch (CommonConfig.OP)
             {
                 case OperatingPlatform.A:
+                case OperatingPlatform.C:
                     if (CommonConfig.bInfraredApp)
                     {
                         //隐藏弓到屏幕的距离
@@ -31,7 +32,8 @@ namespace SmartBow
                             { "BtnLevel", null },
                             { "BtnUserAgreement", null },
                             { "BtnPrivacyPolicy", null },
-                            { "BtnAboutUs", null }
+                            { "BtnAboutUs", null },
+                            { "BtnSignOut",null}
                         };
                         SortChildObjects(actionMapInfrared);
                     }
@@ -45,7 +47,8 @@ namespace SmartBow
                             { "BtnLevel", null },
                             { "BtnUserAgreement", null },
                             { "BtnPrivacyPolicy", null },
-                            { "BtnAboutUs", null }
+                            { "BtnAboutUs", null },
+                            { "BtnSignOut",null}
                         };
 
                         SortChildObjects(actionMapA);
@@ -64,20 +67,6 @@ namespace SmartBow
                     };
                     SortChildObjects(actionMapB);
                     break;
-                case OperatingPlatform.C:
-                    //隐藏弓到屏幕的距离
-                    Dictionary<string, Transform> actionMapInfraredC = new Dictionary<string, Transform>
-                        {
-                            { "BtnSound", null },
-                            { "BtnLanguage", null },
-                            { "BtnNewUser", null },
-                            { "BtnLevel", null },
-                            { "BtnUserAgreement", null },
-                            { "BtnPrivacyPolicy", null },
-                            { "BtnAboutUs", null }
-                        };
-                    SortChildObjects(actionMapInfraredC);
-                    break;
             }
 
         }

BIN
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: 

Деякі файли не було показано, через те що забагато файлів було змінено