a84e6d89ca
* Update to csharpier 1.0 * Fix check and nowarn * format
256 lines
7.0 KiB
C#
256 lines
7.0 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Speckle.Objects.Other;
|
|
using Speckle.Objects.Primitive;
|
|
using Speckle.Sdk.Common;
|
|
using Speckle.Sdk.Models;
|
|
|
|
namespace Speckle.Objects.Geometry;
|
|
|
|
/// <summary>
|
|
/// A Surface in NURBS form.
|
|
/// </summary>
|
|
[SpeckleType("Objects.Geometry.Surface")]
|
|
public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable<Surface>
|
|
{
|
|
[Obsolete("Constructor should only be used by serializer, use one of the other constructors instead")]
|
|
public Surface()
|
|
{
|
|
pointData = [];
|
|
}
|
|
|
|
public Surface(List<List<ControlPoint>> controlPoints)
|
|
{
|
|
SetControlPoints(controlPoints);
|
|
}
|
|
|
|
public Surface(IList<double> pointData, int countU, int countV)
|
|
{
|
|
this.pointData = pointData;
|
|
this.countU = countU;
|
|
this.countV = countV;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The degree of the surface in the U direction
|
|
/// </summary>
|
|
public required int degreeU { get; set; }
|
|
|
|
/// <summary>
|
|
/// The degree of the surface in the V direction
|
|
/// </summary>
|
|
public required int degreeV { get; set; }
|
|
|
|
/// <summary>
|
|
/// Determines if the <see cref="Surface"/> is rational.
|
|
/// </summary>
|
|
public required bool rational { get; set; }
|
|
|
|
/// <summary>
|
|
/// The raw data of the surface's control points. Use <see cref="GetControlPoints"/> or <see cref="SetControlPoints"/> instead of accessing this directly.
|
|
/// </summary>
|
|
public IList<double> pointData { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of control points in the U direction
|
|
/// </summary>
|
|
public int countU { get; set; }
|
|
|
|
/// <summary>
|
|
/// The number of control points in the V direction
|
|
/// </summary>
|
|
public int countV { get; set; }
|
|
|
|
/// <summary>
|
|
/// The knot vector in the U direction
|
|
/// </summary>
|
|
public required List<double> knotsU { get; set; }
|
|
|
|
/// <summary>
|
|
/// The knot vector in the V direction
|
|
/// </summary>
|
|
public required List<double> knotsV { get; set; }
|
|
|
|
/// <summary>
|
|
/// The surface's domain in the U direction
|
|
/// </summary>
|
|
public required Interval domainU { get; set; }
|
|
|
|
/// <summary>
|
|
/// The surface's domain in the V direction
|
|
/// </summary>
|
|
public required Interval domainV { get; set; }
|
|
|
|
/// <summary>
|
|
/// Determines if a surface is closed around the <see cref="domainU"/>.
|
|
/// </summary>
|
|
public required bool closedU { get; set; }
|
|
|
|
/// <summary>
|
|
/// Determines if a surface is closed around the <see cref="domainV"/>
|
|
/// </summary>
|
|
public required bool closedV { get; set; }
|
|
|
|
/// <summary>
|
|
/// The unit's this <see cref="Surface"/> is in.
|
|
/// This should be one of <see cref="Units"/>
|
|
/// </summary>
|
|
public required string units { get; set; }
|
|
|
|
/// <inheritdoc/>
|
|
public double area { get; set; }
|
|
|
|
/// <inheritdoc/>
|
|
public Box? bbox { get; set; }
|
|
|
|
/// <inheritdoc/>
|
|
public bool TransformTo(Transform transform, out Surface transformed)
|
|
{
|
|
var ptMatrix = GetControlPoints();
|
|
foreach (var ctrlPts in ptMatrix)
|
|
{
|
|
for (int i = 0; i < ctrlPts.Count; i++)
|
|
{
|
|
ctrlPts[i].TransformTo(transform, out var tPt);
|
|
ctrlPts[i] = tPt;
|
|
}
|
|
}
|
|
|
|
transformed = new Surface(ptMatrix)
|
|
{
|
|
degreeU = degreeU,
|
|
degreeV = degreeV,
|
|
countU = countU,
|
|
countV = countV,
|
|
rational = rational,
|
|
closedU = closedU,
|
|
closedV = closedV,
|
|
domainU = domainU,
|
|
domainV = domainV,
|
|
knotsU = knotsU,
|
|
knotsV = knotsV,
|
|
units = units,
|
|
};
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool TransformTo(Transform transform, out ITransformable transformed)
|
|
{
|
|
var res = TransformTo(transform, out Surface surface);
|
|
transformed = surface;
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the control points of this s<see cref="Surface"/>
|
|
/// </summary>
|
|
/// <returns>A 2-dimensional array representing this <see cref="Surface"/>s control points.</returns>
|
|
/// <remarks>The ControlPoints will be ordered following directions "[u][v]"</remarks>
|
|
public List<List<ControlPoint>> GetControlPoints()
|
|
{
|
|
var matrix = new List<List<ControlPoint>>();
|
|
for (var i = 0; i < countU; i++)
|
|
{
|
|
matrix.Add(new List<ControlPoint>());
|
|
}
|
|
|
|
for (var i = 0; i < pointData.Count; i += 4)
|
|
{
|
|
var uIndex = i / (countV * 4);
|
|
matrix[uIndex].Add(new ControlPoint(pointData[i], pointData[i + 1], pointData[i + 2], pointData[i + 3], units));
|
|
}
|
|
|
|
return matrix;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the control points of this <see cref="Surface"/>.
|
|
/// </summary>
|
|
/// <param name="value">A 2-dimensional array of <see cref="ControlPoint"/> instances.</param>
|
|
/// <remarks>The <paramref name="value"/> must be ordered following directions "[u][v]"</remarks>
|
|
[MemberNotNull(nameof(pointData))]
|
|
[MemberNotNull(nameof(countU))]
|
|
[MemberNotNull(nameof(countV))]
|
|
public void SetControlPoints(List<List<ControlPoint>> value)
|
|
{
|
|
List<double> data = new();
|
|
countU = value.Count;
|
|
countV = value[0].Count;
|
|
value.ForEach(row =>
|
|
row.ForEach(pt =>
|
|
{
|
|
data.Add(pt.x);
|
|
data.Add(pt.y);
|
|
data.Add(pt.z);
|
|
data.Add(pt.weight);
|
|
})
|
|
);
|
|
pointData = data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the coordinates of this <see cref="Surface"/> as a list of numbers
|
|
/// </summary>
|
|
/// <returns>A list of values representing the surface</returns>
|
|
public List<double> ToList()
|
|
{
|
|
var list = new List<double>();
|
|
list.Add(degreeU);
|
|
list.Add(degreeV);
|
|
list.Add(countU);
|
|
list.Add(countV);
|
|
list.Add(rational ? 1 : 0);
|
|
list.Add(closedU ? 1 : 0);
|
|
list.Add(closedV ? 1 : 0);
|
|
list.Add(domainU.start); // 7
|
|
list.Add(domainU.end);
|
|
list.Add(domainV.start);
|
|
list.Add(domainV.end); // [0] 10
|
|
|
|
list.Add(pointData.Count); // 11
|
|
list.Add(knotsU.Count); // 12
|
|
list.Add(knotsV.Count); // 13
|
|
|
|
list.AddRange(pointData);
|
|
list.AddRange(knotsU);
|
|
list.AddRange(knotsV);
|
|
|
|
list.Add(Units.GetEncodingFromUnit(units));
|
|
list.Insert(0, list.Count);
|
|
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new <see cref="Surface"/> based on a list of coordinates and the unit they're drawn in.
|
|
/// </summary>
|
|
/// <param name="list">The list of values representing this surface</param>
|
|
/// <returns>A new <see cref="Surface"/> with the provided values.</returns>
|
|
public static Surface FromList(List<double> list)
|
|
{
|
|
var pointCount = (int)list[11];
|
|
var knotsUCount = (int)list[12];
|
|
var knotsVCount = (int)list[13];
|
|
var countU = (int)list[2];
|
|
var countV = (int)list[3];
|
|
|
|
var pointData = list.GetRange(14, pointCount);
|
|
var u = list[^1];
|
|
|
|
return new Surface(pointData, countU, countV)
|
|
{
|
|
degreeU = (int)list[0],
|
|
degreeV = (int)list[1],
|
|
rational = list[4] == 1,
|
|
closedU = list[5] == 1,
|
|
closedV = list[6] == 1,
|
|
domainU = new Interval { start = list[7], end = list[8] },
|
|
domainV = new Interval { start = list[9], end = list[10] },
|
|
knotsU = list.GetRange(14 + pointCount, knotsUCount),
|
|
knotsV = list.GetRange(14 + pointCount + knotsUCount, knotsVCount),
|
|
units = Units.GetUnitFromEncoding(u),
|
|
};
|
|
}
|
|
}
|