using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI.Extensions; using TMPro; using UnityEngine.UI; using System; using Newtonsoft.Json; using System.Linq; public class WallLine : MonoBehaviour, IWallSelection { static public float RatioWorldToScreen => 2.0f; [SerializeField] private UILineRenderer _lineRenderer; [SerializeField] private RectTransform _lineCollider; // [SerializeField] private float _thickness = 10.0f; // TODO : have inner and outer wall lengths adjusted by thicknesses and angles [SerializeField] private RectTransform _inputFieldParent; [SerializeField] private InputField _inputField; [SerializeField] private Button _buttonStartDirection; [SerializeField] private Button _buttonEndDirection; private RectTransform _inputFieldTransform; private RectTransform _buttonStartTransform; private RectTransform _buttonEndTransform; private bool _isInputProcessing; public WallPoint _pointStart; public WallPoint _pointEnd; WallLineInfo _lineInfo; public WallLineInfo LineInfo { get { return _lineInfo; } set { _lineInfo = value; } } private float _thickness = 6f; public float Thickness { get { return _thickness; } set { _thickness = value; UpdateValuesFromPoints(); } } public string _category; private Vector2 _previousMousePosition; private Vector2 _preDragStartPointPosition; private Vector2 _preDragEndPointPosition; private bool _isNewDragStartPoint; private bool _isNewDragEndPoint; private bool _isDragging; private float _doubleClickInterval = 0.5f; private float _clickTime; private int _clickCount; [SerializeField] private Color _defaultColor; [SerializeField] private Color _hoverColor; [SerializeField] private Color _selectionColor; public List mat; public List interiorWallFacesNumber = new List(); public bool HasAwaken { get; private set; } public void Awake() { if (HasAwaken) { return; } HasAwaken = true; _inputFieldTransform = _inputField.GetComponent(); //_buttonStartTransform = _buttonStartDirection.GetComponent(); //_buttonEndTransform = _buttonEndDirection.GetComponent(); } public void InitPoints(WallPoint pointStart, WallPoint pointEnd) { _pointStart = pointStart; _pointEnd = pointEnd; UpdateValuesFromPoints(); _pointStart.TryAddWall(this); _pointEnd.TryAddWall(this); } public static void UpdateValuesFromPointsStatic(GameObject Line,WallPoint pointStart, WallPoint pointEnd) { UILineRenderer lineRenderer=Line.GetComponent(); float Thickness=lineRenderer.LineThickness; RectTransform lineCollider=Line.transform.Find("Line Collider").GetComponent(); RectTransform inputFieldTransform=Line.transform.Find("Modify Length/WallMeasure").gameObject.GetComponent(); print("Set dimenssions Now"); Vector2 halfScreen = new(Screen.width / 2.0f, Screen.height / 2.0f); // TODO : get/set screen (to world?) position halfScreen = Vector2.zero; Vector2 startPosition = pointStart.GetAnchoredPosition() - halfScreen; Vector2 endPosition = pointEnd.GetAnchoredPosition() - halfScreen; // line renderer lineRenderer.Points[0] = startPosition; lineRenderer.Points[1] = endPosition; lineRenderer.LineThickness = Thickness; lineRenderer.SetVerticesDirty(); //_lineRenderer.SetAllDirty(); // line collider Vector2 line = endPosition - startPosition; Vector2 linePosition = startPosition + (line / 2.0f); float lineLength = line.magnitude; Quaternion lineRotation = Quaternion.AngleAxis(Vector2.SignedAngle(Vector2.right, line), Vector3.forward); // use of AngleAxis to force rotation on Z axis SetRectTransformStatic(lineCollider, linePosition, new Vector2(lineLength, Thickness), lineRotation); // input field float inputDistance = (Thickness + inputFieldTransform.sizeDelta.y) / 2.0f; float signDot = Mathf.Sign(Vector2.Dot(Vector2.right, line)); Vector3 uprightDirection = Vector3.right * signDot; Quaternion inputRotation = Quaternion.FromToRotation(uprightDirection, (Vector3)line); Vector2 inputOffset = Quaternion.FromToRotation(Vector3.right, Vector3.up) * line.normalized * inputDistance; Vector2 inputFieldPosition = linePosition + inputOffset; #if true // TODO : temporary management of input field positions by flipping them if too close foreach (RectTransform wallTransform in WallCreationManager.GetWallLineContainer()) { if (wallTransform == Line.GetComponent()) { continue; } WallLine wallLine = wallTransform.GetComponent(); bool isColliding = wallLine.AreInputFieldsColliding(inputFieldPosition, inputOffset, 0.0f, Line.transform.GetSiblingIndex()); if (isColliding) { Vector2 flippedPosition = linePosition - inputOffset; if (!wallLine.AreInputFieldsColliding(flippedPosition, -inputOffset, 0.0f, Line.transform.GetSiblingIndex())) { inputFieldPosition = flippedPosition; break; } } } #endif SetRectTransformStatic(inputFieldTransform, inputFieldPosition, inputRotation); //SetInputFieldValue(lineLength); // input buttons Quaternion buttonRotation = Quaternion.AngleAxis(Vector2.SignedAngle(Vector2.up, line), Vector3.forward); Vector2 directionalButtonOffset = lineRotation * Vector2.right * (inputFieldTransform.sizeDelta.x / 2.0f + Thickness); //SetRectTransform(_buttonEndTransform, inputFieldPosition + directionalButtonOffset, buttonRotation); //SetRectTransform(_buttonStartTransform, inputFieldPosition - directionalButtonOffset, buttonRotation); //lineRotation); } public void UpdateValuesFromPoints() { //print("Set dimenssions Now"); Vector2 halfScreen = new(Screen.width / 2.0f, Screen.height / 2.0f); // TODO : get/set screen (to world?) position halfScreen = Vector2.zero; Vector2 startPosition = _pointStart.GetAnchoredPosition() - halfScreen; Vector2 endPosition = _pointEnd.GetAnchoredPosition() - halfScreen; // line renderer _lineRenderer.Points[0] = startPosition; _lineRenderer.Points[1] = endPosition; _lineRenderer.LineThickness = Thickness; _lineRenderer.SetVerticesDirty(); //_lineRenderer.SetAllDirty(); // line collider Vector2 line = endPosition - startPosition; Vector2 linePosition = startPosition + (line / 2.0f); float lineLength = line.magnitude; Quaternion lineRotation = Quaternion.AngleAxis(Vector2.SignedAngle(Vector2.right, line), Vector3.forward); // use of AngleAxis to force rotation on Z axis SetRectTransform(_lineCollider, linePosition, new Vector2(lineLength, Thickness), lineRotation); // input field float inputDistance = (Thickness + _inputFieldTransform.sizeDelta.y) / 2.0f; float signDot = Mathf.Sign(Vector2.Dot(Vector2.right, line)); Vector3 uprightDirection = Vector3.right * signDot; Quaternion inputRotation = Quaternion.FromToRotation(uprightDirection, (Vector3)line); Vector2 inputOffset = Quaternion.FromToRotation(Vector3.right, Vector3.up) * line.normalized * inputDistance; Vector2 inputFieldPosition = linePosition + inputOffset; #if true // TODO : temporary management of input field positions by flipping them if too close foreach (RectTransform wallTransform in WallCreationManager.GetWallLineContainer()) { if (wallTransform == GetComponent()) { continue; } WallLine wallLine = wallTransform.GetComponent(); bool isColliding = wallLine.AreInputFieldsColliding(inputFieldPosition, inputOffset, 0.0f, transform.GetSiblingIndex()); if (isColliding) { Vector2 flippedPosition = linePosition - inputOffset; if (!wallLine.AreInputFieldsColliding(flippedPosition, -inputOffset, 0.0f, transform.GetSiblingIndex())) { inputFieldPosition = flippedPosition; break; } } } #endif SetRectTransform(_inputFieldTransform, inputFieldPosition, inputRotation); SetInputFieldValue(lineLength); // input buttons Quaternion buttonRotation = Quaternion.AngleAxis(Vector2.SignedAngle(Vector2.up, line), Vector3.forward); Vector2 directionalButtonOffset = lineRotation * Vector2.right * (_inputFieldTransform.sizeDelta.x / 2.0f + Thickness); //SetRectTransform(_buttonEndTransform, inputFieldPosition + directionalButtonOffset, buttonRotation); //SetRectTransform(_buttonStartTransform, inputFieldPosition - directionalButtonOffset, buttonRotation); //lineRotation); } private void SetRectTransform(RectTransform rectTransform, Vector2 position, Quaternion rotation) { rectTransform.anchoredPosition = position; rectTransform.rotation = rotation; } private void SetRectTransform(RectTransform rectTransform, Vector2 position, Vector2 size, Quaternion rotation) { rectTransform.sizeDelta = size; SetRectTransform(rectTransform, position, rotation); } public static void SetRectTransformStatic(RectTransform rectTransform, Vector2 position, Quaternion rotation) { rectTransform.anchoredPosition = position; rectTransform.rotation = rotation; } public static void SetRectTransformStatic(RectTransform rectTransform, Vector2 position, Vector2 size, Quaternion rotation) { rectTransform.sizeDelta = size; SetRectTransformStatic(rectTransform, position, rotation); } public void SetDragState(bool isDragging) { _isDragging = isDragging; WallSelection.IsDragging = isDragging; WallSelection.SetAllToDefaultColor(this); ShowAdjacentInputFields(_pointStart, isDragging); ShowAdjacentInputFields(_pointEnd, isDragging); if (_isDragging) { _preDragStartPointPosition = _pointStart.GetAnchoredPosition(); _preDragEndPointPosition = _pointEnd.GetAnchoredPosition(); } else { _isNewDragStartPoint = false; _isNewDragEndPoint = false; } } public bool GetDragState() { return _isDragging; } public void UpdatePreviousMousePosition() { _previousMousePosition = WallCreationManager.GetMousePositionOnScreen(); } public bool IsExterior() { return _category == "Exterior"; } public float GetWallThickness(bool ignoreCategory = false) { if (!ignoreCategory && IsExterior()) { return 0f; } return Thickness; } public void HandleDrag() { Vector2 mousePosition = WallCreationManager.GetMousePositionOnScreen(); Vector2 translation = mousePosition - _previousMousePosition; int otherWallCountPointStart = _pointStart.GetWalls().Count - 1; int otherWallCountPointEnd = _pointEnd.GetWalls().Count - 1; if (otherWallCountPointStart == 0 && otherWallCountPointEnd == 0) { _pointStart.SetAnchoredPosition(_pointStart.GetAnchoredPosition() + translation); _pointEnd.SetAnchoredPosition(_pointEnd.GetAnchoredPosition() + translation); } else { Vector2 directionLine = (_preDragEndPointPosition - _preDragStartPointPosition).normalized; Vector3 perpendicularTranslation = Quaternion.FromToRotation(Vector3.right, Vector3.up) * directionLine; float perpendicularDot = Vector2.Dot(perpendicularTranslation, translation); perpendicularTranslation *= perpendicularDot; // single other wall Vector2 translationSingleOtherWallStart = Vector2.zero; Vector2 translationSingleOtherWallEnd = Vector2.zero; bool isStartParallel = false; bool isEndParallel = false; for (int i = 0; i < 2; i++) { bool isStart = i == 0; WallPoint currentPoint = isStart ? _pointStart : _pointEnd; if (currentPoint.GetWalls().Count - 1 == 0) { continue; } WallPoint otherPoint = currentPoint.GetWalls().Find(x => x != this).GetOtherPoint(currentPoint); Vector2 currentPreDragPointPosition = isStart ? _preDragStartPointPosition : _preDragEndPointPosition; Vector2 directionSingleOtherWall = (otherPoint.GetAnchoredPosition() - currentPreDragPointPosition).normalized; //Vector2 translationSingleOtherWall = directionSingleOtherWall * Vector2.Dot(directionSingleOtherWall, translation); float angle = Vector2.Angle(perpendicularTranslation, Mathf.Sign(perpendicularDot) * directionSingleOtherWall); float cosine = Mathf.Cos(angle * Mathf.Deg2Rad); float hypothenuse = perpendicularDot / cosine; float angleBuffer = 0.01f; // TODO : "project" wall instead when parallel (and maybe other angles) bool isParallel = (angle > 90.0f - angleBuffer && angle < 90.0f + angleBuffer) || (angle > 270.0f - angleBuffer && angle < 270.0f + angleBuffer); //isParallel &= isStart ? !_isNewDragStartPoint : !_isNewDragEndPoint; Vector2 translationSingleOtherWall = isParallel ? Vector2.zero : directionSingleOtherWall * hypothenuse; if (isStart) { translationSingleOtherWallStart = translationSingleOtherWall; isStartParallel = isParallel; } else { translationSingleOtherWallEnd = translationSingleOtherWall; isEndParallel = isParallel; } } // TODO : use some form of dot(s) to find valid wall if multi-other-walled point due to some angles being blocked from the moving wall's view // TODO : rethink new wall projection logic Vector2 translationStart = otherWallCountPointStart > 0 && !_isNewDragStartPoint ? translationSingleOtherWallStart : perpendicularTranslation; Vector2 translationEnd = otherWallCountPointEnd > 0 && !_isNewDragEndPoint ? translationSingleOtherWallEnd : perpendicularTranslation; // create wall projection if adjacent to a parallel wall and moving in parallel float dragProjectionThreshold = 0.0f; // TODO : remove? Vector2 initialStartPosition = _pointStart.GetAnchoredPosition(); if (isStartParallel && perpendicularTranslation.sqrMagnitude > dragProjectionThreshold) { WallPoint oldStartPoint = _pointStart; WallPoint newStartPoint = WallCreationManager.CreatePoint(initialStartPosition); WallLine newLine = WallCreationManager.CreateLine(oldStartPoint, newStartPoint, this.GetCopy()); #if true // TODO : see if still necessary for sequential walls newLine.transform.SetSiblingIndex(transform.GetSiblingIndex()); #endif InitPoints(newStartPoint, _pointEnd); oldStartPoint.TryRemoveWall(this); _isNewDragStartPoint = true; translationStart = perpendicularTranslation; //Debug.Log($"handle drag : project start ; perpendicular : {perpendicularTranslation}"); ShowAdjacentInputFields(oldStartPoint, false); ShowAdjacentInputFields(newStartPoint, true); } Vector2 initialEndPosition = _pointEnd.GetAnchoredPosition(); if (isEndParallel && perpendicularTranslation.sqrMagnitude > dragProjectionThreshold) { WallPoint oldEndPoint = _pointEnd; WallPoint newEndPoint = WallCreationManager.CreatePoint(initialEndPosition); WallLine newLine = WallCreationManager.CreateLine(newEndPoint, oldEndPoint, this.GetCopy()); #if true // TODO : see if still necessary for sequential walls newLine.transform.SetSiblingIndex(transform.GetSiblingIndex() + 1); #endif InitPoints(_pointStart, newEndPoint); oldEndPoint.TryRemoveWall(this); _isNewDragEndPoint = true; translationEnd = perpendicularTranslation; //Debug.Log($"handle drag : project end ; perpendicular : {perpendicularTranslation}"); ShowAdjacentInputFields(oldEndPoint, false); ShowAdjacentInputFields(newEndPoint, true); } // move wall points _pointStart.SetAnchoredPosition(initialStartPosition + translationStart); _pointEnd.SetAnchoredPosition(initialEndPosition + translationEnd); } UpdatePointsAndWalls(); UpdatePreviousMousePosition(); //SetColorOnEvent(Color.green); } public void DestroyWall() { _pointStart.TryRemoveWall(this); _pointEnd.TryRemoveWall(this); //transform.SetParent(WallCreationManager.GetDeletionContainer()); Destroy(_inputFieldParent.gameObject); Destroy(gameObject); } public bool ValidateWall() { Vector2 line = _pointEnd.GetAnchoredPosition() - _pointStart.GetAnchoredPosition(); if (line.sqrMagnitude < 0.01f) { DestroyWall(); return false; } return true; } public bool ContainsWallPoint(WallPoint wallPoint) { return wallPoint == _pointStart || wallPoint == _pointEnd; } public WallPoint GetOtherPoint(WallPoint wallPoint) { if (!ContainsWallPoint(wallPoint)) { return null; } return wallPoint != _pointStart ? _pointStart : _pointEnd; } public void SetActiveInputFieldBackground(bool isEnabled) { print(transform.name); GameObject Keyboard=Get.o2("HIDER","KEYBOARDNUMBERPRO");//WallLine4 Keyboard.SetActive(true); _G.VTcat = "TMP"; string StartValue=Get.o2("Panel_DRAWPlan_NEW/Plan2D_UI/WallLineContainer/"+name+"/Modify Length","InputField (TMP)").GetComponent().text; Keyboard.transform.Find("InputMesure").transform.GetComponent().text=StartValue; //SetAsSelection(isEnabled); // TODO : manage already selected? //StaticCoroutine.Start(SetActiveInputFieldCoroutine(isEnabled, gameObject)); } private IEnumerator SetActiveInputFieldCoroutine(bool isEnabled, GameObject go) { string previousText = _inputField.text; //_isInputProcessing = value || _isInputProcessing; //Debug.Log($"Started input field coroutine : {(isEnabled ? "enable" : "disable")} ; value : {previousText}"); bool hasNotClicked = !Input.GetMouseButtonUp(0) && !Input.GetMouseButtonUp(1); bool isInactive = _lineCollider == null && go != null; // rect transform is null while not active while (hasNotClicked || isInactive) { yield return null; //Debug.Log($"looping : {_lineCollider == null}"); hasNotClicked &= !Input.GetMouseButtonUp(0) && !Input.GetMouseButtonUp(1); isInactive = _lineCollider == null && go != null; } if (go == null) { //Debug.Log("break"); yield break; } if (_isInputProcessing && !isEnabled) // reset value on unselect { //SetInputFieldValue(GetInputFieldValue()); UpdateInputFieldValue(); //_isInputProcessing = false; } //Debug.Log($"Ended input field coroutine : {(isEnabled ? "enable" : "disable")} ; value : {previousText}"); Quaternion rotation; if (isEnabled) { rotation = Quaternion.FromToRotation(Vector3.right, Vector3.right); } else { Vector2 line = _pointEnd.GetAnchoredPosition() - _pointStart.GetAnchoredPosition(); float signDot = Mathf.Sign(Vector2.Dot(Vector2.right, line)); Vector3 uprightDirection = Vector3.right * signDot; rotation = Quaternion.FromToRotation(uprightDirection, (Vector3)line); } SetRectTransform(_inputFieldTransform, _inputFieldTransform.anchoredPosition, rotation); _buttonStartDirection.gameObject.SetActive(isEnabled); _buttonEndDirection.gameObject.SetActive(isEnabled); _isInputProcessing = isEnabled; } private static float GetInputFieldValue(string Value) // TODO : use imperial/metric class { float value = DOIT.TryConvertStringToNumber(Value, out int system) * RatioWorldToScreen; // is imperial if (system == 1 && _G.System == 2) { value /= 2.54f; } // is metric else if (system == 2 && _G.System == 1) { value *= 2.54f; } // is invalid (manage elsewhere too? add safety just in case by setting line current value) else if (system == 0 && value == 0.0f) { //value = _lineCollider.sizeDelta.x; //SetInputFieldValue(value); } return value; //if (float.TryParse(_inputField.text, out float result)) //{ // return result * _ratioInputToScreen; //} // //Debug.Log("invalid input entered for wall length"); //return 0.0f; } private void SetInputFieldValue(float value) // TODO : use imperial/metric class { //_inputField.text = Mathf.Round(value / _ratioInputToScreen).ToString(); _inputField.text = DOIT.ConvertNumberToString(value / RatioWorldToScreen); } public void UpdateInputFieldValue() { SetInputFieldValue(_lineCollider.sizeDelta.x); } public void SetLengthFromButton(Button button) { //Debug.Log($"button pressed : {button}"); bool isEndButton = button != _buttonStartDirection; SetLength(isEndButton); } public static void RedimensionLength(string Side,GameObject Line,float OldValue,float NewValue) { print("Line name===="+Line.name+" Add to plan2D newValue == "+OldValue+"NewValue=== "+NewValue); //Get line to modify //Find line if Input is in SelectedElementContainer //SelectedElementContainer Add to plan2D newValue == 192NewValu if(Line.name=="SelectedElementContainer"){ foreach(Transform WLine in Get.o2("Panel_DRAWPlan_NEW/Plan2D_UI","WallLineContainer").transform){ if(!WLine.Find("Modify Length"))Line=WLine.gameObject; } } WallPoint fromPoint = Line.GetComponent()._pointStart; WallPoint toPoint = Line.GetComponent(). _pointEnd; if(Side=="Both"){NewValue*=0.5f;} //NewValue=NewValue-OldValue; Vector2 line = toPoint.GetAnchoredPosition() - fromPoint.GetAnchoredPosition(); Vector2 direction = line.normalized; Vector2 translate = direction * GetInputFieldValue(NewValue.ToString()); Vector2 Center = Vector2.Lerp(fromPoint.GetAnchoredPosition(), toPoint.GetAnchoredPosition(), 0.5f); if(Side=="Both"){ fromPoint.SetAnchoredPosition(Center - translate); toPoint.SetAnchoredPosition(Center + translate); } if(Side=="Left"){ fromPoint.SetAnchoredPosition(toPoint.GetAnchoredPosition() - translate); } if(Side=="Right"){ toPoint.SetAnchoredPosition(fromPoint.GetAnchoredPosition() + translate); } //Refresh Plan Line.GetComponent().UpdateValuesFromPoints(); Line.GetComponent().UpdatePointsAndWalls(); } public void SetLength(bool isTowardsEndPoint) { WallPoint fromPoint = isTowardsEndPoint ? _pointStart : _pointEnd; WallPoint toPoint = isTowardsEndPoint ? _pointEnd : _pointStart; float inputValue = GetInputFieldValue(_inputField.text); Vector2 line = toPoint.GetAnchoredPosition() - fromPoint.GetAnchoredPosition(); Vector2 direction = line.normalized; Vector2 translate = direction * inputValue; toPoint.SetAnchoredPosition(fromPoint.GetAnchoredPosition() + translate); UpdatePointsAndWalls(); _isInputProcessing = false; } public void OnEditEndInputFieldEvent() { if (!_inputField.wasCanceled) { SetInputFieldValue(GetInputFieldValue(_inputField.text)); bool isEnterKeyDown = Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter); if (isEnterKeyDown) { bool isShiftKeyPressed = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); //Debug.Log($"input confirmed for wall {this} ; shift pressed : {isShiftKeyPressed}"); SetLength(!isShiftKeyPressed); } } //else //{ // Debug.Log($"input cancelled for wall {this}"); //} return; } private void UpdatePointsAndWalls() { UpdateValuesFromPoints(); _pointStart.UpdateWalls(this); _pointEnd.UpdateWalls(this); } public RectTransform GetColliderTransform() { return _lineCollider; } public void OnDoubleClickSplitWallEvent() { if (!WallCreationManager._isAddingWall && !Input.GetMouseButtonDown(0)) // TODO : check if tap works { return; } _clickCount++; if (_clickCount == 1) { //Debug.Log("1st click"); _clickTime = Time.time; return; } if (Time.time - _clickTime > _doubleClickInterval) { //Debug.Log("reset click"); _clickTime = Time.time; _clickCount = 1; return; } if (_clickCount == 2) { //Debug.Log("double click"); SplitWall(WallCreationManager.GetMousePositionOnScreen()); _clickCount = 0; } } public void SplitWall() { Vector2 startPosition = _pointStart.GetAnchoredPosition(); Vector2 endPosition = _pointEnd.GetAnchoredPosition(); Vector2 line = endPosition - startPosition; Vector2 pointPosition = startPosition + line / 2.0f; SplitWall(pointPosition); } public void SplitWall(Vector2 pointPosition) { Vector2 startPosition = _pointStart.GetAnchoredPosition(); Vector2 endPosition = _pointEnd.GetAnchoredPosition(); Vector2 lineDirection = (endPosition - startPosition).normalized; Vector2 linePoint = pointPosition - startPosition; float dot = Vector2.Dot(lineDirection, linePoint); Vector2 splitPosition = startPosition + lineDirection * dot; WallPoint wallPoint = WallCreationManager.CreatePoint(splitPosition); wallPoint.MergeWithWall(this); } public void SetColorOnEvent(Color color) { _lineRenderer.color = color; } public void PointerEnterColor(bool isEntered) { if (WallSelection.IsDragging || WallSelection.CompareSelection(this)) { return; } if (WallSelection.TryGetCurrentSelection(out IWallSelection selection)) { WallPoint selectedWallPoint = selection.GetGameObject().GetComponent(); if (selectedWallPoint != null) { if (!ContainsWallPoint(selectedWallPoint)) { ShowInputField(isEntered); } } else { ShowInputField(isEntered); } } SelectionModePlan2D mode = isEntered ? SelectionModePlan2D.Hover : SelectionModePlan2D.Default; SetSelectionColor(mode); } public void ShowInputField(bool isVisible) { _inputField.gameObject.SetActive(isVisible); if (!isVisible) { //_buttonStartDirection.gameObject.SetActive(false); //_buttonEndDirection.gameObject.SetActive(false); _isInputProcessing = false; } } public void ShowAdjacentInputFields(WallPoint point, bool isVisible) { foreach (WallLine item in point.GetWalls()) { if (item == this) { continue; } item.ShowInputField(isVisible); } } public void PointerEnterHover(bool isEntered) { if (WallSelection.CompareSelection(this)) { return; } if (!WallSelection.IsDragging) { SelectionModePlan2D mode = isEntered ? SelectionModePlan2D.Hover : SelectionModePlan2D.Default; SetSelectionColor(mode); } } public void DestroySelected() { if (IsInteriorWall()) { DestroyWall(); } else { _pointStart.MergeWithPoint(_pointEnd); } } public void SetAsSelection(bool isSelected) { WallSelection.SetSelection(this, isSelected); } public void HandleSelection(bool isSelected) { if (WallSelection.TryGetCurrentSelection(out IWallSelection selection)) { ShowInputField(isSelected); RectTransform parent = isSelected ? WallSelection.GetSelectedElementContainer() : GetComponent(); _inputFieldParent.SetParent(parent); } else { ShowInputField(true); } _inputField.image.enabled = true;//isSelected; SelectionModePlan2D mode = isSelected ? SelectionModePlan2D.Select : SelectionModePlan2D.Default; SetSelectionColor(mode); } public void OnClickDestroyEvent() { if (!WallCreationManager._isAddingWall && Input.GetMouseButtonDown(1)) { // TODO : temporary disabling targeted wall destruction if (IsInteriorWall()) { DestroyWall(); } else { _pointStart.MergeWithPoint(_pointEnd); } } } public void OnClickSelectEvent() { if (Input.GetMouseButtonDown(0)) { SetAsSelection(true); string Title = TRANS.This("T_EXTERIOR WALL"); Color ColorHeader = Color.red; if (transform.GetComponent()._category == "Exterior") { bool isInvesible = false; if (transform.GetComponent().LineThickness == 2) { isInvesible = true; } WallSelection.AddInputOption(ColorHeader, Title, "Invisible", isInvesible, 0, 0); } if (transform.GetComponent()._category == "Interior") { Title = TRANS.This("T_INTERIOR WALL"); ColorHeader = Color.blue; bool isInvesible = false; if (transform.GetComponent().LineThickness == 2) { isInvesible = true; } float Thickness = transform.GetComponent().Thickness; WallSelection.AddInputOption(ColorHeader, Title, "InputThickness_Offset", isInvesible, 0, Thickness); } _G.SelectedWall2D = this; } } public void OnPointerInteraction(bool isEnter) // placeholder { string pointerMode = isEnter ? "entered" : "exited"; Debug.Log($"pointer {pointerMode} : {gameObject}"); } public void SetSelectionColor(SelectionModePlan2D mode) { Color color = _defaultColor; switch (mode) { case SelectionModePlan2D.Default: color = _defaultColor; break; case SelectionModePlan2D.Hover: color = _hoverColor; break; case SelectionModePlan2D.Select: color = _selectionColor; break; default: break; } SetColorOnEvent(color); } public void InsertWall() // TODO : temporary? { SplitWall(); } public GameObject GetGameObject() { return gameObject; } public bool AreInputFieldsColliding(Vector2 fromInputFieldPoint, Vector2 fromInputFieldOffset, float distance, int fromIndex) { bool isColliding = false; //float sqrDistance = distance * distance; distance = 5.0f; float sqrDistance = distance * distance; // wall collision // closest point to wall if (true) { Vector2 startPoint = _pointStart.GetAnchoredPosition(); Vector2 line = _pointEnd.GetAnchoredPosition() - startPoint; Vector2 from = fromInputFieldPoint - startPoint; Vector2 lineDirection = line.normalized; float dot = Vector2.Dot(from, lineDirection); if (dot < 0 || dot * dot > line.sqrMagnitude) { //continue; // note : can still be in range of a point } Vector2 closestPointOnLine = startPoint + lineDirection * dot; Vector2 projectionOnLine = closestPointOnLine - fromInputFieldPoint; float currentSqrDistance = projectionOnLine.sqrMagnitude; // TODO : re-evaluate (near > current) for relative wall if (currentSqrDistance < fromInputFieldOffset.sqrMagnitude) { //Debug.Log($"wall collison with : from {fromIndex} to {transform.GetSiblingIndex()}"); isColliding = true; } //if () //{ // isColliding = true; //} } // inputfield collision Vector2 toInputField = _inputFieldTransform.anchoredPosition - fromInputFieldPoint; if (toInputField.sqrMagnitude < fromInputFieldOffset.sqrMagnitude * 4.0f) { //Debug.Log($"input collison with : from {fromIndex} to {transform.GetSiblingIndex()}"); isColliding = true; } return isColliding; } public Vector2 GetNearestPositionOnWall(Vector2 screenAnchoredPosition) { Vector2 startPoint = _pointStart.GetAnchoredPosition(); Vector2 endPoint = _pointEnd.GetAnchoredPosition(); Vector2 line = endPoint - startPoint; Vector2 from = screenAnchoredPosition - startPoint; Vector2 lineDirection = line.normalized; float dot = Vector2.Dot(from, lineDirection); Vector2 outPosition; if (dot < 0) { outPosition = startPoint; } else if (dot * dot > line.sqrMagnitude) { outPosition = endPoint; } else { outPosition = startPoint + lineDirection * dot; } return outPosition; } public Vector3 GetNearestPositionOnWall(Vector3 worldPosition) { int wallIndex; if (name.StartsWith("WallLine")) { wallIndex = int.Parse(name["WallLine".Length..]) - 1; } else { Debug.LogError("wall line name or index unassigned"); return worldPosition; } return GetNearestPositionOnWall(worldPosition, wallIndex); } public static Vector3 GetNearestPositionOnWall(Vector3 worldPosition, int wallIndex) { Vector2 startPoint = _G.WallsPointStart[wallIndex]; Vector2 endPoint = _G.WallsPointCenter[wallIndex] + (_G.WallsPointCenter[wallIndex] - startPoint); Vector2 line = endPoint - startPoint; Vector2 from = new Vector2(worldPosition.x, worldPosition.z) - startPoint; Vector2 lineDirection = line.normalized; float dot = Vector2.Dot(from, lineDirection); Vector2 outPosition; if (dot < 0) { outPosition = startPoint; } else if (dot * dot > line.sqrMagnitude) { outPosition = endPoint; } else { outPosition = startPoint + lineDirection * dot; } return new Vector3(outPosition.x, worldPosition.y, outPosition.y); } public static void CreateWallInfo(WallLine currentWallLine, out WallLineInfo wallInfo, out bool connectToPrevious, out bool connectToNext) { WallPoint startPoint = currentWallLine._pointStart; WallPoint endPoint = currentWallLine._pointEnd; CreateWallInfo(currentWallLine, out startPoint, out endPoint, out wallInfo, out connectToPrevious, out connectToNext); } public static void CreateWallInfo(WallLine currentWallLine, out WallPoint startPoint, out WallPoint endPoint, out WallLineInfo wallInfo, out bool connectToPrevious, out bool connectToNext) { startPoint = currentWallLine._pointStart; endPoint = currentWallLine._pointEnd; wallInfo = new(currentWallLine); connectToPrevious = CheckConnectedWalls(startPoint, currentWallLine, ref wallInfo, checkWithPrevious: true); connectToNext = CheckConnectedWalls(endPoint, currentWallLine, ref wallInfo, checkWithPrevious: false); currentWallLine.LineInfo = wallInfo; } private static bool CheckConnectedWalls(WallPoint point, WallLine currentWallLine, ref WallLineInfo wallInfo, bool checkWithPrevious) { var walls = point.GetWalls(); if (walls.Count <= 1) return false; // No need to check if only one wall exists. // Find the previous and next walls using your custom logic. var connections = FindNextAndPreviousWalls(point, Vector3.right, currentWallLine, out WallLine previousWall, out WallLine nextWall); // Initialize variables to track connections and intersections. bool hasConnection = false; // Get the central connection point (the point where the walls meet) Vector3 centralPoint = checkWithPrevious ? wallInfo.startPosition : wallInfo.endPosition; // Assuming this is where all walls connect. var previousWallInfo = new WallLineInfo(previousWall); List> previousWallSegments = previousWallInfo.CreateWallSegments(); List> previousWallSegmentsPlan2d = previousWallInfo.CreateWallSegmentsPlan2d(); var nextWallInfo = new WallLineInfo(nextWall); List> nextWallSegments = nextWallInfo.CreateWallSegments(); List> nextWallSegmentsPlan2d = nextWallInfo.CreateWallSegmentsPlan2d(); int indexForCurrentWallSegmentWithPreviousWall = connections.ElementAt(0)[0]; int indexSegmentFromPreviousWall = connections.ElementAt(0)[1]; int indexForCurrentWallSegmentWithNextWall = connections.ElementAt(1)[0]; int indexSegmentFromNextWall = connections.ElementAt(1)[1]; wallInfo.SetConnection(point, previousWall, connections[0], nextWall, connections[1]); List> currentWallSegments = wallInfo.CreateWallSegments(); List> currentWallSegmentsPlan2d = wallInfo.CreateWallSegmentsPlan2d(); List currentWallSegment = currentWallSegments[indexForCurrentWallSegmentWithPreviousWall]; List previousWallSegment = previousWallSegments[indexSegmentFromPreviousWall]; if (FindIntersectionWithWall(currentWallSegment, currentInfo: wallInfo, previousWallSegment, previousWallInfo, out Vector3 prevIntersection)) { if (indexForCurrentWallSegmentWithPreviousWall == 1) // correct { wallInfo.backProjectionEnd = prevIntersection; } else { wallInfo.frontProjectionStart = prevIntersection; } hasConnection = true; } List currentWallSegmentPlan2d = currentWallSegmentsPlan2d[indexForCurrentWallSegmentWithPreviousWall]; List previousWallSegment2d = previousWallSegmentsPlan2d[indexSegmentFromPreviousWall]; if (FindIntersectionWithWall(currentWallSegmentPlan2d, currentInfo: wallInfo, previousWallSegment2d, previousWallInfo, out Vector3 prevIntersectionPlan2D)) { if (indexForCurrentWallSegmentWithPreviousWall == 1) // correct { wallInfo.backProjectionEndPlan2d = prevIntersectionPlan2D; } else { wallInfo.frontProjectionStartPlan2d = prevIntersectionPlan2D; } hasConnection = true; } currentWallSegment = currentWallSegments[indexForCurrentWallSegmentWithNextWall]; List nextWallSegment = nextWallSegments[indexSegmentFromNextWall]; if (FindIntersectionWithWall(currentWallSegment, wallInfo, nextWallSegment, nextWallInfo, out Vector3 nextIntersection)) { if (indexForCurrentWallSegmentWithNextWall == 0) { wallInfo.frontProjectionEnd = nextIntersection; } else { wallInfo.backProjectionStart = nextIntersection; } hasConnection = true; } currentWallSegmentPlan2d = currentWallSegmentsPlan2d[indexForCurrentWallSegmentWithNextWall]; List nextWallSegmentPlan2d = nextWallSegmentsPlan2d[indexSegmentFromNextWall]; if (FindIntersectionWithWall(currentWallSegmentPlan2d, wallInfo, nextWallSegmentPlan2d, nextWallInfo, out Vector3 nextIntersectionPlan2d)) { if (indexForCurrentWallSegmentWithNextWall == 0) { wallInfo.frontProjectionEndPlan2d = nextIntersectionPlan2d; } else { wallInfo.backProjectionStartPlan2d = nextIntersectionPlan2d; } hasConnection = true; } return hasConnection; } private static bool FindIntersectionWithWall(List currentWallSegment, WallLineInfo currentInfo, List otherWallSegment, WallLineInfo otherWallInfo, out Vector3 intersection) { Vector3 start = currentWallSegment[0]; Vector3 end = currentWallSegment[1]; Vector3 currentMiddlePoint = Vector3.Lerp(start, end, 0.5f); // Get wall endpoints Vector3 wallStart = otherWallSegment[0]; Vector3 wallEnd = otherWallSegment[1]; Vector3 otherMiddlePoint = Vector3.Lerp(wallStart, wallEnd, 0.5f); // Check for line-line intersection (use your existing logic) // return FindInfiniteLineIntersection(start, end, wallStart, wallEnd, out intersection); // Check for line-line intersection (use your existing logic) return FindInfiniteLineIntersection(currentMiddlePoint, currentInfo.mainWallDirection, otherMiddlePoint, -otherWallInfo.mainWallDirection, out intersection); } public static bool FindInfiniteLineIntersection( Vector3 line1Start, Vector3 line1Dir, Vector3 line2Start, Vector3 line2Dir, out Vector3 intersection) { // Default intersection to zero intersection = Vector3.zero; // Calculate the cross product of the two direction vectors Vector3 crossDir = Vector3.Cross(line1Dir, line2Dir); float crossDirMagnitude = crossDir.magnitude; // If the magnitude is close to zero, lines are parallel and don't intersect if (Mathf.Approximately(crossDirMagnitude, 0)) { return false; } // Calculate the difference between the starting points Vector3 diff = line2Start - line1Start; // Calculate the t parameter for line1 float t = Vector3.Dot(Vector3.Cross(diff, line2Dir), crossDir) / (crossDirMagnitude * crossDirMagnitude); // Find the intersection point intersection = line1Start + t * line1Dir; return true; } private static List> FindNextAndPreviousWalls(WallPoint point, Vector3 referenceDirection, WallLine currentWall, out WallLine prevWall, out WallLine nextWall) { var walls = point.GetWalls(); nextWall = null; prevWall = null; // Create a list to store walls and their angles List> wallAngles = new List>(); // Calculate angle of each wall foreach (WallLine wall in walls) { if (wall == null) continue; var otherWallInfo = new WallLineInfo(wall); Vector3 endPoint = otherWallInfo.endPosition; Vector3 startPoint = otherWallInfo.startPosition; if (wall._pointEnd != point) { endPoint = otherWallInfo.startPosition; startPoint = otherWallInfo.endPosition; } Vector3 mainWallDirection = (endPoint - startPoint).normalized; Vector3 wallDirection = mainWallDirection; // Assuming Start -> End defines direction float angle = GetAngle(referenceDirection, wallDirection); wallAngles.Add(new Tuple(wall, angle)); } // Sort walls by angle wallAngles.Sort((a, b) => a.Item2.CompareTo(b.Item2)); // Find the index of the current wall in the sorted list int currentIndex = wallAngles.FindIndex(t => t.Item1 == currentWall); if (currentIndex == -1) return null; // Determine next and previous walls (with wrap-around) prevWall = wallAngles[(currentIndex + 1) % wallAngles.Count].Item1; nextWall = wallAngles[(currentIndex - 1 + wallAngles.Count) % wallAngles.Count].Item1; List> connectionIndexes = new List> { new(){currentWall._pointStart == point ? 0 : 1, prevWall._pointStart == point ? 1 : 0 }, // previous connexion new(){currentWall._pointStart == point ? 1 : 0, nextWall._pointStart == point ? 0 : 1 } // next connexion }; return connectionIndexes; } // Function to calculate angle between two vectors private static float GetAngle(Vector3 reference, Vector3 direction) { // Project the vectors onto the XZ plane to ignore Y axis reference.y = 0; direction.y = 0; // Normalize both vectors reference.Normalize(); direction.Normalize(); // Use atan2 to calculate angle in radians float angle = Mathf.Atan2(Vector3.Cross(reference, direction).y, Vector3.Dot(reference, direction)); // Convert angle to degrees (0-360) return (angle < 0) ? angle * Mathf.Rad2Deg + 360 : angle * Mathf.Rad2Deg; } public void SetListInput() { throw new System.NotImplementedException(); } internal void Copy(WallLineData pointsData) { //this._pointStart = pointsData.pointPair[0]; //this._pointEnd = pointsData.pointPair[1]; } public WallLineData GetCopy() { WallLineData copy = new WallLineData(this); return copy; } public string SaveToJson() { return JsonConvert.SerializeObject(GetCopy()); } public bool IsInteriorWall() { return _category == "Interior"; } } [Serializable] public class WallLineData { public Vector2Int pointPair; public string category; public float thickness; public List matList = new List(); public List interiorWallFacesNumber = new List(); public WallLineData(WallLine line) { pointPair = new(line._pointStart.transform.GetSiblingIndex(), line._pointEnd.transform.GetSiblingIndex()); category = line._category; thickness = line.Thickness; interiorWallFacesNumber = line.interiorWallFacesNumber; foreach (Material mat in line.mat) { matList.Add(mat); } } public WallLineData(Vector2Int pair, string _category, float _thickness, Material mat, List numberOfWall) { pointPair = pair; category = _category; thickness = _thickness; interiorWallFacesNumber = numberOfWall; if (category == "Interior") { for (int i = 0; i < interiorWallFacesNumber.Count; i++) { matList.Add(mat); } } else { matList.Add(mat); } } } public class WallLineInfo { public WallLine wallLine; public float wallThickness; public Vector3 mainWallDirection; public Vector3 directionFrontWall; public Vector3 frontProjectionStart; public Vector3 backProjectionStart; public Vector3 frontProjectionEnd; public Vector3 backProjectionEnd; public Vector3 frontProjectionStartPlan2d; public Vector3 backProjectionStartPlan2d; public Vector3 frontProjectionEndPlan2d; public Vector3 backProjectionEndPlan2d; public Vector3 startPosition; public Vector3 endPosition; public WallLine wallLinePreviousStart; public List wallLinePreviousSegmentConnectionStart; public WallLine wallLineNextStart; public List wallLineNextSegmentConnectionStart; public WallLine wallLinePreviousEnd; public List wallLinePreviousSegmentConnectionEnd; public WallLine wallLineNextEnd; public List wallLineNextSegmentConnectionEnd; public WallLineInfo(WallLine wallLine) { this.wallLine = wallLine; WallPoint startPoint = wallLine._pointStart; WallPoint endPoint = wallLine._pointEnd; Vector3 startPosition = startPoint.GetWorldPositionFlat(); Vector3 endPosition = endPoint.GetWorldPositionFlat(); Vector3 mainWallDirection = (endPosition - startPosition).normalized; // Calculate direction offsets Vector3 DirectionFrontWall = Vector3.Cross(Vector3.up, mainWallDirection).normalized; //Debug.Log($"mainWallDirection {mainWallDirection} and DirectionFrontWall {DirectionFrontWall}"); float thicknessFront = wallLine.GetWallThickness() * 0.5f; float thicknessBack = wallLine.GetWallThickness(ignoreCategory: true) * (wallLine.IsExterior() ? 1f : 0.5f); // Calculate projections for the current wall Vector3 FrontProjectionStart = startPosition + thicknessFront * DirectionFrontWall; Vector3 BackProjectionStart = startPosition - thicknessFront * DirectionFrontWall; Vector3 FrontProjectionEnd = endPosition + thicknessFront * DirectionFrontWall; Vector3 BackProjectionEnd = endPosition - thicknessFront * DirectionFrontWall; Vector3 FrontProjectionStartPlan2D = startPosition + thicknessFront * DirectionFrontWall; Vector3 BackProjectionStartPlan2D = startPosition - thicknessBack * DirectionFrontWall; Vector3 FrontProjectionEndPlan2D = endPosition + thicknessFront * DirectionFrontWall; Vector3 BackProjectionEndPlan2D = endPosition - thicknessBack * DirectionFrontWall; this.wallThickness = thicknessFront; this.mainWallDirection = mainWallDirection; this.directionFrontWall = DirectionFrontWall; this.frontProjectionStart = FrontProjectionStart; this.backProjectionStart = BackProjectionStart; this.frontProjectionEnd = FrontProjectionEnd; this.backProjectionEnd = BackProjectionEnd; this.frontProjectionStartPlan2d = FrontProjectionStartPlan2D; this.backProjectionStartPlan2d = BackProjectionStartPlan2D; this.frontProjectionEndPlan2d = FrontProjectionEndPlan2D; this.backProjectionEndPlan2d = BackProjectionEndPlan2D; this.startPosition = startPosition; this.endPosition = endPosition; wallLinePreviousStart = null; wallLinePreviousSegmentConnectionStart = null; wallLineNextStart = null; wallLineNextSegmentConnectionStart = null; wallLinePreviousEnd = null; wallLinePreviousSegmentConnectionEnd = null; wallLineNextEnd = null; wallLineNextSegmentConnectionEnd = null; } public void SetConnection(WallPoint wallPoint, WallLine wallLinePrevious, List wallLinePreviousSegmentConnection, WallLine wallLineNext, List wallLineNextSegmentConnection) { if (wallPoint == wallLine._pointStart) { this.wallLinePreviousStart = wallLinePrevious; this.wallLinePreviousSegmentConnectionStart = wallLinePreviousSegmentConnection; this.wallLineNextStart = wallLineNext; this.wallLineNextSegmentConnectionStart = wallLineNextSegmentConnection; } else { this.wallLinePreviousEnd = wallLinePrevious; this.wallLinePreviousSegmentConnectionEnd = wallLinePreviousSegmentConnection; this.wallLineNextEnd = wallLineNext; this.wallLineNextSegmentConnectionEnd = wallLineNextSegmentConnection; } } public List> CreateWallSegments() { return new List> { new List { this.frontProjectionStart, this.frontProjectionEnd }, new List { this.backProjectionStart, this.backProjectionEnd } }; } public List> CreateWallSegmentsPlan2d() { return new List> { new List { this.frontProjectionStartPlan2d, this.frontProjectionEndPlan2d }, new List { this.backProjectionStartPlan2d, this.backProjectionEndPlan2d } }; } public void ApplyOffset(Vector3 centerOffsetWalls) { Vector3 flatOffset = centerOffsetWalls; frontProjectionStart += flatOffset; backProjectionStart += flatOffset; frontProjectionEnd += flatOffset; backProjectionEnd += flatOffset; frontProjectionStartPlan2d += flatOffset; backProjectionStartPlan2d += flatOffset; frontProjectionEndPlan2d += flatOffset; backProjectionEndPlan2d += flatOffset; startPosition += flatOffset; endPosition += flatOffset; } public void ApplyOffset(Vector2 centerOffsetWalls) { Vector3 flatOffset = new Vector3(centerOffsetWalls.x, 0f, centerOffsetWalls.y); ApplyOffset(flatOffset); } }