Без опису
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

StairDiagramController.cs 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using TMPro;
  4. using System.Linq;
  5. public class StairDiagramController : MonoBehaviour
  6. {
  7. [Header("元件綁定")]
  8. public RectTransform 繪圖區_panel;
  9. public RectTransform 對應資訊區_panel;
  10. public GameObject 階梯Prefab;
  11. public GameObject 資訊列Prefab; // 預設放一個 TMP Text 或含文字的UI列
  12. public GameObject 線段編號Prefab;
  13. [Header("參數設定")]
  14. [SerializeField] TMP_InputField 階梯階數_tb;
  15. [SerializeField] TextMeshProUGUI 總深_tb,總高_tb, 斜邊_tb, 長邊_tb, 推薦斜邊_tb, 推薦長邊_tb, 最小斜邊_tb, 最小長邊_tb;
  16. [SerializeField] TMP_InputField[] 高;
  17. [SerializeField] TMP_InputField[] 深;
  18. [SerializeField] Image 法規_bg,推薦_bg,最小_bg;
  19. [SerializeField] GameObject 簡易圖_pl,斜坡板參數_pl;
  20. int 線段編號 = 1;
  21. public void 返回參數(){
  22. 簡易圖_pl.SetActive(false);
  23. 斜坡板參數_pl.SetActive(true);
  24. }
  25. public void 生成法規圖(string 畫圖種類){
  26. 法規_bg.color=Color.white;推薦_bg.color=Color.white;最小_bg.color=Color.white;
  27. if (畫圖種類 == "法規"){
  28. 法規_bg.color = new Color32(253, 228, 151, 255);
  29. }
  30. else if (畫圖種類 == "推薦"){
  31. 推薦_bg.color = new Color32(253, 228, 151, 255);
  32. }
  33. else if (畫圖種類 == "最小"){
  34. 最小_bg.color = new Color32(253, 228, 151, 255);
  35. }
  36. int 階梯層數 = int.Parse(階梯階數_tb.text);
  37. float shortSide = float.Parse(總高_tb.text);
  38. float longSide = float.Parse(總深_tb.text);
  39. if (shortSide == 0f || longSide == 0f)
  40. return;
  41. 簡易圖_pl.SetActive(true);
  42. 斜坡板參數_pl.SetActive(false);
  43. foreach (Transform child in 繪圖區_panel)
  44. Destroy(child.gameObject);
  45. foreach (Transform child in 對應資訊區_panel)
  46. Destroy(child.gameObject);
  47. 線段編號 = 1;
  48. float[] stepWidths = GetStepWidths(1);
  49. float[] stepHeights = GetStepHeights(1);
  50. float lastStepWidth = stepWidths[階梯層數 - 1];
  51. double angle = 0;
  52. if (shortSide >= 20){
  53. angle = 4.78;
  54. }else if(shortSide >=5 && shortSide < 20){
  55. angle = 5.74;
  56. }else if(shortSide >=4 && shortSide < 5){
  57. angle = 11.54;
  58. }else if(shortSide >=0 && shortSide < 3){
  59. angle = 30;
  60. }
  61. 計算斜邊(angle, shortSide, longSide, stepWidths[階梯層數 - 1], 斜邊_tb, 長邊_tb);
  62. 計算斜邊(9.59, shortSide, longSide, stepWidths[階梯層數 - 1], 推薦斜邊_tb, 推薦長邊_tb);
  63. 計算斜邊(11.54, shortSide, longSide, stepWidths[階梯層數 - 1], 最小斜邊_tb, 最小長邊_tb);
  64. float hypotenuse = float.Parse(斜邊_tb.text);
  65. float hypotenuse2 = float.Parse(推薦斜邊_tb.text);
  66. float hypotenuse3 = float.Parse(最小斜邊_tb.text);
  67. float longuse = float.Parse(長邊_tb.text);
  68. float longuse2 = float.Parse(推薦長邊_tb.text);
  69. float longuse3 = float.Parse(最小長邊_tb.text);
  70. float otherSide = Mathf.Sqrt(hypotenuse * hypotenuse - shortSide * shortSide);
  71. float otherSide2 = Mathf.Sqrt(hypotenuse2 * hypotenuse2 - shortSide * shortSide);
  72. float otherSide3 = Mathf.Sqrt(hypotenuse3 * hypotenuse3 - shortSide * shortSide);
  73. float setWidthLast = GetStepWidths(1)[階梯層數 - 1];
  74. float 倍數 = Mathf.Round((繪圖區_panel.rect.width - 100f) / (otherSide + setWidthLast) * 100f) / 100f;
  75. float bottomY = 100f;
  76. float pointA_x = 10f;
  77. Vector2 pointA = new Vector2(pointA_x, bottomY);
  78. Vector2 pointB = new Vector2(pointA.x + otherSide * 倍數, bottomY);
  79. Vector2 pointC = new Vector2(pointB.x, bottomY + shortSide * 倍數);
  80. Vector2 pointD = new Vector2(pointB.x - otherSide2 * 倍數, bottomY);
  81. Vector2 pointE = new Vector2(pointB.x - otherSide3 * 倍數, bottomY);
  82. if(畫圖種類=="法規"){
  83. DrawLine(pointA, pointC, Color.blue, 畫圖種類+"斜坡板長度", hypotenuse);
  84. DrawLine(pointA, pointB, Color.black, "通道保留空間", longuse);
  85. }else if(畫圖種類=="推薦"){
  86. DrawLine(pointD, pointC, Color.black, 畫圖種類+"斜坡板長度", hypotenuse);
  87. DrawLine(pointD, pointB, Color.black, "通道保留空間", longuse2);
  88. }else{//最小
  89. DrawLine(pointE, pointC, Color.red, 畫圖種類+"斜坡板長度", hypotenuse);
  90. DrawLine(pointE, pointB, Color.black, "通道保留空間", longuse3);
  91. }
  92. float startX = pointA.x + otherSide * 倍數;
  93. for (int i = 0; i < 階梯層數 - 1; i++)
  94. startX -= stepWidths[i] * 倍數;
  95. float baseX;
  96. float baseOtherSide;
  97. if (畫圖種類 == "法規"){
  98. baseX = pointA.x;
  99. baseOtherSide = otherSide;
  100. }else if (畫圖種類 == "推薦"){
  101. baseX = pointD.x;
  102. baseOtherSide = otherSide2;
  103. }else{ // 最小
  104. baseX = pointE.x;
  105. baseOtherSide = otherSide3;
  106. }
  107. for (int i = 0; i < 階梯層數 - 1; i++)
  108. {
  109. float leftX = startX + stepWidths.Take(i).Sum() * 倍數;
  110. float stairTopY = bottomY + stepHeights.Take(i + 1).Sum() * 倍數;
  111. Vector2 stairTop = new Vector2(leftX, stairTopY);
  112. // 法規線在對應 X 的 Y 高度
  113. float slopeY = bottomY + (leftX - baseX) * (shortSide / baseOtherSide);
  114. Vector2 intersectPoint = new Vector2(leftX, slopeY);
  115. // ✅ 關鍵修改:統一與 VB 一樣公式
  116. float 垂直距離 = Mathf.Round((slopeY - stairTopY) / 倍數 * 10f) / 10f;
  117. Vector2 mid = (stairTop + intersectPoint) / 2 + Vector2.up * 10;
  118. DrawLine(stairTop, intersectPoint, Color.yellow, "間距", 垂直距離);
  119. }
  120. float stairX = startX;
  121. float stairY = bottomY;
  122. for (int i = 0; i < 階梯層數; i++){
  123. float w = stepWidths[i] * 倍數;
  124. float h = stepHeights[i] * 倍數;
  125. DrawStairBox(new Vector2(stairX, stairY), w, h);
  126. stairX += w;
  127. stairY += h;
  128. }
  129. }
  130. void DrawStairBox(Vector2 bottomLeft, float width, float height){
  131. GameObject box = Instantiate(階梯Prefab, 繪圖區_panel);
  132. RectTransform rt = box.GetComponent<RectTransform>();
  133. rt.anchorMin = rt.anchorMax = rt.pivot = new Vector2(0, 0); // 左下角錨點
  134. rt.anchoredPosition = bottomLeft;
  135. rt.sizeDelta = new Vector2(width, height);
  136. }
  137. void DrawLine(Vector2 start, Vector2 end, Color color, string 用途 = "", float 實際長度 = -1f){
  138. GameObject line = new GameObject("UILine", typeof(Image));
  139. line.transform.SetParent(繪圖區_panel, false);
  140. RectTransform rt = line.GetComponent<RectTransform>();
  141. Image image = line.GetComponent<Image>();
  142. image.color = color;
  143. Vector2 direction = end - start;
  144. float length = direction.magnitude;
  145. float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
  146. rt.sizeDelta = new Vector2(length, 10f);
  147. rt.pivot = new Vector2(0, 0.5f);
  148. rt.anchorMin = new Vector2(0, 0);
  149. rt.anchorMax = new Vector2(0, 0);
  150. rt.anchoredPosition = start;
  151. rt.localRotation = Quaternion.Euler(0, 0, angle);
  152. // 加線段編號(畫面旁邊)
  153. GameObject label = Instantiate(線段編號Prefab, 繪圖區_panel);
  154. TextMeshProUGUI text = label.GetComponent<TextMeshProUGUI>();
  155. text.text = $"L{線段編號}";
  156. RectTransform textRT = label.GetComponent<RectTransform>();
  157. textRT.anchorMin = textRT.anchorMax = new Vector2(0, 0);
  158. textRT.pivot = new Vector2(0.5f, 0.5f);
  159. // 計算文字偏移(避免被線擋住)
  160. Vector2 mid = (start + end) / 2;
  161. Vector2 offset = Vector2.Perpendicular((end - start).normalized) * 15f;
  162. textRT.anchoredPosition = mid + offset;
  163. label.transform.SetAsLastSibling();
  164. // 紀錄這條線的資訊到對應區域
  165. if (string.IsNullOrEmpty(用途))
  166. 用途 = "未指定";
  167. if (實際長度 < 0f)
  168. 實際長度 = Vector2.Distance(start, end); // fallback 值
  169. AddInfoRow($"L{線段編號}", 用途, 實際長度);
  170. 線段編號++;
  171. }
  172. void AddInfoRow(string 編號, string 用途, float 長度){
  173. GameObject row = Instantiate(資訊列Prefab, 對應資訊區_panel);
  174. TextMeshProUGUI[] labels = row.GetComponentsInChildren<TextMeshProUGUI>();
  175. labels[0].text = 編號 + " | ";
  176. labels[1].text = $"{用途} | {Mathf.Round(長度 * 10f) / 10f} cm";
  177. }
  178. public float[] GetStepWidths(float multiplier){
  179. float[] widths = new float[深.Length];
  180. for (int i = 0; i < 深.Length; i++){
  181. widths[i] = GetDepthValue(深[i], multiplier);
  182. }
  183. return widths;
  184. }
  185. public float[] GetStepHeights(float multiplier)
  186. {
  187. float[] heights = new float[高.Length];
  188. for (int i = 0; i < 高.Length; i++){
  189. heights[i] = GetHeightValue(高[i], multiplier);
  190. }
  191. return heights;
  192. }
  193. private float GetDepthValue(TMP_InputField inputField, float multiplier){
  194. if (!string.IsNullOrWhiteSpace(inputField.text) && float.TryParse(inputField.text, out float value)){
  195. return value * multiplier;
  196. }
  197. return 0f;
  198. }
  199. private float GetHeightValue(TMP_InputField inputField, float multiplier){
  200. if (!string.IsNullOrWhiteSpace(inputField.text) && float.TryParse(inputField.text, out float value)){
  201. return value * multiplier;
  202. }
  203. return 0f;
  204. }
  205. void 計算斜邊(double angle, double 短邊, double 長邊, double setWidth, TextMeshProUGUI 斜邊Text, TextMeshProUGUI 長邊Text){
  206. double angleInRadians = angle * Mathf.Deg2Rad;
  207. double hypotenuse = 短邊 / Mathf.Sin((float)angleInRadians);
  208. double longSide = hypotenuse * Mathf.Cos((float)angleInRadians) - 長邊 + setWidth;
  209. 斜邊Text.text = System.Math.Round(hypotenuse, 1, System.MidpointRounding.AwayFromZero).ToString();
  210. 長邊Text.text = System.Math.Round(longSide, 1, System.MidpointRounding.AwayFromZero).ToString();
  211. }
  212. }