Sicherung

This commit is contained in:
Maier Stephan SI
2023-01-20 16:09:00 +01:00
parent e5257d8413
commit b684704bf8
139 changed files with 95678 additions and 499 deletions

View File

@@ -0,0 +1,16 @@
using System;
namespace Kalk.Core
{
public interface IKalkVectorObject
{
int Length { get; }
Type ElementType { get; }
}
public interface IKalkVectorObject<T> : IKalkVectorObject
{
object Transform(Func<T, T> apply);
}
}

View File

@@ -0,0 +1,178 @@
using System;
using Scriban;
using Scriban.Parsing;
using Scriban.Syntax;
namespace Kalk.Core
{
public struct KalkBool : IEquatable<KalkBool>, IFormattable, IConvertible, IScriptConvertibleTo, IScriptConvertibleFrom, IScriptCustomTypeInfo
{
private int _value;
public KalkBool(bool value)
{
_value = value ? -1 : 0;
}
public override string ToString()
{
return ToString(null, null);
}
public bool TryConvertFrom(TemplateContext context, SourceSpan span, object value)
{
try
{
_value = Convert.ToBoolean(value) ? -1 : 0;
return true;
}
catch
{
// ignore
}
return false;
}
public bool TryConvertTo(TemplateContext context, SourceSpan span, Type type, out object value)
{
if (type == typeof(bool))
{
value = (bool) this;
return true;
}
try
{
value = Convert.ChangeType(_value, type);
return true;
}
catch
{
// ignore
}
value = null;
return false;
}
public string ToString(string? format, IFormatProvider? formatProvider)
{
return _value != 0 ? "true" : "false";
}
public bool Equals(KalkBool other)
{
return _value == other._value;
}
public override bool Equals(object obj)
{
return obj is KalkBool other && Equals(other);
}
public override int GetHashCode()
{
return _value;
}
public static bool operator ==(KalkBool left, KalkBool right)
{
return left.Equals(right);
}
public static bool operator !=(KalkBool left, KalkBool right)
{
return !left.Equals(right);
}
public static implicit operator bool(KalkBool b) => b._value != 0;
public static implicit operator KalkBool(bool b) => new KalkBool(b);
TypeCode IConvertible.GetTypeCode()
{
return _value.GetTypeCode();
}
bool IConvertible.ToBoolean(IFormatProvider? provider)
{
return ((IConvertible) _value).ToBoolean(provider);
}
byte IConvertible.ToByte(IFormatProvider? provider)
{
return ((IConvertible) _value).ToByte(provider);
}
char IConvertible.ToChar(IFormatProvider? provider)
{
return ((IConvertible) _value).ToChar(provider);
}
DateTime IConvertible.ToDateTime(IFormatProvider? provider)
{
return ((IConvertible) _value).ToDateTime(provider);
}
decimal IConvertible.ToDecimal(IFormatProvider? provider)
{
return ((IConvertible) _value).ToDecimal(provider);
}
double IConvertible.ToDouble(IFormatProvider? provider)
{
return ((IConvertible) _value).ToDouble(provider);
}
short IConvertible.ToInt16(IFormatProvider? provider)
{
return ((IConvertible) _value).ToInt16(provider);
}
int IConvertible.ToInt32(IFormatProvider? provider)
{
return ((IConvertible) _value).ToInt32(provider);
}
long IConvertible.ToInt64(IFormatProvider? provider)
{
return ((IConvertible) _value).ToInt64(provider);
}
sbyte IConvertible.ToSByte(IFormatProvider? provider)
{
return ((IConvertible) _value).ToSByte(provider);
}
float IConvertible.ToSingle(IFormatProvider? provider)
{
return ((IConvertible) _value).ToSingle(provider);
}
string IConvertible.ToString(IFormatProvider? provider)
{
return _value.ToString(provider);
}
object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
return ((IConvertible) _value).ToType(conversionType, provider);
}
ushort IConvertible.ToUInt16(IFormatProvider? provider)
{
return ((IConvertible) _value).ToUInt16(provider);
}
uint IConvertible.ToUInt32(IFormatProvider? provider)
{
return ((IConvertible) _value).ToUInt32(provider);
}
ulong IConvertible.ToUInt64(IFormatProvider? provider)
{
return ((IConvertible) _value).ToUInt64(provider);
}
public string TypeName => "bool";
}
}

View File

@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Text;
using Consolus;
using Scriban;
using Scriban.Parsing;
using Scriban.Syntax;
namespace Kalk.Core
{
// https://github.com/google/palette.js/tree/master
[ScriptTypeName("color")]
public abstract class KalkColor : KalkVector<byte>
{
protected KalkColor(int dimension) : base(dimension)
{
}
protected KalkColor(IReadOnlyList<byte> list) : base(list)
{
}
protected KalkColor(KalkVector<byte> values) : base(values)
{
}
public int rgb => (int)((r << 16) | (g << 8) | b);
public string Name
{
get
{
KalkColorRgb.TryGetKnownColor(rgb, out var name);
return name;
}
}
public override IEnumerable<string> GetMembers()
{
for (int i = 0; i < Math.Min(4, Length); i++)
{
switch (i)
{
case 0:
yield return "r";
break;
case 1:
yield return "g";
break;
case 2:
yield return "b";
break;
case 3:
yield return "a";
break;
}
}
yield return "name";
}
public override bool TryGetValue(TemplateContext context, SourceSpan span, string member, out object result)
{
if (member == "name")
{
result = Name;
return true;
}
return base.TryGetValue(context, span, member, out result);
}
private static float Clamp01(float value) => Math.Clamp(value, 0.0f, 1.0f);
public KalkVector<float> GetFloatVector(int targetDimension)
{
return this is KalkColorRgb
? targetDimension == 4 ?
new KalkVector<float>(Clamp01(this[0] / 255.0f), Clamp01(this[1] / 255.0f), Clamp01(this[2] / 255.0f), 1.0f):
new KalkVector<float>(Clamp01(this[0] / 255.0f), Clamp01(this[1] / 255.0f), Clamp01(this[2] / 255.0f))
: new KalkVector<float>(Clamp01(this[0] / 255.0f), Clamp01(this[1] / 255.0f), Clamp01(this[2] / 255.0f), Clamp01(this[3] / 255.0f));
}
protected override object GetSwizzleValue(ComponentKind kind, byte result)
{
return kind == ComponentKind.xyzw ? (object)Clamp01(result / 255.0f) : result;
}
protected override byte TransformComponentToSet(TemplateContext context, SourceSpan span, ComponentKind kind, object value)
{
return kind == ComponentKind.xyzw ? (byte)(Clamp01(context.ToObject<float>(span, value)) * 255) : base.TransformComponentToSet(context, span, kind, value);
}
protected override KalkVector NewVector(ComponentKind kind, IReadOnlyList<byte> list)
{
if (kind == ComponentKind.xyzw)
{
if (list.Count == 4)
{
return new KalkVector<float>(Clamp01(list[0] / 255.0f), Clamp01(list[1] / 255.0f), Clamp01(list[2] / 255.0f), Clamp01(list[3] / 255.0f));
}
if (list.Count == 3)
{
return new KalkVector<float>(Clamp01(list[0] / 255.0f), Clamp01(list[1] / 255.0f), Clamp01(list[2] / 255.0f));
}
if (list.Count == 2)
{
return new KalkVector<float>(Clamp01(list[0] / 255.0f), Clamp01(list[1] / 255.0f));
}
throw new InvalidOperationException("Cannot create a float vector type from this rgba components");
}
else
{
return list.Count == 4 ? new KalkColorRgba(list[0], list[1], list[2], list[3]) : list.Count == 3 ? new KalkColorRgb(list[0], list[1], list[2]) : base.NewVector(kind, list);
}
}
protected abstract override KalkVector<byte> NewVector(int length);
public abstract override string TypeName { get; }
public override string ToString(string format, IFormatProvider formatProvider)
{
var engine = formatProvider as KalkEngine;
var builder = new StringBuilder();
builder.Append(TypeName);
builder.Append('(');
var length = this is KalkColorRgb ? 3 : 4;
for (int i = 0; i < length; i++)
{
if (i > 0) builder.Append(", ");
builder.Append(engine != null ? engine.ObjectToString(this[i]) : this[i].ToString(null, formatProvider));
}
builder.Append(')');
bool isAligned = format == "aligned";
// rgb(240, 248, 255)
// rgb(255, 255, 255, 255)
if (isAligned)
{
if (length == 3)
{
builder.Append(' ', "rgb(255, 255, 255)".Length - builder.Length);
}
else if (length == 4)
{
builder.Append(' ', "rgb(255, 255, 255, 255)".Length - builder.Length);
}
}
builder.Append(" ## ");
for (int i = 0; i < length; i++)
{
builder.Append($"{this[i]:X2}");
}
if (engine != null && engine.IsOutputSupportHighlighting)
{
builder.Append(" ");
builder.Append(ConsoleStyle.BackgroundRgb(this[0], this[1], this[2]));
builder.Append(" ");
builder.Append(ConsoleStyle.Reset);
}
// Add known color name
if (KalkColorRgb.TryGetKnownColor(rgb, out var colorName))
{
builder.Append(isAligned ? $" {colorName,-20}" : $" {colorName}");
}
builder.Append(" ##");
return builder.ToString();
}
}
}

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections;
using Scriban;
using Scriban.Syntax;
namespace Kalk.Core
{
public abstract class KalkColorConstructor : KalkVectorConstructor<byte>
{
protected KalkColorConstructor(int dimension) : base(dimension)
{
}
protected abstract override KalkVector<byte> NewVector(int dimension);
protected override byte GetArgumentValue(TemplateContext context, object arg)
{
switch (arg)
{
case float f32:
return (byte)(255 * Math.Clamp(f32, 0.0f, 1.0f));
case double f64:
return (byte)(255 * Math.Clamp(f64, 0.0, 1.0));
case decimal dec:
return (byte)(255 * Math.Clamp(dec, 0.0m, 1.0m));
default:
return base.GetArgumentValue(context, arg);
}
}
protected override void ProcessSingleArgument(TemplateContext context, ref int index, object arg, KalkVector<byte> vector)
{
int value;
switch (arg)
{
case string rgbStr:
try
{
if (!KalkColorRgb.TryGetKnownColor(rgbStr, out value))
{
value = int.Parse(rgbStr.TrimStart('#'), System.Globalization.NumberStyles.HexNumber);
}
}
catch
{
throw new ScriptArgumentException(0, $"Expecting a known color (e.g `AliceBlue`) or an hexadecimal rgb string (e.g #FF80C2) instead of `{rgbStr}`. Type `colors` for listing known colors.");
}
break;
default:
if (arg is IList)
{
base.ProcessSingleArgument(context, ref index, arg, vector);
return;
}
value = context.ToObject<int>(context.CurrentSpan, arg);
break;
}
vector[index++] = (byte)((value >> 16) & 0xFF);
vector[index++] = (byte)((value >> 8) & 0xFF);
vector[index++] = (byte)(value & 0xFF);
if (Dimension == 4)
{
vector[index++] = (byte)((value >> 24) & 0xFF);
}
}
}
}

