void DrawBezierPathSceneEditor() { bool displayControlPoints = data.displayControlPoints && (bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic || !globalDisplaySettings.hideAutoControls); Bounds bounds = bezierPath.PathBounds; // Draw normals if (data.showNormals) { if (!hasUpdatedNormalsVertexPath) { normalsVertexPath = new VertexPath(bezierPath, normalsSpacing); hasUpdatedNormalsVertexPath = true; } if (editingNormalsOld != data.showNormals) { editingNormalsOld = data.showNormals; Repaint(); } Handles.color = globalDisplaySettings.normals; for (int i = 0; i < normalsVertexPath.NumVertices; i++) { Vector3 prevVertex = normalsVertexPath.vertices[Mathf.Max(0, i - 1)]; Vector3 nextVertex = normalsVertexPath.vertices[Mathf.Min(normalsVertexPath.NumVertices - 1, i + 1)]; Vector3 forward = prevVertex - nextVertex; forward *= globalDisplaySettings.normalsWidth; Vector3[] points = new Vector3[4]; points[0] = normalsVertexPath.vertices[i] - forward * 0.5f; points[1] = points[0] + normalsVertexPath.normals[i] * globalDisplaySettings.normalsLength; points[2] = points[1] + forward; points[3] = points[0] + forward; Handles.DrawSolidRectangleWithOutline(points, globalDisplaySettings.normals, globalDisplaySettings.normals); } } for (int i = 0; i < bezierPath.NumSegments; i++) { Vector3[] points = bezierPath.GetPointsInSegment(i); if (data.showPerSegmentBounds) { Bounds segmentBounds = CubicBezierUtility.CalculateBounds(points); Handles.color = globalDisplaySettings.segmentBounds; Handles.DrawWireCube(segmentBounds.center, segmentBounds.size); } // Draw lines between control points if (displayControlPoints) { Handles.color = (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) ? globalDisplaySettings.handleDisabled : globalDisplaySettings.controlLine; Handles.DrawLine(points[1], points[0]); Handles.DrawLine(points[2], points[3]); } // Draw path bool highlightSegment = (i == selectedSegmentIndex && Event.current.shift && draggingHandleIndex == -1 && mouseOverHandleIndex == -1); Color segmentCol = (highlightSegment) ? globalDisplaySettings.highlightedPath : globalDisplaySettings.bezierPath; Handles.DrawBezier(points[0], points[3], points[1], points[2], segmentCol, null, 2); } // Draw rotate/scale/move tool if (data.pathTransformationEnabled && !Event.current.alt && !Event.current.shift) { if (Tools.current == Tool.Rotate) { Undo.RecordObject(creator, "Rotate Path"); Quaternion newHandleRot = Handles.DoRotationHandle(currentHandleRot, bezierPath.Pivot); Quaternion deltaRot = newHandleRot * Quaternion.Inverse(currentHandleRot); currentHandleRot = newHandleRot; Quaternion newRot = deltaRot * bezierPath.Rotation; bezierPath.Rotation = newRot; if (shareTransformsWithPath) { creator.transform.rotation = newRot; rotationOld = newRot; } } else if (Tools.current == Tool.Scale) { Undo.RecordObject(creator, "Scale Path"); bezierPath.Scale = Handles.DoScaleHandle(bezierPath.Scale, bezierPath.Pivot, Quaternion.identity, HandleUtility.GetHandleSize(bezierPath.Pivot)); if (shareTransformsWithPath) { creator.transform.localScale = bezierPath.Scale; scaleOld = bezierPath.Scale; } } else { Undo.RecordObject(creator, "Move Path"); bezierPath.Pivot = bounds.center; Vector3 newCentre = Handles.DoPositionHandle(bezierPath.Pivot, Quaternion.identity); Vector3 deltaCentre = newCentre - bezierPath.Pivot; bezierPath.Position += deltaCentre; if (shareTransformsWithPath) { creator.transform.position = bezierPath.Position; positionOld = bezierPath.Position; } } } if (data.showPathBounds) { Handles.color = globalDisplaySettings.bounds; Handles.DrawWireCube(bounds.center, bounds.size); } if (data.displayAnchorPoints) { for (int i = 0; i < bezierPath.NumPoints; i += 3) { DrawHandle(i); } } if (displayControlPoints) { for (int i = 1; i < bezierPath.NumPoints - 1; i += 3) { DrawHandle(i); DrawHandle(i + 1); } } }
void DrawBezierPathSceneEditor() { bool displayControlPoints = data.displayControlPoints && (bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic || !globalDisplaySettings.hideAutoControls); Bounds bounds = bezierPath.CalculateBoundsWithTransform(creator.transform); if (Event.current.type == EventType.Repaint) { for (int i = 0; i < bezierPath.NumSegments; i++) { Vector3[] points = bezierPath.GetPointsInSegment(i); for (int j = 0; j < points.Length; j++) { points[j] = MathUtility.TransformPoint(points[j], creator.transform, bezierPath.Space); } if (data.showPerSegmentBounds) { Bounds segmentBounds = CubicBezierUtility.CalculateSegmentBounds(points[0], points[1], points[2], points[3]); Handles.color = globalDisplaySettings.segmentBounds; Handles.DrawWireCube(segmentBounds.center, segmentBounds.size); } // Draw lines between control points if (displayControlPoints) { Handles.color = (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) ? globalDisplaySettings.handleDisabled : globalDisplaySettings.controlLine; Handles.DrawLine(points[1], points[0]); Handles.DrawLine(points[2], points[3]); } // Draw path bool highlightSegment = (i == selectedSegmentIndex && Event.current.shift && draggingHandleIndex == -1 && mouseOverHandleIndex == -1); Color segmentCol = (highlightSegment) ? globalDisplaySettings.highlightedPath : globalDisplaySettings.bezierPath; Handles.DrawBezier(points[0], points[3], points[1], points[2], segmentCol, null, 2); } if (data.showPathBounds) { Handles.color = globalDisplaySettings.bounds; Handles.DrawWireCube(bounds.center, bounds.size); } // Draw normals if (data.showNormals) { if (!hasUpdatedNormalsVertexPath) { normalsVertexPath = new VertexPath(bezierPath, creator.transform, normalsSpacing); hasUpdatedNormalsVertexPath = true; } if (editingNormalsOld != data.showNormals) { editingNormalsOld = data.showNormals; Repaint(); } Vector3[] normalLines = new Vector3[normalsVertexPath.NumPoints * 2]; Handles.color = globalDisplaySettings.normals; for (int i = 0; i < normalsVertexPath.NumPoints; i++) { normalLines[i * 2] = normalsVertexPath.GetPoint(i); normalLines[i * 2 + 1] = normalsVertexPath.GetPoint(i) + normalsVertexPath.GetNormal(i) * globalDisplaySettings.normalsLength; } Handles.DrawLines(normalLines); } } if (data.displayAnchorPoints) { for (int i = 0; i < bezierPath.NumPoints; i += 3) { DrawHandle(i); } } if (displayControlPoints) { for (int i = 1; i < bezierPath.NumPoints - 1; i += 3) { DrawHandle(i); DrawHandle(i + 1); } } }
public ScreenSpacePolyLine(BezierPath bezierPath, Transform transform, float maxAngleError, float minVertexDst, float accuracy = 1) { this.transform = transform; transformPosition = transform.position; transformRotation = transform.rotation; transformScale = transform.localScale; // Split path in vertices based on angle error verticesWorld = new List <Vector3>(); vertexToPathSegmentMap = new List <int>(); segmentStartIndices = new int[bezierPath.NumSegments + 1]; verticesWorld.Add(bezierPath[0]); vertexToPathSegmentMap.Add(0); Vector3 prevPointOnPath = bezierPath[0]; float dstSinceLastVertex = 0; Vector3 lastAddedPoint = prevPointOnPath; float dstSinceLastIntermediary = 0; for (int segmentIndex = 0; segmentIndex < bezierPath.NumSegments; segmentIndex++) { Vector3[] segmentPoints = bezierPath.GetPointsInSegment(segmentIndex); verticesWorld.Add(segmentPoints[0]); vertexToPathSegmentMap.Add(segmentIndex); segmentStartIndices[segmentIndex] = verticesWorld.Count - 1; prevPointOnPath = segmentPoints[0]; lastAddedPoint = prevPointOnPath; dstSinceLastVertex = 0; dstSinceLastIntermediary = 0; float estimatedSegmentLength = CubicBezierUtility.EstimateCurveLength(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]); int divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy * accuracyMultiplier); float increment = 1f / divisions; for (float t = increment; t <= 1; t += increment) { Vector3 pointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t); Vector3 nextPointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t + increment); // angle at current point on path float localAngle = 180 - MathUtility.MinAngle(prevPointOnPath, pointOnPath, nextPointOnPath); // angle between the last added vertex, the current point on the path, and the next point on the path float angleFromPrevVertex = 180 - MathUtility.MinAngle(lastAddedPoint, pointOnPath, nextPointOnPath); float angleError = Mathf.Max(localAngle, angleFromPrevVertex); if (angleError > maxAngleError && dstSinceLastVertex >= minVertexDst) { dstSinceLastVertex = 0; dstSinceLastIntermediary = 0; verticesWorld.Add(pointOnPath); vertexToPathSegmentMap.Add(segmentIndex); lastAddedPoint = pointOnPath; } else { if (dstSinceLastIntermediary > intermediaryThreshold) { verticesWorld.Add(pointOnPath); vertexToPathSegmentMap.Add(segmentIndex); dstSinceLastIntermediary = 0; } else { dstSinceLastIntermediary += (pointOnPath - prevPointOnPath).magnitude; } dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude; } prevPointOnPath = pointOnPath; } } segmentStartIndices[bezierPath.NumSegments] = verticesWorld.Count; // ensure final point gets added (unless path is closed loop) if (!bezierPath.IsClosed) { verticesWorld.Add(bezierPath[bezierPath.NumPoints - 1]); } else { verticesWorld.Add(bezierPath[0]); } // Calculate length cumululativeLengthWorld = new float[verticesWorld.Count]; for (int i = 0; i < verticesWorld.Count; i++) { verticesWorld[i] = MathUtility.TransformPoint(verticesWorld[i], transform, bezierPath.Space); if (i > 0) { pathLengthWorld += (verticesWorld[i - 1] - verticesWorld[i]).magnitude; cumululativeLengthWorld[i] = pathLengthWorld; } } }
void DrawBezierPathSceneEditor() { var displayControlPoints = Data.displayControlPoints && (BezierPath.ControlPointMode != BezierPath.ControlMode.Automatic || !_globalDisplaySettings.hideAutoControls); var bounds = BezierPath.PathBounds; // Draw normals if (Data.showNormals) { if (!_hasUpdatedNormalsVertexPath) { _normalsVertexPath = new VertexPath(BezierPath, NormalsSpacing); _hasUpdatedNormalsVertexPath = true; } if (_editingNormalsOld != Data.showNormals) { _editingNormalsOld = Data.showNormals; Repaint(); } Handles.color = _globalDisplaySettings.normals; for (var i = 0; i < _normalsVertexPath.NumVertices; i++) { Handles.DrawLine( _normalsVertexPath.Vertices[i], _normalsVertexPath.Vertices[i] + _normalsVertexPath.Normals[i] * _globalDisplaySettings.normalsLength); } } for (var i = 0; i < BezierPath.NumSegments; i++) { var points = BezierPath.GetPointsInSegment(i); if (Data.showPerSegmentBounds) { var segmentBounds = CubicBezierUtility.CalculateBounds(points); Handles.color = _globalDisplaySettings.segmentBounds; Handles.DrawWireCube(segmentBounds.center, segmentBounds.size); } // Draw lines between control points if (displayControlPoints) { Handles.color = (BezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) ? _globalDisplaySettings.handleDisabled : _globalDisplaySettings.controlLine; Handles.DrawLine(points[1], points[0]); Handles.DrawLine(points[2], points[3]); } // Draw path var highlightSegment = (i == _selectedSegmentIndex && Event.current.shift && _draggingHandleIndex == -1 && _mouseOverHandleIndex == -1); var segmentCol = (highlightSegment) ? _globalDisplaySettings.highlightedPath : _globalDisplaySettings.bezierPath; Handles.DrawBezier(points[0], points[3], points[1], points[2], segmentCol, null, 2); } // Draw rotate/scale/move tool if (Data.pathTransformationEnabled && !Event.current.alt && !Event.current.shift) { if (Tools.current == Tool.Rotate) { Undo.RecordObject(_creator, "Rotate Path"); var newHandleRot = Handles.DoRotationHandle(_currentHandleRot, BezierPath.Pivot); var deltaRot = newHandleRot * Quaternion.Inverse(_currentHandleRot); _currentHandleRot = newHandleRot; var newRot = deltaRot * BezierPath.Rotation; BezierPath.Rotation = newRot; if (_shareTransformsWithPath) { _creator.transform.rotation = newRot; _rotationOld = newRot; } } else if (Tools.current == Tool.Scale) { Undo.RecordObject(_creator, "Scale Path"); BezierPath.Scale = Handles.DoScaleHandle( BezierPath.Scale, BezierPath.Pivot, Quaternion.identity, HandleUtility.GetHandleSize(BezierPath.Pivot)); if (_shareTransformsWithPath) { _creator.transform.localScale = BezierPath.Scale; _scaleOld = BezierPath.Scale; } } else { Undo.RecordObject(_creator, "Move Path"); BezierPath.Pivot = bounds.center; var newCentre = Handles.DoPositionHandle(BezierPath.Pivot, Quaternion.identity); var deltaCentre = newCentre - BezierPath.Pivot; BezierPath.Position += deltaCentre; if (_shareTransformsWithPath) { _creator.transform.position = BezierPath.Position; _positionOld = BezierPath.Position; } } } if (Data.showPathBounds) { Handles.color = _globalDisplaySettings.bounds; Handles.DrawWireCube(bounds.center, bounds.size); } if (Data.displayAnchorPoints) { for (var i = 0; i < BezierPath.NumPoints; i += 3) { DrawHandle(i); } } if (displayControlPoints) { for (var i = 1; i < BezierPath.NumPoints - 1; i += 3) { DrawHandle(i); DrawHandle(i + 1); } } }
public ScreenSpacePolyLine(BezierPath bezierPath, float maxAngleError, float minVertexDst, float accuracy = 1) { // Split path in vertices based on angle error VerticesWorld = new List <Vector3>(); _vertexToPathSegmentMap = new List <int>(); _segmentStartIndices = new int[bezierPath.NumSegments + 1]; VerticesWorld.Add(bezierPath[0]); _vertexToPathSegmentMap.Add(0); var prevPointOnPath = bezierPath[0]; float dstSinceLastVertex = 0; var lastAddedPoint = prevPointOnPath; float dstSinceLastIntermediary = 0; for (var segmentIndex = 0; segmentIndex < bezierPath.NumSegments; segmentIndex++) { var segmentPoints = bezierPath.GetPointsInSegment(segmentIndex); VerticesWorld.Add(segmentPoints[0]); _vertexToPathSegmentMap.Add(segmentIndex); _segmentStartIndices[segmentIndex] = VerticesWorld.Count - 1; prevPointOnPath = segmentPoints[0]; lastAddedPoint = prevPointOnPath; dstSinceLastVertex = 0; dstSinceLastIntermediary = 0; var estimatedSegmentLength = CubicBezierUtility.EstimateCurveLength( segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]); var divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy * AccuracyMultiplier); var increment = 1f / divisions; for (var t = increment; t <= 1; t += increment) { var pointOnPath = CubicBezierUtility.EvaluateCurve( segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t); var nextPointOnPath = CubicBezierUtility.EvaluateCurve( segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t + increment); // angle at current point on path var localAngle = 180 - MathUtility.MinAngle(prevPointOnPath, pointOnPath, nextPointOnPath); // angle between the last added vertex, the current point on the path, and the next point on the path var angleFromPrevVertex = 180 - MathUtility.MinAngle(lastAddedPoint, pointOnPath, nextPointOnPath); var angleError = Mathf.Max(localAngle, angleFromPrevVertex); if (angleError > maxAngleError && dstSinceLastVertex >= minVertexDst) { dstSinceLastVertex = 0; dstSinceLastIntermediary = 0; VerticesWorld.Add(pointOnPath); _vertexToPathSegmentMap.Add(segmentIndex); lastAddedPoint = pointOnPath; } else { if (dstSinceLastIntermediary > IntermediaryThreshold) { VerticesWorld.Add(pointOnPath); _vertexToPathSegmentMap.Add(segmentIndex); dstSinceLastIntermediary = 0; } else { dstSinceLastIntermediary += (pointOnPath - prevPointOnPath).magnitude; } dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude; } prevPointOnPath = pointOnPath; } } _segmentStartIndices[bezierPath.NumSegments] = VerticesWorld.Count; // ensure final point gets added (unless path is closed loop) if (!bezierPath.IsClosed) { VerticesWorld.Add(bezierPath[bezierPath.NumPoints - 1]); } else { VerticesWorld.Add(bezierPath[0]); } // Calculate length _cumululativeLengthWorld = new float[VerticesWorld.Count]; for (var i = 1; i < VerticesWorld.Count; i++) { _pathLengthWorld += (VerticesWorld[i - 1] - VerticesWorld[i]).magnitude; _cumululativeLengthWorld[i] = _pathLengthWorld; } }
/// <summary> /// 0 means no curvature. 1 means max curvature defined by <see cref="maxCurvature"/> /// </summary> /// <param name="d"></param> /// <returns></returns> public float GetCurvatureAtDistance(float d) { float t = AbsoluteDistanceToT(d); return(Mathf.Clamp(CubicBezierUtility.EvaluateCurvature(GetPoints(), t), 0f, maxCurvature) / maxCurvature); }
/// <summary> /// 0 means no curvature. 1 means max curvature defined by <see cref="maxCurvature"/> /// </summary> /// <param name="t"></param> /// <returns></returns> public float GetCurvature(float t) { return(Mathf.Clamp(CubicBezierUtility.EvaluateCurvature(GetPoints(), t), 0f, maxCurvature) / maxCurvature); }
public Vector3 GetTangent(float t) { return(CubicBezierUtility.EvaluateCurveDerivative(GetPoints(), t).normalized); }
public Vector3 GetPoint(float t) { return(CubicBezierUtility.EvaluateCurve(GetPoints(), t)); }