private void DrawLine(SceneNode sncLine, UserInterfaceInput uiInput) { if (uiInput.IsVisible) { if (uiInput.CircleCanvasPos.Equals(uiInput.CircleCanvasPosCache)) { return; } List <float3> linePoints; if (uiInput.CircleCanvasPos.x <= _canvasWidth / 2) { //LEFT linePoints = new List <float3> { new float3(uiInput.AnnotationCanvasPos.x + UserInterfaceHelper.AnnotationDim.x, uiInput.AnnotationCanvasPos.y + UserInterfaceHelper.AnnotationDim.y / 2, 0), new float3(uiInput.CircleCanvasPos.x - (uiInput.Size.x / 2), uiInput.CircleCanvasPos.y, 0) }; } else { //RIGHT float posX = _canvasWidth - UserInterfaceHelper.AnnotationDim.x - UserInterfaceHelper.AnnotationDistToLeftOrRightEdge; linePoints = new List <float3> { new float3(posX, uiInput.AnnotationCanvasPos.y + UserInterfaceHelper.AnnotationDim.y / 2, 0), new float3(uiInput.CircleCanvasPos.x + (uiInput.Size.x / 2), uiInput.CircleCanvasPos.y, 0) }; } sncLine.GetComponent <RectTransform>().Offsets = GuiElementPosition.CalcOffsets(AnchorPos.Middle, new float2(0, 0), _canvasHeight, _canvasWidth, new float2(_canvasWidth, _canvasHeight)); Line mesh = sncLine.GetComponent <Line>(); if (mesh != null) { Line newLine = new(linePoints, 0.0025f / _resizeScaleFactor.y, _canvasWidth, _canvasHeight); mesh.Vertices = newLine.Vertices; mesh.Normals = newLine.Normals; mesh.Triangles = newLine.Triangles; mesh.UVs = newLine.UVs; } else { Line newLine = new(linePoints, 0.0025f / _resizeScaleFactor.y, _canvasWidth, _canvasHeight); sncLine.AddComponent(newLine); } } else { Line newLine = sncLine.GetComponent <Line>(); if (newLine != null) { sncLine.Components.Remove(newLine); } } }
private void UpdateAnnotationOffsets(SceneNode sncAnnotation, UserInterfaceInput input) { if (input.CircleCanvasPos.x <= _canvasWidth / 2) { //LEFT sncAnnotation.GetComponent <RectTransform>().Anchors = GuiElementPosition.GetAnchors(AnchorPos.DownDownLeft); sncAnnotation.GetComponent <RectTransform>().Offsets = GuiElementPosition.CalcOffsets( AnchorPos.DownDownLeft, input.AnnotationCanvasPos, UserInterfaceHelper.CanvasHeightInit, UserInterfaceHelper.CanvasWidthInit, UserInterfaceHelper.AnnotationDim); } else { //RIGHT sncAnnotation.GetComponent <RectTransform>().Anchors = GuiElementPosition.GetAnchors(AnchorPos.DownDownRight); sncAnnotation.GetComponent <RectTransform>().Offsets = GuiElementPosition.CalcOffsets( AnchorPos.DownDownRight, input.AnnotationCanvasPos, UserInterfaceHelper.CanvasHeightInit, UserInterfaceHelper.CanvasWidthInit, UserInterfaceHelper.AnnotationDim); } }
private void CalculateNonIntersectingAnnotationPositions(ref UserInterfaceInput input, ref Dictionary <int, float2> intersectedAnnotations, ref int iterations) { if (!input.IsVisible) { return; } int intersectionCount = 0; for (int i = 0; i < _uiInput.Count; i++) { UserInterfaceInput counterpart = _uiInput[i]; if (counterpart.Identifier == input.Identifier || !counterpart.IsVisible || intersectedAnnotations.ContainsKey(counterpart.Identifier)) { continue; } float halfAnnotationHeight = (UserInterfaceHelper.AnnotationDim.y / 2f); float buffer = halfAnnotationHeight - (halfAnnotationHeight / 100f * 10f); //If we do not multiply by the resize scale factor the intersction test will return wrong results because AnnotationCanvasPos is in the range of the size of the initial canvas. bool intersect = UserInterfaceHelper.DoesAnnotationIntersectWithAnnotation(input.AnnotationCanvasPos, _uiInput[i].AnnotationCanvasPos, new float2(0, buffer)); if (!intersect || intersectedAnnotations.ContainsKey(counterpart.Identifier)) { continue; } intersectedAnnotations.Add(counterpart.Identifier, _uiInput[i].AnnotationCanvasPos); intersectionCount++; } if (intersectionCount == 0) { return; } if (intersectedAnnotations.Count >= 1) { if (!intersectedAnnotations.ContainsKey(input.Identifier)) { intersectedAnnotations.Add(input.Identifier, input.AnnotationCanvasPos); //add pos that is just being checked } List <KeyValuePair <int, float2> > orderedBy = intersectedAnnotations.OrderBy(item => item.Value.y).ToList(); intersectedAnnotations = new Dictionary <int, float2>(); foreach (KeyValuePair <int, float2> keyValue in orderedBy) //JSIL not implemented exception: ToDictionary { intersectedAnnotations.Add(keyValue.Key, keyValue.Value); } int middleIndex = (intersectedAnnotations.Count) / 2; float2 averagePos = new(); for (int i = 0; i < intersectedAnnotations.Count; i++) { averagePos += intersectedAnnotations.ElementAt(i).Value; } averagePos /= intersectedAnnotations.Count; for (int i = 0; i < intersectedAnnotations.Count; i++) { int identifier = intersectedAnnotations.ElementAt(i).Key; UserInterfaceInput thisInput = _uiInput[identifier]; thisInput.AnnotationCanvasPos = averagePos; int multiplier = System.Math.Abs(i - middleIndex); //Distance between annotations is 0.5* AnnotationDim.y if (intersectedAnnotations.Count % 2 == 0) //even { if (i == middleIndex - 1) { thisInput.AnnotationCanvasPos.y -= 0.75f * UserInterfaceHelper.AnnotationDim.y; } else if (i == middleIndex) { thisInput.AnnotationCanvasPos.y += 0.75f * UserInterfaceHelper.AnnotationDim.y; } else if (i > middleIndex) { thisInput.AnnotationCanvasPos.y += (0.75f * UserInterfaceHelper.AnnotationDim.y) + (multiplier * (UserInterfaceHelper.AnnotationDim.y + UserInterfaceHelper.AnnotationDim.y / 2)); } else if (i < middleIndex) { thisInput.AnnotationCanvasPos.y -= (0.75f * UserInterfaceHelper.AnnotationDim.y) + ((multiplier - 1) * (UserInterfaceHelper.AnnotationDim.y + UserInterfaceHelper.AnnotationDim.y / 2)); } } else //odd { if (i > middleIndex) { thisInput.AnnotationCanvasPos.y += 0.5f * multiplier * UserInterfaceHelper.AnnotationDim.y + (UserInterfaceHelper.AnnotationDim.y * multiplier); } else if (i < middleIndex) { thisInput.AnnotationCanvasPos.y -= 0.5f * multiplier * UserInterfaceHelper.AnnotationDim.y + (UserInterfaceHelper.AnnotationDim.y * multiplier); } } _uiInput[identifier] = thisInput; } } //Recursively check all annotations that where involved in this intersection for (int i = 0; i < intersectedAnnotations.Count; i++) { if (i != 0 && i != intersectedAnnotations.Count - 1) { continue; } iterations++; int identifier = intersectedAnnotations.ElementAt(i).Key; UserInterfaceInput uiInput = _uiInput[identifier]; CalculateNonIntersectingAnnotationPositions(ref uiInput, ref intersectedAnnotations, ref iterations); } }
// RenderAFrame is called once a frame public override void RenderAFrame() { // Clear the backbuffer RC.Clear(ClearFlags.Color | ClearFlags.Depth); RC.Viewport(0, 0, Width, Height); #region Controls // Mouse and keyboard movement if (Input.Keyboard.LeftRightAxis != 0 || Input.Keyboard.UpDownAxis != 0) { _keys = true; } if (Input.Mouse.LeftButton) { _keys = false; _angleVelHorz = -RotationSpeed * Input.Mouse.XVel * Time.DeltaTime * 0.0005f; _angleVelVert = -RotationSpeed * Input.Mouse.YVel * Time.DeltaTime * 0.0005f; } else if (Input.Touch.GetTouchActive(TouchPoints.Touchpoint_0)) { _keys = false; float2 touchVel = Input.Touch.GetVelocity(TouchPoints.Touchpoint_0); _angleVelHorz = -RotationSpeed * touchVel.x * Time.DeltaTime * 0.0005f; _angleVelVert = -RotationSpeed * touchVel.y * Time.DeltaTime * 0.0005f; } else { if (_keys) { _angleVelHorz = -RotationSpeed * Input.Keyboard.LeftRightAxis * Time.DeltaTime; _angleVelVert = -RotationSpeed * Input.Keyboard.UpDownAxis * Time.DeltaTime; } else { float curDamp = (float)System.Math.Exp(-Damping * Time.DeltaTime); _angleVelHorz *= curDamp; _angleVelVert *= curDamp; } } _angleHorz += _angleVelHorz; _angleVert += _angleVelVert; // Create the camera matrix and set it as the current ModelView transformation float4x4 mtxRot = float4x4.CreateRotationX(_angleVert) * float4x4.CreateRotationY(_angleHorz); float4x4 mtxCam = float4x4.LookAt(0, 0, -5, 0, 0, 0, 0, 1, 0); float4x4 view = mtxCam * mtxRot; float4x4 perspective = float4x4.CreatePerspectiveFieldOfView(_fovy, (float)Width / Height, ZNear, ZFar); float4x4 orthographic = float4x4.CreateOrthographic(Width, Height, ZNear, ZFar); #endregion Controls //Annotations will be updated according to circle positions. //Lines will be updated according to circle and annotation positions. RC.View = view; RC.Projection = perspective; SceneNode canvas = _gui.Children[0]; foreach (SceneNode child in canvas.Children) { if (!child.Name.Contains("MarkModelContainer")) { continue; } //1. Calculate the circles canvas position. for (int k = 0; k < child.Children.Count; k++) { SceneNode container = child.Children[k]; SceneNode circle = container.Children[0]; UserInterfaceInput uiInput = _uiInput[k]; //the monkey's matrices SceneNode monkey = _scene.Children[0]; float4x4 model = monkey.GetGlobalTransformation(); float4x4 projection = perspective; float4x4 mvpMonkey = projection * view * model; float3 clipPos = float4x4.TransformPerspective(mvpMonkey, uiInput.Position); //divides by 2 float2 canvasPosCircle = new float2(clipPos.x, clipPos.y) * 0.5f + 0.5f; canvasPosCircle.x *= _canvasWidth; canvasPosCircle.y *= _canvasHeight; uiInput.CircleCanvasPos = canvasPosCircle; var pos = new float2(uiInput.CircleCanvasPos.x - (uiInput.Size.x / 2), uiInput.CircleCanvasPos.y - (uiInput.Size.y / 2)); //we want the lower left point of the rect that encloses the circle.GetComponent <RectTransform>().Offsets = GuiElementPosition.CalcOffsets(AnchorPos.Middle, pos, _canvasHeight, _canvasWidth, uiInput.Size); //1.1 Check if circle is visible PickResult newPick = _scenePicker.Pick(RC, new float2(clipPos.x, clipPos.y)).ToList().OrderBy(pr => pr.ClipPos.z).FirstOrDefault(); if (newPick != null && uiInput.AffectedTriangles[0] == newPick.Triangle) //VISIBLE { uiInput.IsVisible = true; var effect = circle.GetComponent <SurfaceEffect>(); effect.SetDiffuseAlphaInShaderEffect(UserInterfaceHelper.alphaVis); } else { uiInput.IsVisible = false; var effect = circle.GetComponent <SurfaceEffect>(); effect.SetDiffuseAlphaInShaderEffect(UserInterfaceHelper.alphaInv); } //1.2 Calculate annotation positions without intersections. if (!uiInput.CircleCanvasPos.Equals(uiInput.CircleCanvasPosCache)) { float yPosScale = uiInput.CircleCanvasPos.y / _canvasHeight; yPosScale = (yPosScale - 0.5f) * 2f; uiInput.AnnotationCanvasPos.y = uiInput.CircleCanvasPos.y - (UserInterfaceHelper.AnnotationDim.y / 2) + (2 * UserInterfaceHelper.AnnotationDim.y * yPosScale); if (uiInput.CircleCanvasPos.x > _canvasWidth / 2) //RIGHT { uiInput.AnnotationCanvasPos.x = UserInterfaceHelper.CanvasWidthInit - UserInterfaceHelper.AnnotationDim.x - UserInterfaceHelper.AnnotationDistToLeftOrRightEdge; } else { uiInput.AnnotationCanvasPos.x = UserInterfaceHelper.AnnotationDistToLeftOrRightEdge; } } _uiInput[k] = uiInput; } // 2. Find intersecting annotations and correct their position in _uiInput. // Disable rendering of annotation if its corresponding circle is not visible. for (int k = 0; k < child.Children.Count; k++) { SceneNode container = child.Children[k]; SceneNode annotation = container.Children[1]; UserInterfaceInput uiInput = _uiInput[k]; if (uiInput.IsVisible) { if (!uiInput.CircleCanvasPos.Equals(uiInput.CircleCanvasPosCache)) { Dictionary <int, float2> intersectedAnnotations = new(); int iterations = 0; CalculateNonIntersectingAnnotationPositions(ref uiInput, ref intersectedAnnotations, ref iterations); } annotation.GetComponent <NineSlicePlane>().Active = true; foreach (Mesh comp in annotation.GetComponentsInChildren <Mesh>()) { comp.Active = true; } } else { annotation.GetComponent <NineSlicePlane>().Active = false; foreach (Mesh comp in annotation.GetComponentsInChildren <Mesh>()) { comp.Active = false; } } } // 3. Update annotation positions on canvas and draw line for (int k = 0; k < child.Children.Count; k++) { SceneNode container = child.Children[k]; SceneNode line = container.Children[2]; UserInterfaceInput uiInput = _uiInput[k]; if (uiInput.IsVisible) { if (!uiInput.CircleCanvasPos.Equals(uiInput.CircleCanvasPosCache)) { UpdateAnnotationOffsets(child.Children[uiInput.Identifier].Children[1], uiInput); DrawLine(child.Children[uiInput.Identifier].Children[2], uiInput); } } DrawLine(line, uiInput); uiInput.CircleCanvasPosCache = uiInput.CircleCanvasPos; _uiInput[k] = uiInput; } } _sceneRenderer.Render(RC); RC.Projection = _canvasRenderMode == CanvasRenderMode.Screen ? orthographic : perspective; // Constantly check for interactive objects. if (!Input.Mouse.Desc.Contains("Android")) { _sih.CheckForInteractiveObjects(RC, Input.Mouse.Position, Width, Height); } if (Input.Touch.GetTouchActive(TouchPoints.Touchpoint_0) && !Input.Touch.TwoPoint) { _sih.CheckForInteractiveObjects(RC, Input.Touch.GetPosition(TouchPoints.Touchpoint_0), Width, Height); } _guiRenderer.Render(RC); Present(); }