using Plan2D; using System; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; class LineDisplay { public delegate void OnButtonHide(Button button); public OnButtonHide _onButtonHide; Plan2DHandler _plan2DHandler; int _lineLayer = -1; Camera _camera; float _smallBuffer = 0.062f; Vector3 _offsetDirection; readonly List _colors; readonly List _offsets; readonly float _initialOffset = 2f; readonly float _crossOffset = 3f; //5f; readonly int _numberOfObjectType = Enum.GetValues(typeof(ObjectTypes)).Length; Bounds _roomBounds; bool _showWallMeasure; bool _showWallCut; Button _button; float _minFontSize = 0f; float _maxFontSize = 0f; float _lineWidthBuffer = 0.5f; ZoomSettings _zoomSettings; ObjectTypes _baseType; Dictionary> _pos; Dictionary> _ListOfMeasureToDisplayPerLines; ObjectTypes[] _objectTypes; public LineDisplay(Camera cameraRef, int layer, Vector3 startingPoint, Vector3 endingPoint, Vector3 directionToDisplay, string measure, Plan2DHandler handler, ObjectTypes type, bool showWallMeasure, bool showWallCut, ZoomSettings zoomSettings, ObjectTypes[] objectTypes) { _plan2DHandler = handler; _zoomSettings = zoomSettings; _minFontSize = _plan2DHandler.MinFontSize; _maxFontSize = _plan2DHandler.FontSize; _lineWidthBuffer = _plan2DHandler.LineWidth; _objectTypes = objectTypes; _camera = cameraRef; _lineLayer = layer; Vector3 wallPosition = Vector3.Lerp(startingPoint, endingPoint, 0.5f); _showWallMeasure = showWallMeasure; _showWallCut = showWallCut; _baseType = type; _offsetDirection = directionToDisplay; _colors = handler.LineColors; _pos = new(_numberOfObjectType); for (int i = 0; i < _numberOfObjectType; i++) { _pos.Add((ObjectTypes)i, new List()); } _pos[_baseType].Add(startingPoint); _pos[_baseType].Add(endingPoint); Vector3 basePos = Plan2DHandler.GetPositionOnLine(wallPosition, _pos[_baseType][0], _pos[_baseType][1]); _ListOfMeasureToDisplayPerLines = new(_numberOfObjectType) { { type, new List() { new MeasureDisplay(basePos, measure) } } }; } void UpdateCameraZoom(Vector3 pos) { if (_zoomSettings == null) return; _zoomSettings.ComparePosition(pos); _zoomSettings.ResizeCamera(); } public void AddButton(Button button) { _button = button; } public void AddMeasure(Vector3 startingPoint, Vector3 endingPoint, ObjectTypes objectType) { // would like to line up the new point in _direction only to wall line before adding them _pos[objectType].Add(Plan2DHandler.GetPositionOnLine(startingPoint, _pos[_baseType][0], _pos[_baseType][1])); _pos[objectType].Add(Plan2DHandler.GetPositionOnLine(endingPoint, _pos[_baseType][0], _pos[_baseType][1])); if (!_ListOfMeasureToDisplayPerLines.ContainsKey(objectType)) { _ListOfMeasureToDisplayPerLines.Add(objectType, new List()); } //_ListOfMeasureToDisplayPerLines[(int)objectType].Add(new MeasureDisplay(GetPositionOnLine(objectPosition, _pos[(int)ObjectType.Wall][0], _pos[(int)ObjectType.Wall][1]), measureToAdd)); } //public void DisplayLines() //{ // _roomBounds = _plan2DHandler.GetRoomBound(); // //Vector3 startingPoint = _pos[(int)_baseType][0] + _initialOffset * _offsetDirection; // //Vector3 endingPoint = _pos[(int)_baseType][1] + _initialOffset * _offsetDirection; // bool isIncremental = _plan2DHandler.IsIncrementalCut; // float incrementalOffset = _plan2DHandler.OffsetForIncrementalCut; // // final pass draw wall line // int pointAmount = 1; // pointAmount += CreateLines(ObjectTypes.FloorObject, isIncremental ? incrementalOffset * pointAmount : _offsets[(int)ObjectTypes.FloorObject]); // pointAmount += CreateLines(ObjectTypes.WallObject, isIncremental ? incrementalOffset * pointAmount : _offsets[(int)ObjectTypes.WallObject]); // pointAmount += CreateLines(ObjectTypes.HoleInWallObject, isIncremental ? incrementalOffset * pointAmount : _offsets[(int)ObjectTypes.HoleInWallObject]); // pointAmount += CreateLines(ObjectTypes.Null, isIncremental ? incrementalOffset * pointAmount : _offsets[(int)ObjectTypes.Null]); // if (_showWallMeasure) // { // pointAmount += CreateLines(ObjectTypes.Wall, isIncremental ? incrementalOffset * pointAmount : _offsets[(int)ObjectTypes.Wall]); // } // pointAmount += CreateLines(ObjectTypes.IslandObject, isIncremental ? incrementalOffset * pointAmount : _offsets[(int)ObjectTypes.IslandObject]); // pointAmount += CreateLines(ObjectTypes.IslandWall, isIncremental ? incrementalOffset * pointAmount : _offsets[(int)ObjectTypes.IslandWall]); // if (pointAmount <= 2) { HideButton(); } //} public void DisplayLines() { _roomBounds = _plan2DHandler.GetRoomBound(); float incrementalOffset = _plan2DHandler.OffsetForIncrementalCut; int pointOffsetForLineAmount = 1; bool hideButton = true; foreach (var objectType in _objectTypes) { if (objectType == ObjectTypes.Wall && !_showWallMeasure) { continue; } float offset = incrementalOffset * pointOffsetForLineAmount; pointOffsetForLineAmount += CreateLines(objectType, offset, ref hideButton, pointOffsetForLineAmount); } if (pointOffsetForLineAmount <= 2 && hideButton) { HideButton(); } } private int CreateLines(ObjectTypes type, float offset, ref bool hideButton, float amount) { if (_ListOfMeasureToDisplayPerLines.ContainsKey(type) && _pos[type].Count > 0) { if (type == ObjectTypes.StructuralElements && !_showWallCut) { hideButton = false; return 0; } bool closestToWall = _plan2DHandler.DisplayMeasureCloseToWall; //sort by distance from p1 Vector3 pStart = _pos[_baseType][0]; List sortedList = _pos[type]; sortedList.Sort((p1, p2) => Vector3.Distance(pStart, p1).CompareTo(Vector3.Distance(pStart, p2))); if (type != _baseType) { sortedList.Insert(0, _pos[_baseType][0]); sortedList.Add(_pos[_baseType][1]); } List finalListForText = new(); CreateFinalListOfPointOnLine(type, sortedList, finalListForText); List finalListForLineRenderer = new(); Vector3 middleOffset = Vector3.zero; Vector3 closestOffset = Vector3.zero; Vector3 farthestOffset = Vector3.zero; if (type != ObjectTypes.Wall && amount > 1) { // with sorted list, need to add cross section for each point middleOffset = offset * _offsetDirection; closestOffset = middleOffset - _crossOffset * _offsetDirection; farthestOffset = middleOffset + _crossOffset * _offsetDirection; } else { // with sorted list, need to add cross section for each point middleOffset = offset * _offsetDirection; closestOffset = _initialOffset * _offsetDirection; farthestOffset = middleOffset + _crossOffset * _offsetDirection; } //middleoffset if (_baseType == ObjectTypes.Wall && !_showWallCut && !closestToWall) { Bounds bounds = new Bounds(_roomBounds.center, _roomBounds.size + new Vector3(2, 0, 2) * offset); farthestOffset = _crossOffset * _offsetDirection; Vector3 fatherPos = Vector3.zero; for (int i = 0; i < finalListForText.Count; i++) { Vector3 pos = GetBoundaryPoint(_offsetDirection, finalListForText[i], bounds); Vector3 closest = GetClosestPoint(pos, _offsetDirection); finalListForLineRenderer.Add(closest + closestOffset); finalListForLineRenderer.Add(pos + farthestOffset); fatherPos = pos + farthestOffset; } finalListForLineRenderer.Add(GetBoundaryPoint(_offsetDirection, finalListForText[0], bounds)); finalListForLineRenderer.Add(GetBoundaryPoint(_offsetDirection, finalListForText[^1], bounds)); if (finalListForLineRenderer.Count > 4 || type == _baseType) { SetPointInLineRenderer(finalListForLineRenderer, type); UpdateCameraZoom(fatherPos); } if (finalListForText.Count > 2 || type == _baseType) { AddTextOnLineForBounds(type, finalListForText, _offsetDirection, bounds); return 1; } } else { Vector3 fatherPos = Vector3.zero; for (int i = 0; i < finalListForText.Count; i++) { finalListForLineRenderer.Add(finalListForText[i] + closestOffset); finalListForLineRenderer.Add(finalListForText[i] + farthestOffset); fatherPos = finalListForText[i] + farthestOffset; } finalListForLineRenderer.Add(finalListForText[0] + middleOffset); finalListForLineRenderer.Add(finalListForText[^1] + middleOffset); if (finalListForLineRenderer.Count > 6 || type == _baseType) { SetPointInLineRenderer(finalListForLineRenderer, type); UpdateCameraZoom(fatherPos); } if (finalListForText.Count > 2 || type == _baseType) { AddTextOnLine(type, middleOffset, finalListForText); return 1; } } } return 0; } private Vector3 GetClosestPoint(Vector3 pos, Vector3 dir) { LineRenderer lineRenderer = _plan2DHandler.GetRoomLineRenderer(); if (lineRenderer == null) { return Vector3.zero; } Vector3 closest = Vector3.zero; Vector3 intersection = Vector3.zero; for (int i = 0; i < lineRenderer.positionCount; i++) { int j = i == 0 ? lineRenderer.positionCount - 1 : i - 1; Vector3 p1 = lineRenderer.GetPosition(i); Vector3 p2 = lineRenderer.GetPosition(j); if (Plan2DHandler.LineLineIntersection(out intersection, p1, p1 - p2, pos, -dir)) { float totalLineDistance = Vector3.Distance(p2, p1); float distP1 = Vector3.Distance(intersection, p1); float distP2 = Vector3.Distance(intersection, p2); bool notOnLine = distP1 + distP2 > totalLineDistance + _smallBuffer; if (notOnLine) { continue; } float distanceClosest = Vector3.Distance(pos, closest); float distanceIntersection = Vector3.Distance(pos, intersection); if (distanceIntersection < distanceClosest && pos.y == intersection.y) { closest = intersection; } } } return closest; } Vector3 GetBoundaryPoint(Vector3 direction, Vector3 origin, Bounds bounds) { Plane[] planes = new Plane[] { new Plane(Vector3.right, bounds.center + Vector3.right * bounds.extents.x), new Plane(Vector3.left, bounds.center + Vector3.left * bounds.extents.x), new Plane(Vector3.up, bounds.center + Vector3.up * bounds.extents.y), new Plane(Vector3.down, bounds.center + Vector3.down * bounds.extents.y), new Plane(Vector3.forward, bounds.center + Vector3.forward * bounds.extents.z), new Plane(Vector3.back, bounds.center + Vector3.back * bounds.extents.z), }; float closestDistance = float.MaxValue; Vector3 closestPoint = Vector3.zero; foreach (Plane plane in planes) { float distance; if (plane.Raycast(new Ray(origin, direction), out distance)) { Vector3 point = origin + direction.normalized * distance; float pointDistance = Vector3.Dot(point - origin, direction); if (pointDistance < closestDistance) { closestDistance = pointDistance; closestPoint = point; } } } return closestPoint; } private void CreateFinalListOfPointOnLine(ObjectTypes index, List sortedList, List finalListForText) { Vector3 p1 = sortedList[0]; finalListForText.Add(p1); Vector3 p0 = sortedList[0]; Vector3 pend = sortedList[^1]; Vector3 deltaTotal = pend - p0; float distTotal = Vector3.Magnitude(deltaTotal); float roundedTotalDistance = Mathf.Round(distTotal * 100f) / 100f; if (index != _baseType) { for (int i = 1; i < sortedList.Count - 1; i++) { Vector3 p2 = sortedList[i]; Vector3 deltaBeg = p2 - p0; Vector3 deltaEnd = p2 - pend; float distBeg = Vector3.Magnitude(deltaBeg); float distEnd = Vector3.Magnitude(deltaEnd); float distP2 = distBeg + distEnd; float roundedDistP2 = Mathf.Round(distP2 * 100f) / 100f; if (roundedDistP2 > roundedTotalDistance) continue; float distPrevious = Vector3.Distance(p1, p2); if (distPrevious < _smallBuffer) continue; if (i == sortedList.Count - 2) { Vector3 pNext = sortedList[^1]; float distNext = Vector3.Distance(pNext, p2); if (distNext < _smallBuffer) continue; } finalListForText.Add(p2); //AddMeasure(index, p1, p2); p1 = p2; } } if (Vector3.Distance(finalListForText[^1], sortedList[^1]) > _smallBuffer) { finalListForText.Add(sortedList[^1]); //AddMeasure(index, p1, sortedList[^1]); } for (int i = 1; i < finalListForText.Count; i++) { AddMeasure(index, finalListForText[i - 1], finalListForText[i]); } } void HideButton() { if (_button != null) { //Debug.Log("HideButton" + _button.name); _onButtonHide?.Invoke(_button); _button.gameObject.SetActive(false); } } private void AddMeasure(ObjectTypes index, Vector3 p1, Vector3 p2) { if (index == _baseType) return; float Distance=Vector3.Distance(p1, p2); string measureToAdd = ""; if(Distance>=1)measureToAdd = DOIT.ConvertNumberToString(Distance); Vector3 middlePosition = Vector3.Lerp(p1, p2, 0.5f); _ListOfMeasureToDisplayPerLines[index].Add(new MeasureDisplay(Plan2DHandler.GetPositionOnLine(middlePosition, _pos[_baseType][0], _pos[_baseType][1]), measureToAdd)); } private void AddTextOnLine(ObjectTypes index, Vector3 offset, List linePos) { for (int i = 0; i < _ListOfMeasureToDisplayPerLines[index].Count; i++) { if(linePos.Count>1){ GameObject textObject = _plan2DHandler.GetTextInstance(); _plan2DHandler.AddTextToList(textObject, index); textObject.transform.SetParent(_plan2DHandler.transform, false); textObject.transform.position = _ListOfMeasureToDisplayPerLines[index][i].Position + offset + 0.2f * -_camera.transform.forward; RotateText(textObject); if (_lineLayer != -1) { textObject.layer = _lineLayer; foreach (Transform t in textObject.transform) { t.gameObject.layer = _lineLayer; } } Vector3 p1 = linePos[i]; Vector3 p2 = linePos[i + 1]; Vector3 delta = p2 - p1; Vector2 deltaTest = new Vector2(Vector3.Dot(_camera.transform.right, delta), Vector3.Dot(_camera.transform.up, delta)); float distance = Vector3.Dot(delta.normalized, delta); AddText(index, i, textObject, distance, deltaTest); } } } private void AddTextOnLineForBounds(ObjectTypes index, List linePos, Vector3 direction, Bounds bounds) { for (int i = 0; i < _ListOfMeasureToDisplayPerLines[index].Count; i++) { GameObject textObject = _plan2DHandler.GetTextInstance(); _plan2DHandler.AddTextToList(textObject, index); textObject.transform.SetParent(_plan2DHandler.transform, false); RotateText(textObject); if (_lineLayer != -1) { textObject.layer = _lineLayer; foreach (Transform t in textObject.transform) { t.gameObject.layer = _lineLayer; } } Vector3 p1 = linePos[i]; Vector3 p2 = linePos[i + 1]; Vector3 delta = (p2 - p1); textObject.transform.position = Vector3.Lerp(GetBoundaryPoint(direction, p1, bounds), GetBoundaryPoint(direction, p2, bounds), 0.5f) + 0.2f * -_camera.transform.forward; ; float distance = Vector3.Dot(delta.normalized, delta); Vector2 deltaTest = new Vector2(Vector3.Dot(_camera.transform.right, delta), Vector3.Dot(_camera.transform.up, delta)); AddText(index, i, textObject, distance, deltaTest); } } private void AddText(ObjectTypes index, int i, GameObject textObject, float distance, Vector2 deltaTest) { distance = MathF.Abs(distance) - _lineWidthBuffer; TextMeshPro text = textObject.GetComponentInChildren(); text.text = _ListOfMeasureToDisplayPerLines[index][i].Measure.ToString(); GameObject quad = textObject.transform.GetChild(1).gameObject; text.fontSize = _plan2DHandler.FontSize; Vector2 originalTextSize = text.GetPreferredValues(); float originalFontSize = text.fontSize; float distanceText = Mathf.Abs(Vector3.Dot(deltaTest.normalized, originalTextSize)); // Check if the text needs to be resized if (distanceText > distance) { // Calculate the recommended font size to fit the distance float size = distance / distanceText; float recommendedSize = size * originalFontSize; // Ensure the font size stays within the min and max limits recommendedSize = Mathf.Clamp(recommendedSize, _minFontSize, _maxFontSize); // Resize the text text.fontSize = recommendedSize; originalTextSize = text.GetPreferredValues(); distanceText = Mathf.Abs(Vector3.Dot(deltaTest.normalized, originalTextSize)); // Check if the recommended size is smaller than the min size if (distanceText > distance && (_offsetDirection == _camera.transform.up || _offsetDirection == -_camera.transform.up)) { // Rotate the text to take up less space RotateTextToward(textObject); } } float bufferx = 0f; float buffery = -0.02f; quad.transform.localScale = new Vector3(originalTextSize.x / 10f + bufferx, 1f, originalTextSize.y / 10f + buffery); text.color = _colors[GetColorIndex(index)]; Material mat = quad.GetComponent().sharedMaterial; mat.color = Color.white; } private void RotateTextToward(GameObject textObject) { // Get the current rotation of the text object Vector3 currentRotation = textObject.transform.localEulerAngles; // Rotate the text object by 90 degrees on the local z axis currentRotation.z += 90f; // Set the new rotation to the text object textObject.transform.localEulerAngles = currentRotation; } private void RotateText(GameObject textObject) { RectTransform t = textObject.GetComponent(); if (t) { t.localRotation = _camera.transform.localRotation; } } private LineRenderer AddLineRenderer(ObjectTypes objectType, Color color) { string name = "LineRenderer " + objectType.ToString(); GameObject child = _plan2DHandler.GetLineRendererInstance(); if (_lineLayer != -1) { child.layer = _lineLayer; } child.name = name; _plan2DHandler.AddLineRendererToList(child, objectType); LineRenderer lineRenderer = child.GetComponent(); lineRenderer.startWidth = _lineWidthBuffer; lineRenderer.endWidth = _lineWidthBuffer; lineRenderer.sortingOrder = -(int)objectType; lineRenderer.startColor = color; lineRenderer.endColor = color; return lineRenderer; } private void SetPointInLineRenderer(List listOfPointsToRender, ObjectTypes index) { for (int i = 1; i < listOfPointsToRender.Count;) { int j = i - 1; LineRenderer lineRenderer = AddLineRenderer(index, _colors[GetColorIndex(index)]); lineRenderer.positionCount = 2; lineRenderer.SetPosition(0, listOfPointsToRender[j]); lineRenderer.SetPosition(1, listOfPointsToRender[i]); i += 2; } } int GetColorIndex(ObjectTypes type) { return (int)type < _colors.Count ? (int)type : 0; } }