View File

@@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Scriban.Syntax;
namespace Kalk.Core
{
[ScriptTypeName("rgb")]
public class KalkColorRgb : KalkColor
{
public KalkColorRgb() : base(4)
{
this[3] = 0xFF;
}
public KalkColorRgb(KalkColorRgb values) : base(values)
{
this[3] = 0xFF;
}
public KalkColorRgb(byte r, byte g, byte b) : this()
{
this[0] = r;
this[1] = g;
this[2] = b;
}
public KalkColorRgb(int rgb) : this()
{
this[0] = (byte)((rgb >> 16) & 0xFF);
this[1] = (byte)((rgb >> 8) & 0xFF);
this[2] = (byte)(rgb & 0xFF);
this[3] = 0xFF;
}
public override string TypeName => "rgb";
public override KalkVector Clone()
{
return new KalkColorRgb(this);
}
protected override KalkVector<byte> NewVector(int length) => new KalkColorRgb();
public static bool TryGetKnownColor(string color, out int rgb)
{
if (color == null) throw new ArgumentNullException(nameof(color));
return KnownColors.TryGetValue(color, out rgb);
}
public static bool TryGetKnownColor(int rgb, out string color)
{
return ReverseKnownColors.TryGetValue(rgb, out color);
}
public static List<KalkColorRgb> GetKnownColors()
{
return KnownColors.OrderBy(x => x.Key).Select(x => new KalkColorRgb(x.Value)).ToList();
}
private static readonly Dictionary<string, int> KnownColors = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
{"AliceBlue", 0xF0F8FF},
{"AntiqueWhite", 0xFAEBD7},
{"Aqua", 0x00FFFF},
{"Aquamarine", 0x7FFFD4},
{"Azure", 0xF0FFFF},
{"Beige", 0xF5F5DC},
{"Bisque", 0xFFE4C4},
{"Black", 0x0},
{"BlanchedAlmond", 0xFFEBCD},
{"Blue", 0x0000FF},
{"BlueViolet", 0x8A2BE2},
{"Brown", 0xA52A2A},
{"BurlyWood", 0xDEB887},
{"CadetBlue", 0x5F9EA0},
{"Chartreuse", 0x7FFF00},
{"Chocolate", 0xD2691E},
{"Coral", 0xFF7F50},
{"CornflowerBlue", 0x6495ED},
{"Cornsilk", 0xFFF8DC},
{"Crimson", 0xDC143C},
{"Cyan", 0x00FFFF},
{"DarkBlue", 0x00008B},
{"DarkCyan", 0x008B8B},
{"DarkGoldenrod", 0xB8860B},
{"DarkGray", 0xA9A9A9},
{"DarkGreen", 0x6400},
{"DarkKhaki", 0xBDB76B},
{"DarkMagenta", 0x8B008B},
{"DarkOliveGreen", 0x556B2F},
{"DarkOrange", 0xFF8C00},
{"DarkOrchid", 0x9932CC},
{"DarkRed", 0x8B0000},
{"DarkSalmon", 0xE9967A},
{"DarkSeaGreen", 0x8FBC8F},
{"DarkSlateBlue", 0x483D8B},
{"DarkSlateGray", 0x2F4F4F},
{"DarkTurquoise", 0x00CED1},
{"DarkViolet", 0x9400D3},
{"DeepPink", 0xFF1493},
{"DeepSkyBlue", 0x00BFFF},
{"DimGray", 0x696969},
{"DodgerBlue", 0x1E90FF},
{"Firebrick", 0xB22222},
{"FloralWhite", 0xFFFAF0},
{"ForestGreen", 0x228B22},
{"Fuchsia", 0xFF00FF},
{"Gainsboro", 0xDCDCDC},
{"GhostWhite", 0xF8F8FF},
{"Gold", 0xFFD700},
{"Goldenrod", 0xDAA520},
{"Gray", 0x808080},
{"Green", 0x8000},
{"GreenYellow", 0xADFF2F},
{"Honeydew", 0xF0FFF0},
{"HotPink", 0xFF69B4},
{"IndianRed", 0xCD5C5C},
{"Indigo", 0x4B0082},
{"Ivory", 0xFFFFF0},
{"Khaki", 0xF0E68C},
{"Lavender", 0xE6E6FA},
{"LavenderBlush", 0xFFF0F5},
{"LawnGreen", 0x7CFC00},
{"LemonChiffon", 0xFFFACD},
{"LightBlue", 0xADD8E6},
{"LightCoral", 0xF08080},
{"LightCyan", 0xE0FFFF},
{"LightGoldenrodYellow", 0xFAFAD2},
{"LightGreen", 0x90EE90},
{"LightGray", 0xD3D3D3},
{"LightPink", 0xFFB6C1},
{"LightSalmon", 0xFFA07A},
{"LightSeaGreen", 0x20B2AA},
{"LightSkyBlue", 0x87CEFA},
{"LightSlateGray", 0x778899},
{"LightSteelBlue", 0xB0C4DE},
{"LightYellow", 0xFFFFE0},
{"Lime", 0x00FF00},
{"LimeGreen", 0x32CD32},
{"Linen", 0xFAF0E6},
{"Magenta", 0xFF00FF},
{"Maroon", 0x800000},
{"MediumAquamarine", 0x66CDAA},
{"MediumBlue", 0x0000CD},
{"MediumOrchid", 0xBA55D3},
{"MediumPurple", 0x9370DB},
{"MediumSeaGreen", 0x3CB371},
{"MediumSlateBlue", 0x7B68EE},
{"MediumSpringGreen", 0x00FA9A},
{"MediumTurquoise", 0x48D1CC},
{"MediumVioletRed", 0xC71585},
{"MidnightBlue", 0x191970},
{"MintCream", 0xF5FFFA},
{"MistyRose", 0xFFE4E1},
{"Moccasin", 0xFFE4B5},
{"NavajoWhite", 0xFFDEAD},
{"Navy", 0x80},
{"OldLace", 0xFDF5E6},
{"Olive", 0x808000},
{"OliveDrab", 0x6B8E23},
{"Orange", 0xFFA500},
{"OrangeRed", 0xFF4500},
{"Orchid", 0xDA70D6},
{"PaleGoldenrod", 0xEEE8AA},
{"PaleGreen", 0x98FB98},
{"PaleTurquoise", 0xAFEEEE},
{"PaleVioletRed", 0xDB7093},
{"PapayaWhip", 0xFFEFD5},
{"PeachPuff", 0xFFDAB9},
{"Peru", 0xCD853F},
{"Pink", 0xFFC0CB},
{"Plum", 0xDDA0DD},
{"PowderBlue", 0xB0E0E6},
{"Purple", 0x800080},
{"RebeccaPurple", 0x663399},
{"Red", 0xFF0000},
{"RosyBrown", 0xBC8F8F},
{"RoyalBlue", 0x41690},
{"SaddleBrown", 0x8B4513},
{"Salmon", 0xFA8072},
{"SandyBrown", 0xF4A460},
{"SeaGreen", 0x2E8B57},
{"SeaShell", 0xFFF5EE},
{"Sienna", 0xA0522D},
{"Silver", 0xC0C0C0},
{"SkyBlue", 0x87CEEB},
{"SlateBlue", 0x6A5ACD},
{"SlateGray", 0x708090},
{"Snow", 0xFFFAFA},
{"SpringGreen", 0x00FF7F},
{"SteelBlue", 0x4682B4},
{"Tan", 0xD2B48C},
{"Teal", 0x8080},
{"Thistle", 0xD8BFD8},
{"Tomato", 0xFF6347},
{"Turquoise", 0x40E0D0},
{"Violet", 0xEE82EE},
{"Wheat", 0xF5DEB3},
{"White", 0xFFFFFF},
{"WhiteSmoke", 0xF5F5F5},
{"Yellow", 0xFFFF00},
{"YellowGreen", 0x9ACD32},
};
private static readonly Dictionary<int, string> ReverseKnownColors = new Dictionary<int, string>();
static KalkColorRgb()
{
foreach (var knownColor in KnownColors)
{
if (!ReverseKnownColors.ContainsKey(knownColor.Value))
{
ReverseKnownColors.Add(knownColor.Value, knownColor.Key);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Kalk.Core
{
public class KalkColorRgbConstructor : KalkColorConstructor
{
public KalkColorRgbConstructor() : base(3)
{
}
protected override KalkVector<byte> NewVector(int dimension) => new KalkColorRgb();
}
}

View File

@@ -0,0 +1,42 @@
using Scriban.Syntax;
namespace Kalk.Core
{
[ScriptTypeName("rgba")]
public class KalkColorRgba : KalkColor
{
public KalkColorRgba() : base(4)
{
}
public KalkColorRgba(byte r, byte g, byte b, byte a) : this()
{
this[0] = r;
this[1] = g;
this[2] = b;
this[3] = a;
}
public KalkColorRgba(int rgb) : this()
{
this[0] = (byte)((rgb >> 16) & 0xFF);
this[1] = (byte)((rgb >> 8) & 0xFF);
this[2] = (byte)(rgb & 0xFF);
this[3] = (byte)((rgb >> 24) & 0xFF);
}
public KalkColorRgba(KalkColorRgba values) : base(values)
{
}
public override string TypeName => "rgba";
public override KalkVector Clone()
{
return new KalkColorRgba(this);
}
protected override KalkVector<byte> NewVector(int length) => new KalkColorRgba();
}
}

View File

@@ -0,0 +1,11 @@
namespace Kalk.Core
{
public class KalkColorRgbaConstructor : KalkColorConstructor
{
public KalkColorRgbaConstructor() : base(4)
{
}
protected override KalkVector<byte> NewVector(int dimension) => new KalkColorRgba();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,159 @@
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using Kalk.Core.Helpers;
using Scriban;
using Scriban.Helpers;
using Scriban.Syntax;
namespace Kalk.Core
{
public class KalkVectorConstructor<T> : KalkConstructor where T : unmanaged
{
public KalkVectorConstructor(int dimension)
{
Dimension = dimension;
}
public int Dimension { get; }
public KalkVector<T> Invoke(TemplateContext context, object[] arguments)
{
if (arguments.Length == 0)
{
return NewVector(Dimension);
}
var vector = NewVector(Dimension);
int index = 0;
if (arguments.Length == 1)
{
var arg = arguments[0];
// Replace implicitly Rgb/Rgba to xyzw
if (arg is KalkColor color)
{
if (this is KalkColorConstructor)
{
var colorLength = color is KalkColorRgb ? 3 : 4;
if (Dimension != colorLength)
{
if (Dimension == 3) // 4 to 3
{
arg = new KalkVector<byte>(color.r, color.g, color.b);
}
else // 3 to 4
{
Debug.Assert(Dimension == 4);
arg = new KalkVector<byte>(color.r, color.g, color.b, 255);
}
}
}
else
{
arg = color.GetFloatVector(Dimension);
}
}
var argLength = GetArgLength(arg, true);
var length = index + argLength;
if (length != Dimension)
{
throw new ScriptArgumentException(0, $"Invalid number of arguments for {vector.TypeName}. Expecting {Dimension} arguments instead of {length}.");
}
ProcessSingleArgument(context, ref index, arg, vector);
}
else
{
for (var i = 0; i < arguments.Length; i++)
{
var arg = arguments[i];
var argLength = GetArgLength(arg, false);
var length = index + argLength;
if (length > Dimension)
{
throw new ScriptArgumentException(i, $"Invalid number of arguments for {vector.TypeName}. Expecting {Dimension} arguments instead of {length}.");
}
ProcessArgument(context, ref index, arg, vector);
}
}
if (index != Dimension)
{
throw new ScriptArgumentException(arguments.Length - 1, $"Invalid number of arguments for {vector.TypeName}. Expecting {Dimension} arguments instead of {index}.");
}
return vector;
}
private int GetArgLength(object arg, bool isSingleArg)
{
if (arg is IList list)
{
int argLength = 0;
for (int i = 0; i < list.Count; i++)
{
argLength += GetArgLength(list[i], false);
}
return argLength;
}
return isSingleArg ? Dimension : 1;
}
private void AddListItem(TemplateContext context, ref int index, object arg, KalkVector<T> vector)
{
if (arg is IList list)
{
var count = list.Count;
for (int j = 0; j < count; j++)
{
AddListItem(context, ref index, list[j], vector);
}
}
else
{
var value = GetArgumentValue(context, arg);
vector[index++] = value;
}
}
protected virtual void ProcessSingleArgument(TemplateContext context, ref int index, object arg, KalkVector<T> vector)
{
if (arg is IList list)
{
AddListItem(context, ref index, list, vector);
}
else
{
var value = GetArgumentValue(context, arg);
for (int j = 0; j < Dimension; j++)
{
vector[index++] = value;
}
}
}
protected void ProcessArgument(TemplateContext context, ref int index, object arg, KalkVector<T> vector)
{
if (arg is IList list)
{
AddListItem(context, ref index, list, vector);
}
else
{
var value = GetArgumentValue(context, arg);
vector[index++] = value;
}
}
protected virtual KalkVector<T> NewVector(int dimension) => new KalkVector<T>(dimension);
protected virtual T GetArgumentValue(TemplateContext context, object value)
{
return context.ToObject<T>(context.CurrentSpan, value);
}
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using Scriban;
using Scriban.Parsing;
using Scriban.Runtime;
using Scriban.Syntax;
namespace Kalk.Core
{
public class PrimitiveSwizzleAccessor : IObjectAccessor
{
public static readonly PrimitiveSwizzleAccessor Default = new PrimitiveSwizzleAccessor();
private PrimitiveSwizzleAccessor()
{
}
public int GetMemberCount(TemplateContext context, SourceSpan span, object target)
{
return 0;
}
public IEnumerable<string> GetMembers(TemplateContext context, SourceSpan span, object target)
{
yield break;
}
public bool HasMember(TemplateContext context, SourceSpan span, object target, string member)
{
return IsSwizzle(member);
}
public bool TryGetValue(TemplateContext context, SourceSpan span, object target, string member, out object value)
{
var targetFloat = context.ToObject<float>(span, target);
if (member.Length == 1)
{
value = targetFloat;
return true;
}
var vector = new KalkVector<float>(member.Length);
int index = 0;
for(int i = 0; i < member.Length; i++)
{
var c = member[i];
switch (c)
{
case 'x':
vector[index] = targetFloat;
break;
case 'y':
vector[index] = targetFloat;
break;
case 'z':
vector[index] = targetFloat;
break;
case 'w':
vector[index] = targetFloat;
break;
}
index++;
}
value = vector;
return true;
}
public bool TrySetValue(TemplateContext context, SourceSpan span, object target, string member, object value)
{
throw new ScriptRuntimeException(span, "Cannot set a member on a primitive");
}
public bool TryGetItem(TemplateContext context, SourceSpan span, object target, object index, out object value)
{
throw new NotImplementedException();
}
public bool TrySetItem(TemplateContext context, SourceSpan span, object target, object index, object value)
{
throw new NotImplementedException();
}
public bool HasIndexer => false;
public Type IndexType => typeof(int);
private static bool IsSwizzle(string text)
{
if (text.Length > 4) return false;
for (int i = 0; i < text.Length; i++)
{
var c = text[i];
switch (c)
{
case 'x':
case 'y':
case 'z':
case 'w':
break;
default:
return false;
}
}
return true;
}
}
}

View File

@@ -0,0 +1,538 @@
using System;
using Scriban.Syntax;
namespace Kalk.Core.Modules
{
public sealed partial class TypesModule : KalkModuleWithFunctions
{
public const string CategoryTypeConstructors = "Type Constructors";
public const string CategoryVectorTypeConstructors = "Type Vector Constructors";
private static readonly KalkColorRgbConstructor RgbConstructor = new KalkColorRgbConstructor();
private static readonly KalkColorRgbaConstructor RgbaConstructor = new KalkColorRgbaConstructor();
public TypesModule() : base("Types")
{
IsBuiltin = true;
RegisterFunctionsAuto();
}
/// <summary>
/// Creates an unsigned byte value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>An unsigned byte value</returns>
/// <example>
/// ```kalk
/// >>> byte
/// # byte
/// out = 0
/// >>> byte 0
/// # byte(0)
/// out = 0
/// >>> byte 255
/// # byte(255)
/// out = 255
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> byte 256
/// Unable to convert type `int` to `byte`
/// ```
/// </test>
[KalkExport("byte", CategoryTypeConstructors)]
public byte CreateByte(object value = null) => value == null ? (byte)0 : Engine.ToObject<byte>(0, value);
/// <summary>
/// Creates a signed-byte value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A signed-byte value</returns>
/// <example>
/// ```kalk
/// >>> sbyte
/// # sbyte
/// out = 0
/// >>> sbyte 0
/// # sbyte(0)
/// out = 0
/// >>> sbyte 127
/// # sbyte(127)
/// out = 127
/// >>> sbyte(-128)
/// # sbyte(-128)
/// out = -128
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> sbyte 128
/// Unable to convert type `int` to `sbyte`
/// ```
/// </test>
[KalkExport("sbyte", CategoryTypeConstructors)]
public sbyte CreateSByte(object value = null) => value == null ? (sbyte)0 : Engine.ToObject<sbyte>(0, value);
/// <summary>
/// Creates a signed-short (16-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A signed-short (16-bit) value</returns>
/// <example>
/// ```kalk
/// >>> short
/// # short
/// out = 0
/// >>> short 0
/// # short(0)
/// out = 0
/// >>> short 32767
/// # short(32767)
/// out = 32_767
/// >>> short(-32768)
/// # short(-32768)
/// out = -32_768
/// >>> short 32768
/// Unable to convert type `int` to `short`
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> short 32768
/// Unable to convert type `int` to `short`
/// ```
/// </test>
[KalkExport("short", CategoryTypeConstructors)]
public short CreateShort(object value = null) => value == null ? (short)0 : Engine.ToObject<short>(0, value);
/// <summary>
/// Creates an unsigned short (16-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>An unsigned short (16-bit) value</returns>
/// <example>
/// ```kalk
/// >>> ushort
/// # ushort
/// out = 0
/// >>> ushort 0
/// # ushort(0)
/// out = 0
/// >>> ushort 65535
/// # ushort(65535)
/// out = 65_535
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> ushort 65536
/// Unable to convert type `int` to `ushort`
/// ```
/// </test>
[KalkExport("ushort", CategoryTypeConstructors)]
public ushort CreateUShort(object value = null) => value == null ? (ushort)0 : Engine.ToObject<ushort>(0, value);
/// <summary>
/// Creates an unsigned int (32-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>An unsigned int (32-bit) value</returns>
/// <example>
/// ```kalk
/// >>> uint
/// # uint
/// out = 0
/// >>> uint 0
/// # uint(0)
/// out = 0
/// >>> uint(1&lt;&lt;32 - 1)
/// # uint(1 &lt;&lt; 32 - 1)
/// out = 4_294_967_295
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> uint 1 &lt;&lt; 32
/// Unable to convert type `long` to `uint`
/// ```
/// </test>
[KalkExport("uint", CategoryTypeConstructors)]
public uint CreateUInt(object value = null) => value == null ? 0U : Engine.ToObject<uint>(0, value);
/// <summary>
/// Creates a signed-int (32-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A signed-int (32-bit) value</returns>
/// <example>
/// ```kalk
/// >>> int
/// # int
/// out = 0
/// >>> int 0
/// # int(0)
/// out = 0
/// >>> int(1 &lt;&lt; 31 - 1)
/// # int(1 &lt;&lt; 31 - 1)
/// out = 2_147_483_647
/// >>> int(-(1&lt;&lt;31))
/// # int(-(1 &lt;&lt; 31))
/// out = -2_147_483_648
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> int 1 &lt;&lt; 31
/// Unable to convert type `long` to int
/// ```
/// </test>
[KalkExport("int", CategoryTypeConstructors)]
public int CreateInt(object value = null) => value == null ? 0 : Engine.ToObject<int>(0, value);
/// <summary>
/// Creates an unsigned long (64-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>An unsigned long (64-bit) value</returns>
/// <example>
/// ```kalk
/// >>> ulong
/// # ulong
/// out = 0
/// >>> ulong 0
/// # ulong(0)
/// out = 0
/// >>> ulong(1 &lt;&lt; 64 - 1)
/// # ulong(1 &lt;&lt; 64 - 1)
/// out = 18_446_744_073_709_551_615
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> ulong 1 &lt;&lt; 64
/// Unable to convert type `bigint` to `ulong`
/// ```
/// </test>
[KalkExport("ulong", CategoryTypeConstructors)]
public ulong CreateULong(object value = null) => value == null ? 0UL : Engine.ToObject<ulong>(0, value);
/// <summary>
/// Creates a signed-long (64-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A signed-long (64-bit) value</returns>
/// <example>
/// ```kalk
/// >>> long
/// # long
/// out = 0
/// >>> long 0
/// # long(0)
/// out = 0
/// >>> long(1 &lt;&lt; 63 - 1)
/// # long(1 &lt;&lt; 63 - 1)
/// out = 9_223_372_036_854_775_807
/// >>> long(-(1&lt;&lt;63))
/// # long(-(1 &lt;&lt; 63))
/// out = -9_223_372_036_854_775_808
/// ```
/// </example>
/// <test>
/// ```kalk
/// >>> long 1 &lt;&lt; 63
/// Unable to convert type `bigint` to `long`
/// ```
/// </test>
[KalkExport("long", CategoryTypeConstructors)]
public long CreateLong(object value = null) => value == null ? 0L : Engine.ToObject<long>(0, value);
/// <summary>
/// Creates a boolean value (32-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A boolean (32-bit) value</returns>
/// <example>
/// ```kalk
/// >>> bool 1
/// # bool(1)
/// out = true
/// >>> bool 0
/// # bool(0)
/// out = false
/// >>> bool true
/// # bool(true)
/// out = true
/// >>> bool false
/// # bool(false)
/// out = false
/// ```
/// </example>
[KalkExport("bool", CategoryTypeConstructors)]
public KalkBool CreateBool(object value = null) => value != null && Engine.ToObject<KalkBool>(0, value);
/// <summary>
/// Creates a float value (32-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A float (32-bit) value</returns>
/// <example>
/// ```kalk
/// >>> float(1)
/// # float(1)
/// out = 1
/// >>> float(-1)
/// # float(-1)
/// out = -1
/// >>> float(100000000000)
/// # float(100000000000)
/// out = 1E+11
/// ```
/// </example>
[KalkExport("float", CategoryTypeConstructors)]
public float CreateFloat(object value = null) => value == null ? 0.0f : Engine.ToObject<float>(0, value);
/// <summary>
/// Creates a half float value (16-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A half float (16-bit) value</returns>
/// <example>
/// ```kalk
/// >>> half(1)
/// # half(1)
/// out = 1
/// >>> half(-1)
/// # half(-1)
/// out = -1
/// >>> half(1000.5)
/// # half(1000.5)
/// out = 1000.5
/// >>> kind out
/// # kind(out)
/// out = "half"
/// ```
/// </example>
[KalkExport("half", CategoryTypeConstructors)]
public KalkHalf CreateHalf(object value = null) => value == null ? (KalkHalf)0.0f : Engine.ToObject<KalkHalf>(0, value);
/// <summary>
/// Creates a double value (64-bit) value.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>A double (64-bit) value</returns>
/// <example>
/// ```kalk
/// >>> double(1)
/// # double(1)
/// out = 1
/// >>> double(-1)
/// # double(-1)
/// out = -1
/// >>> double(100000000000)
/// # double(100000000000)
/// out = 100000000000
/// >>> double(1&lt;&lt;200)
/// # double(1 &lt;&lt; 200)
/// out = 1.6069380442589903E+60
/// ```
/// </example>
[KalkExport("double", CategoryTypeConstructors)]
public double CreateDouble(object value = null) => value == null ? 0.0 : Engine.ToObject<double>(0, value);
/// <summary>
/// Creates a vector of the specified element type, with the number of elements and optional values.
/// </summary>
/// <param name="name">The element type of the vector (e.g float).</param>
/// <param name="dimension">The dimension of the vector.</param>
/// <param name="arguments">The optional values (must have 1 or dimension elements).</param>
/// <returns>A matrix of the specified row x column.</returns>
/// <example>
/// ```kalk
/// >>> vector(float, 4, 5..8)
/// # vector(float, 4, 5..8)
/// out = float4(5, 6, 7, 8)
/// ```
/// </example>
[KalkExport("vector", CategoryVectorTypeConstructors)]
public object CreateVector(ScriptVariable name, int dimension, params object[] arguments)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (dimension <= 1) throw new ArgumentOutOfRangeException(nameof(dimension), "Invalid dimension. Expecting a value > 1.");
switch (name.Name)
{
case "int":
switch (dimension)
{
case 2: return CreateInt2(arguments);
case 3: return CreateInt3(arguments);
case 4: return CreateInt4(arguments);
case 8: return CreateInt8(arguments);
case 16: return CreateInt16(arguments);
}
return new KalkVectorConstructor<int>(dimension).Invoke(Engine, arguments);
case "bool":
switch (dimension)
{
case 2: return CreateBool2(arguments);
case 3: return CreateBool3(arguments);
case 4: return CreateBool4(arguments);
case 8: return CreateBool8(arguments);
case 16: return CreateBool16(arguments);
}
return new KalkVectorConstructor<KalkBool>(dimension).Invoke(Engine, arguments);
case "float":
switch (dimension)
{
case 2: return CreateFloat2(arguments);
case 3: return CreateFloat3(arguments);
case 4: return CreateFloat4(arguments);
case 8: return CreateFloat8(arguments);
case 16: return CreateFloat16(arguments);
}
return new KalkVectorConstructor<float>(dimension).Invoke(Engine, arguments);
case "half":
switch (dimension)
{
case 2: return CreateHalf2(arguments);
case 3: return CreateHalf3(arguments);
case 4: return CreateHalf4(arguments);
case 8: return CreateHalf8(arguments);
case 16: return CreateHalf16(arguments);
case 32: return CreateHalf32(arguments);
}
return new KalkVectorConstructor<KalkHalf>(dimension).Invoke(Engine, arguments);
case "double":
switch (dimension)
{
case 2: return CreateDouble2(arguments);
case 3: return CreateDouble3(arguments);
case 4: return CreateDouble4(arguments);
case 8: return CreateDouble8(arguments);
}
return new KalkVectorConstructor<double>(dimension).Invoke(Engine, arguments);
}
throw new ArgumentException($"Unsupported vector type {name.Name}. Only bool, int, float and double are supported", nameof(name));
}
/// <summary>
/// Creates an rgb vector type with the specified argument values.
/// </summary>
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector (3). The arguments can be:
/// - No values: All items of the rgb vector are initialized with the value 0.
/// - an integer value: `rgb(0xAABBCC)` will extract the RGB 8-bits component values (AA: R, BB: G, CC: B).
/// - a string value: `rgb("#AABBCC")` or `rgb("AABBCC")` will extract the RGB 8-bits component values (AA: R, BB: G, CC: B).
/// - an array value: `rgb([0xAA,0xBB,0xCC])` will initialize rgb elements with the array elements. The size of the array must match the size of the rgb vector (3).
/// - A combination of vectors/single values (e.g `rgb(float3(0.1, 0.2, 0.3)`).
/// </param>
/// <returns>A rgb vector initialized with the specified arguments</returns>
/// <example>
/// ```kalk
/// >>> rgb(0xAABBCC)
/// # rgb(11189196)
/// out = rgb(170, 187, 204) ## AABBCC ##
/// >>> rgb("#AABBCC")
/// # rgb("#AABBCC")
/// out = rgb(170, 187, 204) ## AABBCC ##
/// >>> rgb("AABBCC")
/// # rgb("AABBCC")
/// out = rgb(170, 187, 204) ## AABBCC ##
/// >>> rgb([0xAA,0xBB,0xCC])
/// # rgb([170,187,204])
/// out = rgb(170, 187, 204) ## AABBCC ##
/// >>> out.xyz
/// # out.xyz
/// out = float3(0.6666667, 0.73333335, 0.8)
/// >>> rgb(out)
/// # rgb(out)
/// out = rgb(170, 187, 204) ## AABBCC ##
/// ```
/// </example>
[KalkExport("rgb", CategoryVectorTypeConstructors)]
public KalkColorRgb CreateRgb(params object[] arguments) => (KalkColorRgb)RgbConstructor.Invoke(Engine, arguments);
/// <summary>
/// Creates an rgba vector type with the specified argument values.
/// </summary>
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector (4). The arguments can be:
/// - No values: All items of the rgba vector are initialized with the value 0.
/// - an integer value: `rgba(0xFFAABBCC)` will extract the RGB 8-bits component values (FF: A, AA: R, BB: G, CC: B).
/// - a string value: `rgba("#FFAABBCC")` or `rgba("FFAABBCC")` will extract the RGB 8-bits component values (FF: A, AA: R, BB: G, CC: B).
/// - an array value: `rgba([0xAA,0xBB,0xCC,0xFF])` will initialize rgba elements with the array elements. The size of the array must match the size of the rgb vector (3).
/// - A combination of vectors/single values (e.g `rgba(float4(0.1, 0.2, 0.3, 1.0)`).
/// </param>
/// <returns>A rgb vector initialized with the specified arguments</returns>
/// <example>
/// ```kalk
/// >>> rgba(0xFFAABBCC)
/// # rgba(-5588020)
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
/// >>> rgba("#FFAABBCC")
/// # rgba("#FFAABBCC")
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
/// >>> rgba("FFAABBCC")
/// # rgba("FFAABBCC")
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
/// >>> rgba([0xAA,0xBB,0xCC,0xFF])
/// # rgba([170,187,204,255])
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
/// >>> out.xyzw
/// # out.xyzw
/// out = float4(0.6666667, 0.73333335, 0.8, 1)
/// >>> rgba(out)
/// # rgba(out)
/// out = rgba(170, 187, 204, 255) ## AABBCCFF ##
/// ```
/// </example>
[KalkExport("rgba", CategoryVectorTypeConstructors)]
public KalkColorRgba CreateRgba(params object[] arguments) => (KalkColorRgba)RgbaConstructor.Invoke(Engine, arguments);
/// <summary>
/// Creates a matrix of the specified element type, number of rows and columns and optional values.
/// </summary>
/// <param name="name">The element type of the matrix (e.g float).</param>
/// <param name="row">The number of rows.</param>
/// <param name="column">The number of columns.</param>
/// <param name="arguments">The optional values (must have 1 or row x column elements).</param>
/// <returns>A matrix of the specified row x column.</returns>
/// <example>
/// ```kalk
/// >>> matrix(float,4,3,1..12)
/// # matrix(float, 4, 3, 1..12)
/// out = float4x3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
/// # col 0 1 2 / row
/// float3(1 , 2 , 3 ) # 0
/// float3(4 , 5 , 6 ) # 1
/// float3(7 , 8 , 9 ) # 2
/// float3(10 , 11 , 12 ) # 3
/// ```
/// </example>
[KalkExport("matrix", CategoryMatrixConstructors)]
public object CreateMatrix(ScriptVariable name, int row, int column, params object[] arguments)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (row <= 1) throw new ArgumentOutOfRangeException(nameof(row), $"Invalid row count {row}. Expecting a value > 1.");
if (column <= 1) throw new ArgumentOutOfRangeException(nameof(column), $"Invalid column count {column}. Expecting a value > 1.");
switch (name.Name)
{
case "int":
return new KalkMatrixConstructor<int>(row, column).Invoke(Engine, arguments);
case "bool":
return new KalkMatrixConstructor<KalkBool>(row, column).Invoke(Engine, arguments);
case "float":
return new KalkMatrixConstructor<float>(row, column).Invoke(Engine, arguments);
case "half":
return new KalkMatrixConstructor<KalkHalf>(row, column).Invoke(Engine, arguments);
case "double":
return new KalkMatrixConstructor<double>(row, column).Invoke(Engine, arguments);
}
throw new ArgumentException($"Unsupported matrix type {name.Name}. Only bool, int, float, half and double are supported", nameof(name));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,131 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".generated.cs" #>
<#
var types = new List<(string, string, int, int, string)>();
foreach(var typeU in new string[] { "Bool", "Int", "Float", "Double", "Half" })
{
var type = typeU.ToLowerInvariant();
for(int y = 2; y <= 4; y++) {
for(int x = 2; x <= 4; x++) {
var genericType = typeU == "Bool" ? "KalkBool" : typeU == "Half" ? "KalkHalf" : type;
types.Add((type, $"{typeU}{y}x{x}", y, x, genericType));
}
}
}
var vectypes = new List<(string, string, string, int[])>() {
("byte", "Byte", "byte", new int[] {16,32,64}),
("sbyte", "SByte", "sbyte", new int[] {16,32,64}),
("short", "Short", "short", new int[] {2,4,8,16,32}),
("ushort", "UShort", "ushort", new int[] {2,4,8,16,32}),
("int", "Int", "int", new int[] {2,3,4,8,16}),
("uint", "UInt", "uint", new int[] {2,3,4,8,16}),
("long", "Long", "long", new int[] {2,3,4,8}),
("ulong", "ULong", "ulong", new int[] {2,3,4,8}),
("KalkBool", "Bool", "bool", new int[] {2,3,4,8,16}),
("float", "Float", "float", new int[] {2,3,4,8,16}),
("double", "Double", "double", new int[] {2,3,4,8}),
("KalkHalf", "Half", "half", new int[] {2,3,4,8,16,32}),
};
#>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Date: <#= DateTime.Now #>
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using Scriban.Helpers;
using Scriban.Syntax;
namespace Kalk.Core.Modules
{
public partial class TypesModule
{
private const string CategoryMatrixConstructors = "Type Matrix Constructors";
<# foreach(var type in vectypes) { #>
<# foreach(var dim in type.Item4) { #>
private static readonly KalkVectorConstructor<<#= type.Item1 #>> <#= type.Item2 #><#= dim #>Constructor = new KalkVectorConstructor<<#= type.Item1 #>>(<#= dim #>);
<# } #>
<# } #>
<# foreach(var type in types) { #>
private static readonly KalkMatrixConstructor<<#= type.Item5 #>> <#= type.Item2 #>Constructor = new KalkMatrixConstructor<<#= type.Item5 #>>(<#= type.Item3 #>, <#= type.Item4 #>);
<# } #>
<# foreach(var type in vectypes) { #>
<# foreach(var dim in type.Item4) {
var ctor = $"{type.Item3}{dim}";
if (type.Item3 == "bool") { #>
/// <summary>Creates a vector of <#= dim #> `<#= type.Item3 #>` items.</summary>
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector. The arguments can be:
/// - No values: The vector is initialized with false values.
/// - a single value: `<#= ctor #>(true)` will initialize all elements with 123.
/// - an array value: `<#= ctor #>([true, false, ...])` will initialize all elements with the array elements. The size of the array must match the size of the vector.
/// - A combination of vectors/single values (e.g `<#= type.Item3 #>4(<#= type.Item3 #>2(true,false), <#= type.Item3 #>2(false,true))` or `<#= type.Item3 #>4(<#= type.Item3 #>3(false,true,true), false)`)
/// </param>
/// <returns>A <#= ctor #> vector initialized with the specified arguments</returns>
/// <example>
/// ```kalk
/// >>> <#= ctor #>(true)
/// # <#= ctor #>(true)
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(1, dim).Select(x => "true")) #>)
<# } else { #>
/// <summary>Creates a vector of <#= dim #> `<#= type.Item3 #>` items.</summary>
/// <param name="arguments">The vector item values. The total number of values must equal the dimension of the vector. The arguments can be:
/// - No values: All items of the vector are initialized with the value 0.
/// - a single value: `<#= ctor #>(123)` will initialize all elements with 123.
/// - an array value: `<#= ctor #>(1..<#= dim #>)` will initialize all elements with the array elements. The size of the array must match the size of the vector.
/// - A combination of vectors/single values (e.g `<#= type.Item3 #>4(<#= type.Item3 #>2(1,2), <#= type.Item3 #>2(3,4))` or `<#= type.Item3 #>4(<#= type.Item3 #>3(1,2,3), 4)`.
/// </param>
/// <returns>A <#= ctor #> vector initialized with the specified arguments</returns>
/// <example>
/// ```kalk
/// >>> <#= ctor #>
/// # <#= ctor #>
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(1, dim).Select(x => "0")) #>)
/// >>> <#= ctor #>(1..<#= dim #>)
/// # <#= ctor #>(1..<#= dim #>)
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(1, dim)) #>)
<# if (dim <= 16) { #>
/// >>> <#= ctor #>(<#= string.Join(", ", Enumerable.Range(10, dim)) #>)
/// # <#= ctor #>(<#= string.Join(", ", Enumerable.Range(10, dim)) #>)
/// out = <#= ctor #>(<#= string.Join(", ", Enumerable.Range(10, dim)) #>)
<# }
}
#>
/// ```
/// </example>
[KalkExport("<#= ctor #>", CategoryVectorTypeConstructors)]
public KalkVector<<#= type.Item1 #>> Create<#= type.Item2 #><#= dim #>(params object[] arguments) => <#= type.Item2 #><#= dim #>Constructor.Invoke(Engine, arguments);
<# }
} #>
<# foreach(var type in types) {
var dim = type.Item3 * type.Item4;
var ctor_prefix = type.Item1.ToLowerInvariant();
var ctor = type.Item2.ToLowerInvariant();
#>
/// <summary>Creates a <#= type.Item3 #> (rows) x <#= type.Item4 #> (columns) matrix of <#= type.Item1 #>.</summary>
/// <param name="arguments">The matrix item values. The total number of values must equal the total dimension of the matrix. The arguments can be:
/// - No values: All items of the vector are initialized with the value 0.
/// - a single value: `<#= ctor #>(123)` will initialize all elements with 123.
/// - an array value: `<#= ctor #>(1..<#= dim #>)` will initialize all elements with the array elements. The size of the array must match the size of the vector.
/// - A combination of vectors/single values (e.g `<#= ctor_prefix #>3x4(<#= ctor_prefix #>4(1), <#= ctor_prefix #>4(2), <#= ctor_prefix #>4(3))`.
/// </param>
/// <returns>A <#= ctor #> matrix initialized with the specified arguments</returns>
[KalkExport("<#= ctor #>", CategoryMatrixConstructors)]
public KalkMatrix<<#= type.Item5 #>> Create<#= type.Item2 #>(params object[] arguments) => <#= type.Item2 #>Constructor.Invoke(Engine, arguments);
<# } #>
}
}

View File

@@ -0,0 +1,228 @@
using System;
using Scriban.Syntax;
namespace Kalk.Core.Modules
{
public partial class VectorModule
{
/// <summary>
/// Transposes the specified matrix.
/// </summary>
/// <param name="m">The matrix to transpose.</param>
/// <returns>The transposed matrix.</returns>
/// <example>
/// ```kalk
/// >>> transpose float3x4(1..12)
/// # transpose(float3x4(1..12))
/// out = float4x3(1, 5, 9, 2, 6, 10, 3, 7, 11, 4, 8, 12)
/// # col 0 1 2 / row
/// float3(1 , 5 , 9 ) # 0
/// float3(2 , 6 , 10 ) # 1
/// float3(3 , 7 , 11 ) # 2
/// float3(4 , 8 , 12 ) # 3
/// >>> transpose(out)
/// # transpose(out)
/// out = float3x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
/// # col 0 1 2 3 / row
/// float4(1 , 2 , 3 , 4 ) # 0
/// float4(5 , 6 , 7 , 8 ) # 1
/// float4(9 , 10 , 11 , 12 ) # 2
/// ```
/// </example>
[KalkExport("transpose", CategoryMathVectorMatrixFunctions)]
public static KalkMatrix Transpose(KalkMatrix m) => KalkMatrix.Transpose(m);
/// <summary>
/// Creates an identity of a squared matrix.
/// </summary>
/// <param name="m">The type of the squared matrix.</param>
/// <returns>The identity matrix of the squared matrix type.</returns>
/// <example>
/// ```kalk
/// >>> identity(float4x4)
/// # identity(float4x4)
/// out = float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
/// # col 0 1 2 3 / row
/// float4(1 , 0 , 0 , 0 ) # 0
/// float4(0 , 1 , 0 , 0 ) # 1
/// float4(0 , 0 , 1 , 0 ) # 2
/// float4(0 , 0 , 0 , 1 ) # 3
/// ```
/// </example>
[KalkExport("identity", CategoryMathVectorMatrixFunctions)]
public static KalkMatrix Identity(KalkMatrix m) => KalkMatrix.Identity(m);
/// <summary>
/// Calculates the determinant of the specified matrix.
/// </summary>
/// <param name="m">The matrix to calculate the determinant for.</param>
/// <returns>A scalar representing the determinant of the matrix.</returns>
/// <example>
/// ```kalk
/// >>> float4x4(4,3,2,2,0,1,-3,3,0,-1,3,3,0,3,1,1)
/// # float4x4(4, 3, 2, 2, 0, 1, -3, 3, 0, -1, 3, 3, 0, 3, 1, 1)
/// out = float4x4(4, 3, 2, 2, 0, 1, -3, 3, 0, -1, 3, 3, 0, 3, 1, 1)
/// # col 0 1 2 3 / row
/// float4(4 , 3 , 2 , 2 ) # 0
/// float4(0 , 1 , -3 , 3 ) # 1
/// float4(0 , -1 , 3 , 3 ) # 2
/// float4(0 , 3 , 1 , 1 ) # 3
/// >>> determinant out
/// # determinant(out)
/// out = -240
/// ```
/// </example>
[KalkExport("determinant", CategoryMathVectorMatrixFunctions)]
public static object Determinant(KalkMatrix m) => KalkMatrix.Determinant(m);
/// <summary>
/// Calculates the inverse of the specified matrix.
/// </summary>
/// <param name="m">The matrix to calculate the inverse for.</param>
/// <returns>The inverse matrix of the specified matrix.</returns>
/// <example>
/// ```kalk
/// >>> inverse(float3x3(10,20,10,4,5,6,2,3,5))
/// # inverse(float3x3(10, 20, 10, 4, 5, 6, 2, 3, 5))
/// out = float3x3(-0.1, 1, -1, 0.11428571, -0.42857143, 0.28571427, -0.028571427, -0.14285715, 0.42857143)
/// # col 0 1 2 / row
/// float3(-0.1 , 1 , -1 ) # 0
/// float3( 0.11428571 , -0.42857143, 0.28571427) # 1
/// float3(-0.028571427, -0.14285715, 0.42857143) # 2
/// ```
/// </example>
[KalkExport("inverse", CategoryMathVectorMatrixFunctions)]
public static KalkMatrix Inverse(KalkMatrix m) => KalkMatrix.Inverse(m);
/// <summary>
/// Returns the diagonal vector of a squared matrix or a diagonal matrix from the specified vector.
/// </summary>
/// <param name="x">A vector or matrix to return the associated diagonal for.</param>
/// <returns>A diagonal vector of a matrix or a diagonal matrix of a vector.</returns>
/// <example>
/// ```kalk
/// >>> diag(float4x4(1..16))
/// # diag(float4x4(1..16))
/// out = float4(1, 6, 11, 16)
/// >>> diag(float4(1,2,3,4))
/// # diag(float4(1, 2, 3, 4))
/// out = float4x4(1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4)
/// # col 0 1 2 3 / row
/// float4(1 , 0 , 0 , 0 ) # 0
/// float4(0 , 2 , 0 , 0 ) # 1
/// float4(0 , 0 , 3 , 0 ) # 2
/// float4(0 , 0 , 0 , 4 ) # 3
///
/// ```
/// </example>
[KalkExport("diag", CategoryMathVectorMatrixFunctions)]
public object Diagonal(object x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (x is KalkMatrix m)
{
return KalkMatrix.Diagonal(m);
}
if (x is KalkVector v)
{
return KalkVector.Diagonal(v);
}
throw new ArgumentException($"Invalid argument type {Engine.GetTypeName(x)}. Expecting a matrix or a vector type.", nameof(x));
}
/// <summary>
/// Extract a row from the specified matrix.
/// </summary>
/// <param name="x">The matrix to extract a row from.</param>
/// <param name="index">The index of the row (zero based).</param>
/// <returns>A vector extracted from the matrix.</returns>
/// <example>
/// ```kalk
/// >>> row(float4x4(1..16), 2)
/// # row(float4x4(1..16), 2)
/// out = float4(9, 10, 11, 12)
/// ```
/// </example>
[KalkExport("row", CategoryMathVectorMatrixFunctions)]
public KalkVector GetRow(KalkMatrix x, int index)
{
return x.GetRow(index);
}
/// <summary>
/// Extract a column from the specified matrix.
/// </summary>
/// <param name="x">The matrix to extract a column from.</param>
/// <param name="index">The index of the column (zero based).</param>
/// <returns>A vector extracted from the matrix.</returns>
/// <example>
/// ```kalk
/// >>> col(float4x4(1..16), 2)
/// # col(float4x4(1..16), 2)
/// out = float4(3, 7, 11, 15)
/// ```
/// </example>
[KalkExport("col", CategoryMathVectorMatrixFunctions)]
public KalkVector GetColumn(KalkMatrix x, int index)
{
return x.GetColumn(index);
}
/// <summary>
/// Multiplies a vector x vector (dot product), or a vector x matrix, or a matrix x vector or a matrix x matrix.
/// </summary>
/// <param name="x">A left vector or a matrix.</param>
/// <param name="y">A right vector or matrix.</param>
/// <returns>The result of the multiplication.</returns>
/// <example>
/// ```kalk
/// >>> mul(float4(1,2,3,4), float4(5,6,7,8))
/// # mul(float4(1, 2, 3, 4), float4(5, 6, 7, 8))
/// out = 70
/// >>> mul(float3(3,7,5), float3x3(2,3,-4,11,8,7,2,5,3))
/// # mul(float3(3, 7, 5), float3x3(2, 3, -4, 11, 8, 7, 2, 5, 3))
/// out = float3(7, 124, 56)
/// >>> mul(float3x3(2,3,-4,11,8,7,2,5,3), float3(3,7,5))
/// # mul(float3x3(2, 3, -4, 11, 8, 7, 2, 5, 3), float3(3, 7, 5))
/// out = float3(93, 90, 52)
/// >>> mul(float3x3(2,7,4,3,2,1,9,-1,2), float3x3(1,4,6,-1,-2,5,8,7,6))
/// # mul(float3x3(2, 7, 4, 3, 2, 1, 9, -1, 2), float3x3(1, 4, 6, -1, -2, 5, 8, 7, 6))
/// out = float3x3(68, 9, 20, 37, -16, 4, 91, 64, 51)
/// # col 0 1 2 / row
/// float3(68 , 9 , 20 ) # 0
/// float3(37 , -16 , 4 ) # 1
/// float3(91 , 64 , 51 ) # 2
/// ```
/// </example>
[KalkExport("mul", CategoryMathVectorMatrixFunctions)]
public object Multiply(object x, object y)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (y == null) throw new ArgumentNullException(nameof(y));
if (x is KalkVector vx && y is KalkVector vy)
{
return KalkVector.Dot(vx, vy);
}
if (x is KalkVector vx1 && y is KalkMatrix my)
{
return KalkMatrix.Multiply(vx1, my);
}
if (x is KalkMatrix mx && y is KalkVector vy1)
{
return KalkMatrix.Multiply(mx, vy1);
}
if (x is KalkMatrix mx1 && y is KalkMatrix my2)
{
return KalkMatrix.Multiply(mx1, my2);
}
throw new ArgumentException($"Unsupported type for matrix multiplication. The combination of {Engine.GetTypeName(x)} * {Engine.GetTypeName(y)} is not supported.", nameof(x));
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using Scriban.Syntax;
namespace Kalk.Core.Modules
{
public sealed partial class VectorModule : KalkModuleWithFunctions
{
public const string CategoryMathVectorMatrixFunctions = "Math Vector/Matrix Functions";
private static readonly KalkColorRgbConstructor RgbConstructor = new KalkColorRgbConstructor();
private static readonly KalkColorRgbaConstructor RgbaConstructor = new KalkColorRgbaConstructor();
private MathModule _mathModule;
public VectorModule() : base("Vectors")
{
IsBuiltin = true;
RegisterFunctionsAuto();
}
protected override void Initialize()
{
_mathModule = Engine.GetOrCreateModule<MathModule>();
//Engine.LoadSystemFile(Path.Combine("Modules", "Vectors", "colorspaces.kalk"));
}
/// <summary>
/// Returns the length of the specified floating-point vector.
/// </summary>
/// <param name="x">The specified floating-point vector.</param>
/// <returns>A floating-point scalar that represents the length of the x parameter.</returns>
/// <example>
/// ```kalk
/// >>> length float2(1, 2)
/// # length(float2(1, 2))
/// out = 2.23606797749979
/// >>> length(-5)
/// # length(-5)
/// out = 5
/// ```
/// </example>
[KalkExport("length", CategoryMathVectorMatrixFunctions)]
public object Length(object x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (_mathModule == null) throw new InvalidOperationException($"The module {Name} is not initialized.");
if (x is KalkVector v)
{
return _mathModule.Sqrt(new KalkDoubleValue(KalkVector.Dot(v, v)));
}
return _mathModule.Abs(new KalkCompositeValue(x));
}
/// <summary>
/// Normalizes the specified floating-point vector according to x / length(x).
/// </summary>
/// <param name="x">he specified floating-point vector.</param>
/// <returns>The normalized x parameter. If the length of the x parameter is 0, the result is indefinite.</returns>
/// <example>
/// ```kalk
/// >>> normalize float2(1,2)
/// # normalize(float2(1, 2))
/// out = float2(0.4472136, 0.8944272)
/// ```
/// </example>
[KalkExport("normalize", CategoryMathVectorMatrixFunctions)]
public object Normalize(object x)
{
return ScriptBinaryExpression.Evaluate(Engine, Engine.CurrentSpan, ScriptBinaryOperator.Divide, x, Length(x));
}
/// <summary>
/// Returns the dot product of two vectors.
/// </summary>
/// <param name="x">The first vector.</param>
/// <param name="y">The second vector.</param>
/// <returns>The dot product of the x parameter and the y parameter.</returns>
/// <example>
/// ```kalk
/// >>> dot(float3(1,2,3), float3(4,5,6))
/// # dot(float3(1, 2, 3), float3(4, 5, 6))
/// out = 32
/// >>> dot(float3(1,2,3), 4)
/// # dot(float3(1, 2, 3), 4)
/// out = 24
/// >>> dot(4, float3(1,2,3))
/// # dot(4, float3(1, 2, 3))
/// out = 24
/// >>> dot(5,6)
/// # dot(5, 6)
/// out = 30
/// ```
/// </example>
[KalkExport("dot", CategoryMathVectorMatrixFunctions)]
public object Dot(object x, object y)
{
if (x is KalkVector vx)
{
if (y is KalkVector vy)
{
return KalkVector.Dot(vx, vy);
}
return KalkVector.Dot(vx, vx.FromValue(Engine.ToObject(1, y, vx.ElementType)));
}
else if (y is KalkVector vy)
{
return KalkVector.Dot(vy.FromValue(Engine.ToObject(1, x, vy.ElementType)), vy);
}
return ScriptBinaryExpression.Evaluate(Engine, Engine.CurrentSpan, ScriptBinaryOperator.Multiply, x, y);
}
/// <summary>
/// Returns the cross product of two floating-point, 3D vectors.
/// </summary>
/// <param name="x">The first floating-point, 3D vector.</param>
/// <param name="y">The second floating-point, 3D vector.</param>
/// <returns>The cross product of the x parameter and the y parameter.</returns>
/// <example>
/// ```kalk
/// >>> cross(float3(1,2,3), float3(4,5,6))
/// # cross(float3(1, 2, 3), float3(4, 5, 6))
/// out = float3(-3, 6, -3)
/// >>> cross(float3(1,0,0), float3(0,1,0))
/// # cross(float3(1, 0, 0), float3(0, 1, 0))
/// out = float3(0, 0, 1)
/// >>> cross(float3(0,0,1), float3(0,1,0))
/// # cross(float3(0, 0, 1), float3(0, 1, 0))
/// out = float3(-1, 0, 0)
/// ```
/// </example>
[KalkExport("cross", CategoryMathVectorMatrixFunctions)]
public object Cross(KalkVector x, KalkVector y)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (y == null) throw new ArgumentNullException(nameof(y));
if (x.Length != 3 || (x.ElementType != typeof(float) && x.ElementType != typeof(double))) throw new ArgumentOutOfRangeException(nameof(x), "Expecting a float3 or double3 vector.");
if (y.Length != 3 || (y.ElementType != typeof(float) && y.ElementType != typeof(double))) throw new ArgumentOutOfRangeException(nameof(y), "Expecting a float3 or double3 vector.");
return KalkVector.Cross(x, y);
}
}
}

View File

@@ -0,0 +1,153 @@
# From http://www.chilliant.com/rgb2hsv.html
# Based on work by Sam Hocevar and Emil Persson
func HUEtoRGB(x)
$r = abs(x * 6 - 3) - 1;
$g = 2 - abs(x * 6 - 2);
$b = 2 - abs(x * 6 - 4);
ret saturate(float3($r, $g, $b));
end
func RGBtoHCV(x)
x = x.xyz;
Epsilon = 1e-10f
if x.g < x.b; $p =float4(x.bg, -1.0f, 2.0f/3.0f); else; $p = float4(x.gb, 0.0f, -1.0f/3.0f); end;
if x.r < $p.x; $q = float4($p.xyw, x.r); else; $q = float4(x.r, $p.yzx); end;
$c = $q.x - min($q.w, $q.y);
$h = abs(($q.w - $q.y) / (6.0f * $c + Epsilon) + $q.z);
ret float3($h, $c, $q.x);
end
func HSVtoRGB(x)
x = x.xyz;
$rgb = HUEtoRGB(x.x);
ret (($rgb - 1) * x.y + 1) * x.z;
end
func HSLtoRGB(x)
x = x.xyz;
$rgb = HUEtoRGB(x.x);
$c = (1 - abs(2 * x.z - 1)) * x.y;
ret ($rgb - 0.5) * $c + x.z;
end
func HCYtoRGB(x)
x = x.xyz;
# The weights of RGB contributions to luminance.
# Should sum to unity.
$HCYwts = float3(0.299f, 0.587f, 0.114f);
$rgb = HUEtoRGB(x.x);
$z = dot($rgb, $HCYwts);
if x.z < $z
x.y = x.y * x.z / $z;
else if $z < 1
x.y = x.y * (1 - x.z) / (1 - $z);
end
ret ($rgb - $z) * x.y + x.z;
end
func HCLtoRGB(x)
x = x.xyz;
HCLgamma = 3;
HCLy0 = 100;
HCLmaxL = 0.530454533953517; # == exp(HCLgamma / HCLy0) - 0.5
$rgb = float3;
if x.z != 0
H = x.x;
C = x.y;
L = x.z * HCLmaxL;
Q = exp((1 - C / (2 * L)) * (HCLgamma / HCLy0));
U = (2 * L - C) / (2 * Q - 1);
V = C / Q;
A = (H + min(frac(2 * H) / 4, frac(-2 * H) / 8)) * pi * 2;
H = H * 6;
if (H <= 0.999)
T = tan(A);
$rgb.r = 1;
$rgb.g = T / (1 + T);
else if (H <= 1.001)
$rgb.r = 1;
$rgb.g = 1;
else if (H <= 2)
T = tan(A);
$rgb.r = (1 + T) / T;
$rgb.g = 1;
else if (H <= 3)
T = tan(A);
$rgb.g = 1;
$rgb.b = 1 + T;
else if (H <= 3.999)
T = tan(A);
$rgb.g = 1 / (1 + T);
$rgb.b = 1;
else if (H <= 4.001)
$rgb.g = 0;
$rgb.b = 1;
else if (H <= 5)
T = tan(A);
$rgb.r = -1 / T;
$rgb.b = 1;
else
T = tan(A);
$rgb.r = 1;
$rgb.b = -T;
end
$rgb = $rgb * V + U;
end
ret $rgb;
end
func RGBtoHSV(x)
x = x.xyz;
Epsilon = 1e-10f
$Hcv = RGBtoHCV(x);
$s = $Hcv.y / ($Hcv.z + Epsilon);
ret float3($Hcv.x, $s, $Hcv.z);
end
func RGBtoHSL(x)
x = x.xyz;
Epsilon = 1e-10f
$hcv = RGBtoHCV(x);
$l = $hcv.z - $hcv.y * 0.5;
$s = $hcv.y / (1 - abs($l * 2 - 1) + Epsilon);
ret float3($hcv.x, $s, $l);
end
func RGBtoHCY(x)
x = x.xyz;
# Corrected by David Schaeffer
$HCYwts = float3(0.299f, 0.587f, 0.114f);
Epsilon = 1e-10f
$hcv = RGBtoHCV(x);
$y = dot(x, $HCYwts);
$z = dot(HUEtoRGB($hcv.x), $HCYwts);
if ($y < $z)
$hcv.y = $hcv.y * $z / (Epsilon + $y);
else
$hcv.y = $hcv.y * (1 - $z) / (Epsilon + 1 - $y);
end
ret float3($hcv.x, $hcv.y, $y);
end
func RGBtoHCL(x)
x = x.xyz;
HCLgamma = 3;
HCLy0 = 100;
HCLmaxL = 0.530454533953517; # == exp(HCLgamma / HCLy0) - 0.5
$hcl = float3
H = 0;
U = min(x.r, min(x.g, x.b));
V = max(x.r, max(x.g, x.b));
Q = HCLgamma / HCLy0;
$hcl.y = V - U;
if ($hcl.y != 0)
H = atan2(x.g - x.b, x.r - x.g) / pi;
Q = Q * U / V;
end
Q = exp(Q);
$hcl.x = frac(H / 2 - min(frac(H), frac(-H)) / 6);
$hcl.y = $hcl.y * Q;
$hcl.z = lerp(-U, V, Q) / (HCLmaxL * 2);
ret $hcl;
end