using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.UI.Extensions; using Shapes2D; using TMPro; using System.Linq; public class SetPlan : MonoBehaviour { static private SetPlan _instance; [SerializeField] private Transform _roomContainer; [SerializeField] private Transform _sceneContainer; private List _pointPositions = new(); private List _wallIndexPairs = new(); private const float _presetRoomSize = 192.0f; private const float _presetRoomHeight = 96.0f; private Vector3 _roomPosition; private float _roomHeight = _presetRoomHeight; private void Awake() { if (_instance != null && _instance != this) { Destroy(gameObject); return; } else { _instance = this; } } public static void newRooms(){ _G.WIDE = 192; _G.DEPTH = 168; _G.HEIGHT = 96; DOIT.DELETEAllChild("Plan"); _G.ROOMCLOSE=true; Rooms(); } public static void Rooms(){ DOIT.DELETEAllChild("Plan"); print("_G.ROOM =============================== "+_G.ROOM ); Addpoints(); AddFloor(); if(_G.PNL=="WALLCAB")AddWallNum(); } static public void SetRoomHeight(float value) { _instance._roomHeight = value; _G.HEIGHT = value; } public static void Addpoints(){ //Add Plan true GameObject plan=Get.o3("Canvas",_G.PNL,"Plan"); plan.SetActive(true); //Set linerender UILineRenderer pr=plan.transform.GetComponent(); pr.Points=new Vector2[_G.WallPoints2D.Count+1]; _G.WallPoints2D.Clear(); foreach(Vector3 V3 in _G.WallPoints) { Vector2 point= new(V3.x,V3.y); _G.WallPoints2D.Add(new(V3.x,V3.y)); print("V2===="+point); } _G.WallPoints2D.Add(_G.WallPoints2D[0]); pr.Points=_G.WallPoints2D.ToArray(); } public static void RedrawRoom78(string Ms,float Value){ print("Ms===="+Ms); if(Ms=="m101")_G.WIDE=Value; if(Ms=="m104")_G.DEPTH=Value; if(Ms=="m102")_G.DEPTH=Value; Rooms(); } public static void setM(string M,Vector2 p1,Vector2 p2){ GameObject Mesure=(GameObject)Instantiate(Get.o2("HIDER","Mesure")); Mesure.name=M; Mesure.SetActive(true); Mesure.transform.Find("m").name=M.Replace("M","m"); Mesure.transform.SetParent(Get.o2(_G.PNL,"Plan").transform,false); Mesure.transform.localPosition=(p1+p2)/2; int i= int.Parse(M.Substring(1, M.Length-1)); Mesure.transform.Find("no").gameObject.GetComponent().text=(i-100).ToString(); } public static void addMesures(int Q){ for(int i=1; i().color=col; Get.o1(no+"Rond").GetComponent().color=col; Get.o1(no+"no").GetComponent().color=col; Get.o1(no).GetComponent().blocksRaycasts=false; } static void UpdateWALLCABFinish() { GameObject planGO = Get.o2("WALLCAB", "Plan"); if (planGO == null) return; bool anyOn = false; foreach (Transform child in planGO.transform) { if (child.TryGetComponent(out Toggle t) && t.isOn) { anyOn = true; break; } } Measure.okShow("WALLCAB", "FINISH", anyOn); } public static void AddWallNum() { // Retire les anciens boutons de mur ("w...") pour eviter qu'ils s'accumulent : DELETEAllChild("Plan") // vise un GameObject "Plan" ambigu (plusieurs dans la scene) et ne vide pas toujours WALLCAB/Plan. Transform plan = Get.o2("WALLCAB", "Plan").transform; for (int i = plan.childCount - 1; i >= 0; i--) { string n = plan.GetChild(i).name; if (n.Length > 0 && (n[0] == 'w' || n[0] == 'W')) UnityEngine.Object.DestroyImmediate(plan.GetChild(i).gameObject); } int count = _G.WallPoints2D.Count; if (count < 2) return; if (count > 2 && _G.WallPoints2D[count - 1] == _G.WallPoints2D[0]) count -= 1; Vector2 roomCenter = Vector2.zero; for (int i = 0; i < count; i++) roomCenter += _G.WallPoints2D[i]; roomCenter /= count; HashSet ignored = IgnoredObstacles(); for (int i = 0; i < count; i++) { Vector2 A = _G.WallPoints2D[i]; Vector2 B = _G.WallPoints2D[(i + 1) % count]; Vector2 AB = B - A; float abLen2 = AB.sqrMagnitude; if (abLen2 < 0.0001f) continue; float abLen = Mathf.Sqrt(abLen2); Vector2 dir = AB / abLen; Vector2 normal = new(-dir.y, dir.x); if (Vector2.Dot(normal, (A + B) * 0.5f - roomCenter) < 0) normal = -normal; int wallNum = i + 1; // Portions du mur occupees par un obstacle, en fractions [0..1] le long de A->B. List<(float start, float end, bool isWindow)> spans = ObstacleSpansOnWall(wallNum, A, AB, abLen2, abLen, ignored); if (spans.Count == 0) { // Aucun obstacle : un seul bouton pour tout le mur (comportement d'origine). SpawnWallButton("w" + wallNum, wallNum.ToString(), (A + B) * 0.5f, normal); continue; } // On decoupe le mur : portion libre avant / entre / apres chaque obstacle, // et la fenetre elle-meme devient une section (on peut y mettre une armoire haute). spans.Sort((a, b) => a.start.CompareTo(b.start)); var sections = new List<(float start, float end, bool isWindow)>(); float cursor = 0f; foreach (var span in spans) { if (span.start > cursor) sections.Add((cursor, span.start, false));// portion libre avant if (span.isWindow) sections.Add((span.start, span.end, true)); // la fenetre = section cursor = Mathf.Max(cursor, span.end); } if (cursor < 1f) sections.Add((cursor, 1f, false)); // portion libre apres const float minLen2D = 6f;// ~3 po : on ignore les sections trop etroites pour une armoire int sec = 0; foreach (var s in sections) { if ((s.end - s.start) * abLen < minLen2D) continue; sec++; float mid = (s.start + s.end) * 0.5f; // La section-fenetre se place sous la fenetre (cote interieur) ; les autres a l'oppose. Vector2 sideDir = s.isWindow ? -normal : normal; // Nom "w3-1" (garde la section pour la sauvegarde) mais libelle = numero de mur seul. SpawnWallButton("w" + wallNum + "-" + sec, wallNum.ToString(), A + AB * mid, sideDir); } } _G.NW = count; } // Retourne, pour le mur donne, les segments couverts par un obstacle : (debut, fin) en // fractions 0..1 le long de A->B, et un drapeau indiquant s'il s'agit d'une fenetre. static List<(float start, float end, bool isWindow)> ObstacleSpansOnWall(int wallNum, Vector2 A, Vector2 AB, float abLen2, float abLen, HashSet ignored) { var spans = new List<(float start, float end, bool isWindow)>(); if (_G.OBJs == null) return spans; string wallTag = "w" + wallNum; for (int o = 0; o < _G.OBJs.Length; o++) { string[] C = _G.OBJs[o]; if (C == null || C[0] == null || C[0] == "null") continue; if (C[22] != wallTag) continue;// obstacle non assigne a ce mur (autre mur ou "floor") if (ignored != null && ignored.Contains(o)) continue;// retombee de plafond // Centre de l'obstacle en espace plan (WallPoints2D = 2x l'espace scene). Vector2 P = new(DOIT.CSn(C[15]) * 2f, DOIT.CSn(C[17]) * 2f); float t = Vector2.Dot(P - A, AB) / abLen2; float halfFrac = DOIT.CSn(C[6]) / abLen;// demi-largeur (espace plan) / longueur du mur float start = Mathf.Clamp01(t - halfFrac); float end = Mathf.Clamp01(t + halfFrac); if (end > start) spans.Add((start, end, C[2] == "Window"));// C[2]=="Window" => fenetre } return spans; } // Obstacles a ne pas compter dans le decoupage des murs. Une retombee de plafond (BULKHEAD) // fait 192 de large par defaut : elle couvrirait le mur entier et ferait disparaitre son bouton // du plan, alors qu'elle n'empeche pas d'y poser des armoires (les hautes passent dessous). // Le type n'est pas conserve dans _G.OBJs (BULKHEAD n'est dans aucun switch de ADDOBJECT) : // on le lit sur le nom du GameObject de OBSTACLELIST, "BULKHEAD_12". static HashSet IgnoredObstacles() { var ignored = new HashSet(); GameObject OL = Get.o2("Canvas", "OBSTACLELIST"); if (OL == null) return ignored; foreach (Transform O in OL.transform) { string[] parts = O.name.Split('_'); if (parts.Length < 2 || !int.TryParse(parts[1], out int n)) continue; if (parts[0] == "BULKHEAD") ignored.Add(n); } return ignored; } static void SpawnWallButton(string goName, string label, Vector2 onWallPos, Vector2 sideDir) { Get.o2("HIDER", "BTNwn").SetActive(true); GameObject WN = Instantiate(Get.o2("HIDER", "BTNwn")); WN.transform.Find("NW").GetComponent().text = label; WN.name = goName; WN.transform.SetParent(Get.o2("WALLCAB", "Plan").transform, false); RectTransform rt = WN.GetComponent(); // Decale d'une demi-hauteur pour que le bord du bouton touche la ligne du mur. float half = rt.rect.height * 0.5f; rt.anchoredPosition = onWallPos + sideDir * half; if (WN.TryGetComponent(out Toggle toggle)) toggle.onValueChanged.AddListener(_ => UpdateWALLCABFinish()); Get.o2("HIDER", "BTNwn").SetActive(false); } // Ajuste l'echelle et le centre du plan pour que tout le contenu tienne dans la zone visible du panneau. // A appeler apres avoir cree le contenu (murs, boutons, colliders, marqueurs). // On attend quelques frames : le panneau vient d'etre active et son rect n'est pas encore calcule. public static void FitPlan(string pnl) { StaticCoroutine.Start(FitPlanNextFrame(pnl)); } static IEnumerator FitPlanNextFrame(string pnl) { yield return null; yield return null; yield return null; FitPlanNow(pnl); } static void FitPlanNow(string pnl) { GameObject planGO = Get.o2(pnl, "Plan"); RectTransform plan = planGO != null ? planGO.GetComponent() : null; RectTransform area = plan != null ? plan.parent as RectTransform : null; if (plan == null || area == null) return; if (_G.WallPoints2D == null || _G.WallPoints2D.Count < 2) return; Canvas.ForceUpdateCanvases();// s'assurer que le rect du parent est a jour // Etendue reelle de la piece, mesuree sur les coins des murs (le UILineRenderer qui dessine // les murs n'est PAS capte par les bounds des enfants -> on ne peut pas se fier a ceux-ci). // WallPoints2D est dans le meme repere local que le contenu du plan. Vector2 min = _G.WallPoints2D[0], max = _G.WallPoints2D[0]; foreach (Vector2 p in _G.WallPoints2D) { min = Vector2.Min(min, p); max = Vector2.Max(max, p); } Vector2 size = max - min; if (size.x < 1f || size.y < 1f) return; Vector2 contentCenter = (min + max) * 0.5f; Rect ar = area.rect; const float pad = 0.75f;// marge : le plan occupe ~80% de la zone float s = Mathf.Min(ar.width * pad / size.x, ar.height * pad / size.y); if (s <= 0f || float.IsInfinity(s) || float.IsNaN(s)) return; // Plan ancre au centre du parent, pivot (0,0) : un enfant en coords locales c s'affiche a // centreParent + anchoredPosition + c*s. On met le centre du contenu au centre de la zone. plan.localScale = new Vector3(s, s, 1f); plan.anchoredPosition = -contentCenter * s; } public static float getA(Vector2 d){ float A=0; d.x= Mathf.Round(d.x * 10f) / 10f; d.y= Mathf.Round(d.y * 10f) / 10f; if(d.x==1 && d.y==0.0f)A=0; if(d.x==0.7f && d.y==-0.7f)A=45; if(d.x==0.0f && d.y==-1.0f)A=90; if(d.x==-1.0f && d.y==0.0f)A=180; if(d.x==0.0f && d.y==1.0f)A=270; if(d.x==0.7f && d.y==0.7f)A=315; return A; } public static void RoomSize() { // // _G.NW-=1; // for(int i=1; i<_G.NW+1; i++){ // _G.WallsWidth[i-1] = Vector2.Distance(_G.wP[i-1],_G.wP[i]); // //print("_G.WallsWidth[i-1]==="+_G.WallsWidth[i-1]); // } // _G.WallsWidth[_G.NW-1] = Vector2.Distance(_G.wP[_G.NW-1],_G.wP[1-1]); // float Xmax=0; // float Xmin=0; // float Ymax=0; // float Ymin=0; // for(int i=1; i<_G.NW+1; i++){ // if(_G.wP[i-1].xXmax)Xmax=_G.wP[i-1].x; // if(_G.wP[i-1].yYmax)Ymax=_G.wP[i-1].y; // } // for(int i=1; i<_G.NW; i++){ // _G.wD[i-1]=DRAW.sense(_G.wP[i-1],_G.wP[i]); // } // _G.wD[_G.NW-1]=DRAW.sense(_G.wP[_G.NW-1],_G.wP[1-1]); // for(int i=1; i<_G.NW+1; i++){ // _G.WallsPointCenter[i-1]=(_G.wP[i-1] + _G.wP[i])/2; // } // _G.WallsPointCenter[_G.NW-1]=(_G.wP[_G.NW-1] + _G.wP[1-1])/2; // _G.WIDE=Xmax-Xmin; // _G.DEPTH=Ymax-Ymin; // //_G.NW+=1; } public static void AddFloor() { float scR=800/_G.WIDE/3;//2.5 if(_G.DEPTH*scR>1200)scR=1200/_G.DEPTH/3;//1.5 Get.o2(_G.PNL,"Plan").transform.localScale=new Vector3(scR,scR,1); } }