Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The opt-in lives in `.sonarlint/sonar-local.props` (analyzer package) and `.sona
| `Semantics.Paths` | Polymorphic file system path types (`IPath`, `IFilePath`, `IDirectoryPath`, …). |
| `Semantics.Music` | Immutable musical value types (`Pitch`, `Interval`, `Scale`, `Chord`, `Key`, `Duration`, `TimeSignature`) plus an analysis aggregate layer (`Progression`, `Section`, `Arrangement`, `Form`) computing roman numerals, cadences, key inference, chromatic identification, and named forms. Targets `net8.0`–`net10.0` + `netstandard2.0`/`netstandard2.1`. |
| `Semantics.Color` | Physically-grounded color types. Canonical linear-RGB `Color` hub plus color-space satellites (`Srgb`, `Hsl`, `Hsv`, `Oklab`, `Oklch`); every type converts to and from every other, routed through the nearest shared hub (`Srgb` within the sRGB family, `Oklab` within the perceptual family, linear `Color` across families) so no conversion takes a redundant gamma round-trip. Also WCAG accessibility tooling, HSL/perceptual adjustment operations (lighten/saturate/hue/invert), and `NamedColors`. Targets `net8.0`–`net10.0` + `netstandard2.0`/`netstandard2.1`. |
| `Semantics.Quantities` | Hand-written runtime types (`PhysicalQuantity<TSelf, T>`, `IVector0`..`IVector4`, `UnitSystem`) plus generator output under `Generated/`. |
| `Semantics.Quantities` | Hand-written runtime types (`IPhysicalQuantity<TSelf, T>`, `PhysicalQuantityCore`, `IVector0`..`IVector4`, `UnitSystem`) plus generator output under `Generated/`. Every generated quantity is a `readonly record struct`. |
| `Semantics.SourceGenerators` | Roslyn incremental generators that emit quantity types, units, conversions, magnitudes, physical constants, and storage-type helpers from metadata. Only the physics-specific half lives here — `Models/`, `Metadata/`, `Generators/`, and the bindings in `SemanticsGenerator`/`SemanticsDiagnostics`/`Emit`. The C# syntax templates come from `ktsu.CodeBlocker.Templates`; the metadata-driven generator base, metadata loading and the diagnostic catalogue come from `ktsu.SourceGeneratorToolkit` (#181, #192). |
| `Semantics.Quantities.{Double,Float,Decimal}` | Props-only satellite packages. Each ships a `buildTransitive` props file (generated by `scripts/Generate-AliasProps.ps1`) that injects global-using aliases binding every quantity to one storage type, so consumers write `Mass` instead of `Mass<double>`. |
| `Semantics.Test` | MSTest project covering all of the above. |
Expand Down
9 changes: 2 additions & 7 deletions Semantics.Quantities/AudioEngineering/Gain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace ktsu.Semantics.Quantities;
/// using the amplitude (field) convention <c>dB = 20·log10(gain)</c>. As a Vector0
/// magnitude, gain is non-negative — polarity inversion is a separate concern.
/// </remarks>
public partial record Gain<T>
public readonly partial record struct Gain<T>
where T : struct, INumber<T>
{
/// <summary>Gets unity gain (a factor of one).</summary>
Expand Down Expand Up @@ -44,12 +44,7 @@ public partial record Gain<T>
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Usage", "CA2225:Operator overloads have named alternates",
Justification = "Multiply is provided as the named alternate.")]
public static Gain<T> operator *(Gain<T> left, Gain<T> right)
{
ArgumentNullException.ThrowIfNull(left);
ArgumentNullException.ThrowIfNull(right);
return Create(left.Value * right.Value);
}
public static Gain<T> operator *(Gain<T> left, Gain<T> right) => Create(left.Value * right.Value);

/// <summary>Multiplies two gains (friendly alternate for <c>operator *</c>).</summary>
/// <param name="left">The first gain.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,89 @@ namespace ktsu.Semantics.Quantities;
/// Magnitude (Vector0) quantity for the AbsorbedDose dimension.
/// </summary>
/// <typeparam name="T">The numeric storage type.</typeparam>
public partial record AbsorbedDose<T> : PhysicalQuantity<AbsorbedDose<T>, T>, IVector0<AbsorbedDose<T>, T>
public readonly partial record struct AbsorbedDose<T> : IVector0<AbsorbedDose<T>, T>, IPhysicalQuantity<AbsorbedDose<T>, T>
where T : struct, INumber<T>
{
/// <summary>Gets the value stored in this quantity (in the dimension's SI base unit).</summary>
public T Value => Quantity;

/// <summary>Gets whether this quantity is finite and not NaN.</summary>
public bool IsPhysicallyValid => PhysicalQuantityCore.IsPhysicallyValid(Quantity);

/// <summary>Gets a quantity with value zero.</summary>
public static AbsorbedDose<T> Zero => Create(T.Zero);

/// <summary>Gets the physical dimension this quantity belongs to.</summary>
public override DimensionInfo Dimension => PhysicalDimensions.AbsorbedDose;
public DimensionInfo Dimension => PhysicalDimensions.AbsorbedDose;

/// <summary>Gets the stored value, in the dimension's SI base unit.</summary>
public T Quantity { get; init; }

/// <summary>
/// Creates a quantity holding <paramref name="value"/>, in the SI base unit.
/// </summary>
/// <param name="value">The value in the SI base unit.</param>
/// <returns>A new quantity holding <paramref name="value"/>.</returns>
public static AbsorbedDose<T> Create(T value) => new() { Quantity = value };

/// <summary>
/// Compares this quantity to another of the same physical dimension.
/// </summary>
/// <param name="other">The quantity to compare against.</param>
/// <returns>A negative number, zero or a positive number as this sorts before, with, or after <paramref name="other"/>.</returns>
/// <exception cref="System.ArgumentException">When the two do not share a dimension.</exception>
public int CompareTo(IPhysicalQuantity<T> other) => PhysicalQuantityCore.Compare<AbsorbedDose<T>, T>(this, other);

/// <summary>
/// Reports whether this quantity shares a dimension and a value with <paramref name="other"/>.
/// </summary>
/// <param name="other">The quantity to compare against.</param>
/// <returns><see langword="true"/> when both dimension and value match.</returns>
public bool Equals(IPhysicalQuantity<T> other) => PhysicalQuantityCore.AreEqual<AbsorbedDose<T>, T>(this, other);

/// <summary>Adds two <see cref="AbsorbedDose{T}"/> values.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AbsorbedDose<T> operator +(AbsorbedDose<T> left, AbsorbedDose<T> right) => Create(left.Quantity + right.Quantity);

/// <summary>Negates a <see cref="AbsorbedDose{T}"/>.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AbsorbedDose<T> operator -(AbsorbedDose<T> value) => Create(-value.Quantity);

/// <summary>Scales a <see cref="AbsorbedDose{T}"/> by a bare number.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AbsorbedDose<T> operator *(AbsorbedDose<T> left, T right) => Create(left.Quantity * right);

/// <summary>Scales a <see cref="AbsorbedDose{T}"/> by a bare number.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AbsorbedDose<T> operator *(T left, AbsorbedDose<T> right) => Create(left * right.Quantity);

/// <summary>Divides a <see cref="AbsorbedDose{T}"/> by a bare number.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AbsorbedDose<T> operator /(AbsorbedDose<T> left, T right) => T.IsZero(right) ? throw new System.DivideByZeroException("Cannot divide by zero.") : Create(left.Quantity / right);

/// <summary>Divides one <see cref="AbsorbedDose{T}"/> by another, giving the bare ratio.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static T operator /(AbsorbedDose<T> left, AbsorbedDose<T> right) => T.IsZero(right.Quantity) ? throw new System.DivideByZeroException("Cannot divide by zero.") : left.Quantity / right.Quantity;

/// <summary>Reports whether the left value sorts before the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator <(AbsorbedDose<T> left, AbsorbedDose<T> right) => left.Quantity < right.Quantity;

/// <summary>Reports whether the left value sorts before or with the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator <=(AbsorbedDose<T> left, AbsorbedDose<T> right) => left.Quantity <= right.Quantity;

/// <summary>Reports whether the left value sorts after the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator >(AbsorbedDose<T> left, AbsorbedDose<T> right) => left.Quantity > right.Quantity;

/// <summary>Reports whether the left value sorts after or with the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator >=(AbsorbedDose<T> left, AbsorbedDose<T> right) => left.Quantity >= right.Quantity;

/// <summary>Returns the stored value as text.</summary>
/// <returns>The value in the SI base unit, rendered by <typeparamref name="T"/>.</returns>
public override string ToString() => Quantity.ToString() ?? string.Empty;

/// <summary>
/// Creates a new <see cref="AbsorbedDose{T}"/> from a value in Gray.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,93 @@ namespace ktsu.Semantics.Quantities;
/// Signed one-dimensional (Vector1) quantity for the Acceleration dimension.
/// </summary>
/// <typeparam name="T">The numeric storage type.</typeparam>
public partial record Acceleration1D<T> : PhysicalQuantity<Acceleration1D<T>, T>, IVector1<Acceleration1D<T>, T>
public readonly partial record struct Acceleration1D<T> : IVector1<Acceleration1D<T>, T>, IPhysicalQuantity<Acceleration1D<T>, T>
where T : struct, INumber<T>
{
/// <summary>Gets the value stored in this quantity (in the dimension's SI base unit).</summary>
public T Value => Quantity;

/// <summary>Gets whether this quantity is finite and not NaN.</summary>
public bool IsPhysicallyValid => PhysicalQuantityCore.IsPhysicallyValid(Quantity);

/// <summary>Gets a quantity with value zero.</summary>
public static Acceleration1D<T> Zero => Create(T.Zero);

/// <summary>Gets the physical dimension this quantity belongs to.</summary>
public override DimensionInfo Dimension => PhysicalDimensions.Acceleration;
public DimensionInfo Dimension => PhysicalDimensions.Acceleration;

/// <summary>Gets the stored value, in the dimension's SI base unit.</summary>
public T Quantity { get; init; }

/// <summary>
/// Creates a quantity holding <paramref name="value"/>, in the SI base unit.
/// </summary>
/// <param name="value">The value in the SI base unit.</param>
/// <returns>A new quantity holding <paramref name="value"/>.</returns>
public static Acceleration1D<T> Create(T value) => new() { Quantity = value };

/// <summary>
/// Compares this quantity to another of the same physical dimension.
/// </summary>
/// <param name="other">The quantity to compare against.</param>
/// <returns>A negative number, zero or a positive number as this sorts before, with, or after <paramref name="other"/>.</returns>
/// <exception cref="System.ArgumentException">When the two do not share a dimension.</exception>
public int CompareTo(IPhysicalQuantity<T> other) => PhysicalQuantityCore.Compare<Acceleration1D<T>, T>(this, other);

/// <summary>
/// Reports whether this quantity shares a dimension and a value with <paramref name="other"/>.
/// </summary>
/// <param name="other">The quantity to compare against.</param>
/// <returns><see langword="true"/> when both dimension and value match.</returns>
public bool Equals(IPhysicalQuantity<T> other) => PhysicalQuantityCore.AreEqual<Acceleration1D<T>, T>(this, other);

/// <summary>Adds two <see cref="Acceleration1D{T}"/> values.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Acceleration1D<T> operator +(Acceleration1D<T> left, Acceleration1D<T> right) => Create(left.Quantity + right.Quantity);

/// <summary>Subtracts one <see cref="Acceleration1D{T}"/> from another.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Acceleration1D<T> operator -(Acceleration1D<T> left, Acceleration1D<T> right) => Create(left.Quantity - right.Quantity);

/// <summary>Negates a <see cref="Acceleration1D{T}"/>.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Acceleration1D<T> operator -(Acceleration1D<T> value) => Create(-value.Quantity);

/// <summary>Scales a <see cref="Acceleration1D{T}"/> by a bare number.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Acceleration1D<T> operator *(Acceleration1D<T> left, T right) => Create(left.Quantity * right);

/// <summary>Scales a <see cref="Acceleration1D{T}"/> by a bare number.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Acceleration1D<T> operator *(T left, Acceleration1D<T> right) => Create(left * right.Quantity);

/// <summary>Divides a <see cref="Acceleration1D{T}"/> by a bare number.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Acceleration1D<T> operator /(Acceleration1D<T> left, T right) => T.IsZero(right) ? throw new System.DivideByZeroException("Cannot divide by zero.") : Create(left.Quantity / right);

/// <summary>Divides one <see cref="Acceleration1D{T}"/> by another, giving the bare ratio.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static T operator /(Acceleration1D<T> left, Acceleration1D<T> right) => T.IsZero(right.Quantity) ? throw new System.DivideByZeroException("Cannot divide by zero.") : left.Quantity / right.Quantity;

/// <summary>Reports whether the left value sorts before the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator <(Acceleration1D<T> left, Acceleration1D<T> right) => left.Quantity < right.Quantity;

/// <summary>Reports whether the left value sorts before or with the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator <=(Acceleration1D<T> left, Acceleration1D<T> right) => left.Quantity <= right.Quantity;

/// <summary>Reports whether the left value sorts after the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator >(Acceleration1D<T> left, Acceleration1D<T> right) => left.Quantity > right.Quantity;

/// <summary>Reports whether the left value sorts after or with the right.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static bool operator >=(Acceleration1D<T> left, Acceleration1D<T> right) => left.Quantity >= right.Quantity;

/// <summary>Returns the stored value as text.</summary>
/// <returns>The value in the SI base unit, rendered by <typeparamref name="T"/>.</returns>
public override string ToString() => Quantity.ToString() ?? string.Empty;

/// <summary>
/// Creates a new <see cref="Acceleration1D{T}"/> from a value in MeterPerSecondSquared.
Expand Down Expand Up @@ -50,12 +129,12 @@ public partial record Acceleration1D<T> : PhysicalQuantity<Acceleration1D<T>, T>
/// Multiplies Acceleration1D by Duration to produce Velocity1D.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Velocity1D<T> operator *(Acceleration1D<T> left, Duration<T> right) => Multiply<Velocity1D<T>>(left, right);
public static Velocity1D<T> operator *(Acceleration1D<T> left, Duration<T> right) => Velocity1D<T>.Create(left.Quantity * right.Quantity);

/// <summary>
/// Divides Acceleration1D by Duration to produce Jerk1D.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Jerk1D<T> operator /(Acceleration1D<T> left, Duration<T> right) => Divide<Jerk1D<T>>(left, right);
public static Jerk1D<T> operator /(Acceleration1D<T> left, Duration<T> right) => Jerk1D<T>.Create(left.Quantity / right.Quantity);
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace ktsu.Semantics.Quantities;
/// 2D vector representation of Acceleration.
/// </summary>
/// <typeparam name="T">The numeric component type.</typeparam>
public partial record Acceleration2D<T> : IVector2<Acceleration2D<T>, T>
public readonly partial record struct Acceleration2D<T> : IVector2<Acceleration2D<T>, T>
where T : struct, INumber<T>
{
/// <summary>Gets the X component.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace ktsu.Semantics.Quantities;
/// 3D vector representation of Acceleration.
/// </summary>
/// <typeparam name="T">The numeric component type.</typeparam>
public partial record Acceleration3D<T> : IVector3<Acceleration3D<T>, T>
public readonly partial record struct Acceleration3D<T> : IVector3<Acceleration3D<T>, T>
where T : struct, INumber<T>
{
/// <summary>Gets the X component.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace ktsu.Semantics.Quantities;
/// 4D vector representation of Acceleration.
/// </summary>
/// <typeparam name="T">The numeric component type.</typeparam>
public partial record Acceleration4D<T> : IVector4<Acceleration4D<T>, T>
public readonly partial record struct Acceleration4D<T> : IVector4<Acceleration4D<T>, T>
where T : struct, INumber<T>
{
/// <summary>Gets the X component.</summary>
Expand Down
Loading
Loading