protected virtual void UpdateRenderPoints() { ComputeControlPoints(); // This should have been updated before : make sure anyway. if (m_RenderPointsDirty == false && m_ControlPoints != null) { return; } m_RenderPointsDirty = false; m_MeshDirty = true; m_RenderPoints.Clear(); float diameter = k_EdgeTurnDiameter; Vector2 p1 = parent.ChangeCoordinatesTo(this, m_ControlPoints[0]); Vector2 p2 = parent.ChangeCoordinatesTo(this, m_ControlPoints[1]); Vector2 p3 = parent.ChangeCoordinatesTo(this, m_ControlPoints[2]); Vector2 p4 = parent.ChangeCoordinatesTo(this, m_ControlPoints[3]); // We have to handle a special case of the edge when it is a straight line, but not // when going backwards in space (where the start point is in front in y to the end point). // We do this by turning the line into 3 linear segments with no curves. This also // avoids possible NANs in later angle calculations. if ((orientation == Orientation.Horizontal && Mathf.Abs(p1.y - p4.y) < 2 && p1.x + k_EdgeLengthFromPort < p4.x - k_EdgeLengthFromPort) || (orientation == Orientation.Vertical && Mathf.Abs(p1.x - p4.x) < 2 && p1.y + k_EdgeLengthFromPort < p4.y - k_EdgeLengthFromPort)) { RenderStraightLines(p1, p2, p3, p4); return; } EdgeCornerSweepValues corner1 = GetCornerSweepValues(p1, p2, p3, diameter, Direction.Output); EdgeCornerSweepValues corner2 = GetCornerSweepValues(p2, p3, p4, diameter, Direction.Input); if (!ValidateCornerSweepValues(ref corner1, ref corner2)) { RenderStraightLines(p1, p2, p3, p4); return; } m_RenderPoints.Add(p1); GetRoundedCornerPoints(m_RenderPoints, corner1, Direction.Output); GetRoundedCornerPoints(m_RenderPoints, corner2, Direction.Input); m_RenderPoints.Add(p4); }
private void GetRoundedCornerPoints(List <Vector2> points, EdgeCornerSweepValues corner, Direction closestPortDirection) { // Calculate the number of points that will sample the arc from the sweep angle. int pointsCount = Mathf.CeilToInt((float)Math.Abs(corner.sweepAngle * k_EdgeSweepResampleRatio)); int sign = Math.Sign(corner.sweepAngle); bool backwards = (closestPortDirection == Direction.Input); for (int i = 0; i < pointsCount; ++i) { // If we are computing the second corner (into the input port), the sweep is going backwards // but we still need to add the points to the list in the correct order. float sweepIndex = backwards ? i - pointsCount : i; double sweepedAngle = corner.startAngle + sign * sweepIndex / k_EdgeSweepResampleRatio; var pointX = (float)(corner.circleCenter.x + Math.Cos(sweepedAngle) * corner.radius); var pointY = (float)(corner.circleCenter.y + Math.Sin(sweepedAngle) * corner.radius); // Check if we overlap the previous point. If we do, we skip this point so that we // don't cause the edge polygons to twist. if (i == 0 && backwards) { if (outputOrientation == Orientation.Horizontal) { if (corner.sweepAngle < 0 && points[points.Count - 1].y > pointY) { continue; } else if (corner.sweepAngle >= 0 && points[points.Count - 1].y < pointY) { continue; } } else { if (corner.sweepAngle < 0 && points[points.Count - 1].x < pointX) { continue; } else if (corner.sweepAngle >= 0 && points[points.Count - 1].x > pointX) { continue; } } } points.Add(new Vector2(pointX, pointY)); } }
private bool ValidateCornerSweepValues(ref EdgeCornerSweepValues corner1, ref EdgeCornerSweepValues corner2) { // Get the midpoint between the two corner circle centers. Vector2 circlesMidpoint = (corner1.circleCenter + corner2.circleCenter) / 2; // Find the angle to the corner circles midpoint so we can compare it to the sweep angles of each corner. Vector2 p2CenterToCross1 = corner1.circleCenter - corner1.crossPoint1; Vector2 p2CenterToCirclesMid = corner1.circleCenter - circlesMidpoint; double angleToCirclesMid = outputOrientation == Orientation.Horizontal ? Math.Atan2(p2CenterToCross1.y, p2CenterToCross1.x) - Math.Atan2(p2CenterToCirclesMid.y, p2CenterToCirclesMid.x) : Math.Atan2(p2CenterToCross1.x, p2CenterToCross1.y) - Math.Atan2(p2CenterToCirclesMid.x, p2CenterToCirclesMid.y); if (double.IsNaN(angleToCirclesMid)) { return(false); } // We need the angle to the circles midpoint to match the turn direction of the first corner's sweep angle. angleToCirclesMid = Math.Sign(angleToCirclesMid) * 2 * Mathf.PI - angleToCirclesMid; if (Mathf.Abs((float)angleToCirclesMid) > 1.5 * Mathf.PI) { angleToCirclesMid = -1 * Math.Sign(angleToCirclesMid) * 2 * Mathf.PI + angleToCirclesMid; } // Calculate the maximum sweep angle so that both corner sweeps and with the tangents of the 2 circles meeting each other. float h = p2CenterToCirclesMid.magnitude; float p2AngleToMidTangent = Mathf.Acos(corner1.radius / h); if (double.IsNaN(p2AngleToMidTangent)) { return(false); } float maxSweepAngle = Mathf.Abs((float)corner1.sweepAngle) - p2AngleToMidTangent * 2; // If the angle to the circles midpoint is within the sweep angle, we need to apply our maximum sweep angle // calculated above, otherwise the maximum sweep angle is irrelevant. if (Mathf.Abs((float)angleToCirclesMid) < Mathf.Abs((float)corner1.sweepAngle)) { corner1.sweepAngle = Math.Sign(corner1.sweepAngle) * Mathf.Min(maxSweepAngle, Mathf.Abs((float)corner1.sweepAngle)); corner2.sweepAngle = Math.Sign(corner2.sweepAngle) * Mathf.Min(maxSweepAngle, Mathf.Abs((float)corner2.sweepAngle)); } return(true); }
private EdgeCornerSweepValues GetCornerSweepValues( Vector2 p1, Vector2 cornerPoint, Vector2 p2, float diameter, Direction closestPortDirection) { EdgeCornerSweepValues corner = new EdgeCornerSweepValues(); // Calculate initial radius. This radius can change depending on the sharpness of the corner. corner.radius = diameter / 2; // Calculate vectors from p1 to cornerPoint. Vector2 d1Corner = (cornerPoint - p1).normalized; Vector2 d1 = d1Corner * diameter; float dx1 = d1.x; float dy1 = d1.y; // Calculate vectors from p2 to cornerPoint. Vector2 d2Corner = (cornerPoint - p2).normalized; Vector2 d2 = d2Corner * diameter; float dx2 = d2.x; float dy2 = d2.y; // Calculate the angle of the corner (divided by 2). float angle = (float)(Math.Atan2(dy1, dx1) - Math.Atan2(dy2, dx2)) / 2; // Calculate the length of the segment between the cornerPoint and where // the corner circle with given radius meets the line. float tan = (float)Math.Abs(Math.Tan(angle)); float segment = corner.radius / tan; // If the segment is larger than the diameter, we need to cap the segment // to the diameter and reduce the radius to match the segment. This is what // makes the corner turn radii get smaller as the edge corners get tighter. if (segment > diameter) { segment = diameter; corner.radius = diameter * tan; } // Calculate both cross points (where the circle touches the p1-cornerPoint line // and the p2-cornerPoint line). corner.crossPoint1 = cornerPoint - (d1Corner * segment); corner.crossPoint2 = cornerPoint - (d2Corner * segment); // Calculation of the coordinates of the circle center. corner.circleCenter = GetCornerCircleCenter(cornerPoint, corner.crossPoint1, corner.crossPoint2, segment, corner.radius); // Calculate the starting and ending angles. corner.startAngle = Math.Atan2(corner.crossPoint1.y - corner.circleCenter.y, corner.crossPoint1.x - corner.circleCenter.x); corner.endAngle = Math.Atan2(corner.crossPoint2.y - corner.circleCenter.y, corner.crossPoint2.x - corner.circleCenter.x); // Get the full sweep angle from the starting and ending angles. corner.sweepAngle = corner.endAngle - corner.startAngle; // If we are computing the second corner (into the input port), we want to start // the sweep going backwards. if (closestPortDirection == Direction.Input) { double endAngle = corner.endAngle; corner.endAngle = corner.startAngle; corner.startAngle = endAngle; } // Validate the sweep angle so it turns into the correct direction. if (corner.sweepAngle > Math.PI) { corner.sweepAngle = -2 * Math.PI + corner.sweepAngle; } else if (corner.sweepAngle < -Math.PI) { corner.sweepAngle = 2 * Math.PI + corner.sweepAngle; } return(corner); }
protected virtual void UpdateRenderPoints() { ComputeControlPoints(); // This should have been updated before : make sure anyway. if (m_RenderPointsDirty == false && m_ControlPoints != null) { return; } Vector2 p1 = parent.ChangeCoordinatesTo(this, m_ControlPoints[0]); Vector2 p2 = parent.ChangeCoordinatesTo(this, m_ControlPoints[1]); Vector2 p3 = parent.ChangeCoordinatesTo(this, m_ControlPoints[2]); Vector2 p4 = parent.ChangeCoordinatesTo(this, m_ControlPoints[3]); // Only compute this when the "local" points have actually changed if (lastLocalControlPoints.Count == 4) { if (Approximately(p1, lastLocalControlPoints[0]) && Approximately(p2, lastLocalControlPoints[1]) && Approximately(p3, lastLocalControlPoints[2]) && Approximately(p4, lastLocalControlPoints[3])) { m_RenderPointsDirty = false; return; } } Profiler.BeginSample("EdgeControl.UpdateRenderPoints"); lastLocalControlPoints.Clear(); lastLocalControlPoints.Add(p1); lastLocalControlPoints.Add(p2); lastLocalControlPoints.Add(p3); lastLocalControlPoints.Add(p4); m_RenderPointsDirty = false; m_RenderPoints.Clear(); float diameter = k_EdgeTurnDiameter; // We have to handle a special case of the edge when it is a straight line, but not // when going backwards in space (where the start point is in front in y to the end point). // We do this by turning the line into 3 linear segments with no curves. This also // avoids possible NANs in later angle calculations. bool sameOrientations = outputOrientation == inputOrientation; if (sameOrientations && ((outputOrientation == Orientation.Horizontal && Mathf.Abs(p1.y - p4.y) < 2 && p1.x + k_EdgeLengthFromPort < p4.x - k_EdgeLengthFromPort) || (outputOrientation == Orientation.Vertical && Mathf.Abs(p1.x - p4.x) < 2 && p1.y + k_EdgeLengthFromPort < p4.y - k_EdgeLengthFromPort))) { RenderStraightLines(p1, p2, p3, p4); Profiler.EndSample(); return; } bool renderBothCorners = true; EdgeCornerSweepValues corner1 = GetCornerSweepValues(p1, p2, p3, diameter, Direction.Output); EdgeCornerSweepValues corner2 = GetCornerSweepValues(p2, p3, p4, diameter, Direction.Input); if (!ValidateCornerSweepValues(ref corner1, ref corner2)) { if (sameOrientations) { RenderStraightLines(p1, p2, p3, p4); Profiler.EndSample(); return; } renderBothCorners = false; //we try to do it with a single corner instead Vector2 px = (outputOrientation == Orientation.Horizontal) ? new Vector2(p4.x, p1.y) : new Vector2(p1.x, p4.y); corner1 = GetCornerSweepValues(p1, px, p4, diameter, Direction.Output); } m_RenderPoints.Add(p1); if (!sameOrientations && renderBothCorners) { //if the 2 corners or endpoints are too close, the corner sweep angle calculations can't handle different orientations float minDistance = 2 * diameter * diameter; if ((p3 - p2).sqrMagnitude < minDistance || (p4 - p1).sqrMagnitude < minDistance) { Vector2 px = (p2 + p3) * 0.5f; corner1 = GetCornerSweepValues(p1, px, p4, diameter, Direction.Output); renderBothCorners = false; } } GetRoundedCornerPoints(m_RenderPoints, corner1, Direction.Output); if (renderBothCorners) { GetRoundedCornerPoints(m_RenderPoints, corner2, Direction.Input); } m_RenderPoints.Add(p4); Profiler.EndSample(); }