Compare commits
4 Commits
a657d6ed1c
...
engine-rew
| Author | SHA1 | Date | |
|---|---|---|---|
| e76952cd0d | |||
| 2207116d0a | |||
| 201b13f80f | |||
| e3f9c1cc52 |
@@ -7,8 +7,10 @@ using System.IO;
|
||||
namespace _2DGAMELIB
|
||||
{
|
||||
[Serializable]
|
||||
public class BodyTemplate
|
||||
public class BodyTemplate
|
||||
{
|
||||
public string Tag = ""; // I didn't remove this, but it has been removed on merging my changes? What? - REDCODE
|
||||
|
||||
public OrderedDictionary<string, VariantGrid> Difss = new OrderedDictionary<string, VariantGrid>();
|
||||
|
||||
private VariantGrid r;
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace _2DGAMELIB
|
||||
public bool Dra = true;
|
||||
public bool Hit = true;
|
||||
private bool closed;
|
||||
public bool IsClosed => closed;
|
||||
|
||||
public PartGroup GetParent()
|
||||
{
|
||||
|
||||
187
SlaveEngine.AssetBuilder/Compiler.cs
Normal file
187
SlaveEngine.AssetBuilder/Compiler.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using SlaveEngine.AssetBuilder.Models;
|
||||
using SlaveEngine.Assets;
|
||||
using SlaveEngine.Assets.Models;
|
||||
using SlaveEngine.Assets.Primitives;
|
||||
using Spectre.Console;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace SlaveEngine.AssetBuilder;
|
||||
|
||||
public class Compiler {
|
||||
private readonly string _inputDir;
|
||||
private readonly string _outputDir;
|
||||
private readonly IDeserializer _yaml;
|
||||
|
||||
public Compiler(string inputDir, string outputDir) {
|
||||
_inputDir = inputDir;
|
||||
_outputDir = outputDir;
|
||||
|
||||
_yaml = new DeserializerBuilder()
|
||||
.WithNamingConvention(UnderscoredNamingConvention.Instance)
|
||||
.IgnoreUnmatchedProperties()
|
||||
.Build();
|
||||
}
|
||||
|
||||
private T LoadYaml<T>(string path) {
|
||||
var text = File.ReadAllText(path);
|
||||
text = NormalizeFlowArrays(text);
|
||||
return _yaml.Deserialize<T>(text);
|
||||
}
|
||||
|
||||
private static string NormalizeFlowArrays(string yaml) {
|
||||
return Regex.Replace(yaml,
|
||||
@"(position:\s*)\[([\s\S]*?)\]",
|
||||
m => {
|
||||
var prefix = m.Groups[1].Value.TrimEnd();
|
||||
var values = m.Groups[2].Value
|
||||
.Replace('\n', ' ')
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(v => v.Trim())
|
||||
.Where(v => v.Length > 0)
|
||||
.ToList();
|
||||
if (values.Count == 0) return prefix + " []";
|
||||
return prefix + "\n" +
|
||||
string.Join("\n", values.Select(v => $" - {v}"));
|
||||
});
|
||||
}
|
||||
|
||||
public int Run() {
|
||||
AnsiConsole.Write(new Rule("[yellow]SlaveEngine Asset Builder[/]") { Justification = Justify.Left });
|
||||
AnsiConsole.MarkupLine(" [bold]Input:[/] {0}", _inputDir);
|
||||
AnsiConsole.MarkupLine(" [bold]Output:[/] {0}", _outputDir);
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
Directory.CreateDirectory(_outputDir);
|
||||
|
||||
var parts = DiscoverParts();
|
||||
if (parts.Count == 0) {
|
||||
AnsiConsole.MarkupLine("[red]No parts found.[/]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var success = 0;
|
||||
|
||||
AnsiConsole.Progress()
|
||||
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn(), new ElapsedTimeColumn())
|
||||
.Start(ctx => {
|
||||
var task = ctx.AddTask("Compiling parts");
|
||||
task.MaxValue = parts.Count;
|
||||
|
||||
foreach (var (id, partDir) in parts) {
|
||||
var (ok, msg) = CompilePart(partDir);
|
||||
if (ok)
|
||||
AnsiConsole.MarkupLine("[green]{0}[/]", msg);
|
||||
else
|
||||
AnsiConsole.MarkupLine("[red]{0}[/]", msg);
|
||||
if (ok) success++;
|
||||
task.Increment(1);
|
||||
}
|
||||
});
|
||||
|
||||
if (success == parts.Count)
|
||||
AnsiConsole.MarkupLine("[green]Done. {0}/{1} compiled.[/]", success, parts.Count);
|
||||
else if (success > 0)
|
||||
AnsiConsole.MarkupLine("[yellow]Done. {0}/{1} compiled.[/]", success, parts.Count);
|
||||
else
|
||||
AnsiConsole.MarkupLine("[red]Done. {0}/{1} compiled.[/]", success, parts.Count);
|
||||
|
||||
return success == parts.Count ? 0 : 1;
|
||||
}
|
||||
|
||||
private List<(string id, string dir)> DiscoverParts() {
|
||||
var parts = new List<(string id, string dir)>();
|
||||
|
||||
var catalogPath = Path.Combine(_inputDir, "Catalog.yaml");
|
||||
if (File.Exists(catalogPath)) {
|
||||
AnsiConsole.MarkupLine("[dim]Using catalog:[/] {0}", catalogPath);
|
||||
var catalog = LoadYaml<CatalogYaml>(catalogPath);
|
||||
if (catalog?.Parts != null) {
|
||||
foreach (var entry in catalog.Parts) {
|
||||
var partDir = Path.GetFullPath(Path.Combine(_inputDir, entry.Path));
|
||||
if (Directory.Exists(partDir))
|
||||
parts.Add((entry.Id, partDir));
|
||||
else
|
||||
AnsiConsole.MarkupLine("[yellow] Warning:[/] directory not found: {0}", partDir);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
|
||||
var scanDir = Path.Combine(_inputDir, "Parts");
|
||||
if (!Directory.Exists(scanDir)) return parts;
|
||||
|
||||
AnsiConsole.MarkupLine("[dim]Scanning:[/] {0}", scanDir);
|
||||
foreach (var dir in Directory.GetDirectories(scanDir)) {
|
||||
var yamlPath = Path.Combine(dir, "part.yaml");
|
||||
if (!File.Exists(yamlPath)) continue;
|
||||
try {
|
||||
var partYaml = LoadYaml<PartYaml>(yamlPath);
|
||||
if (partYaml != null && !string.IsNullOrEmpty(partYaml.Id))
|
||||
parts.Add((partYaml.Id, dir));
|
||||
} catch (Exception ex) {
|
||||
AnsiConsole.MarkupLine("[yellow] Warning:[/] failed to parse {0}: {1}", yamlPath, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
private (bool success, string message) CompilePart(string partDir) {
|
||||
var yamlPath = Path.Combine(partDir, "part.yaml");
|
||||
if (!File.Exists(yamlPath))
|
||||
return (false, $"Skipping (no part.yaml): {partDir}");
|
||||
|
||||
PartYaml partYaml;
|
||||
try {
|
||||
partYaml = LoadYaml<PartYaml>(yamlPath);
|
||||
} catch (Exception ex) {
|
||||
return (false, $"Failed to parse {yamlPath}: {ex.Message}");
|
||||
}
|
||||
|
||||
if (partYaml == null)
|
||||
return (false, $"Empty YAML: {yamlPath}");
|
||||
|
||||
try {
|
||||
var variantList = partYaml.Variants ?? new List<VariantYaml>();
|
||||
var variants = new VariantAsset[variantList.Count];
|
||||
for (var i = 0; i < variantList.Count; i++) {
|
||||
var v = variantList[i];
|
||||
var svgPath = Path.Combine(partDir, v.File);
|
||||
if (!File.Exists(svgPath)) {
|
||||
variants[i] = new VariantAsset { X = v.X, Y = v.Y, Groups = Array.Empty<PathGroup>() };
|
||||
continue;
|
||||
}
|
||||
var groups = SvgParser.Parse(svgPath);
|
||||
variants[i] = new VariantAsset { X = v.X, Y = v.Y, Groups = groups };
|
||||
}
|
||||
|
||||
var fieldList = partYaml.Fields ?? new List<FieldYaml>();
|
||||
var jointList = partYaml.Joints ?? new List<JointYaml>();
|
||||
|
||||
var asset = new PartAsset {
|
||||
Guid = Guid.NewGuid(),
|
||||
FileInfo = new AssetFileInfo { Filename = partYaml.Id, FilePath = "" },
|
||||
Id = partYaml.Id,
|
||||
OriginalKey = partYaml.OriginalKey ?? "",
|
||||
Resource = partYaml.Resource ?? "",
|
||||
MorphX = partYaml.MorphX,
|
||||
MorphY = partYaml.MorphY,
|
||||
Fields = fieldList.Select(f => f.Name ?? "").ToArray(),
|
||||
Joints = jointList.Select(j => new Joint {
|
||||
X = j.Position != null && j.Position.Count > 0 ? j.Position[0] : 0,
|
||||
Y = j.Position != null && j.Position.Count > 1 ? j.Position[1] : 0
|
||||
}).ToArray(),
|
||||
Variants = variants
|
||||
};
|
||||
|
||||
var outputPath = Path.Combine(_outputDir, $"{partYaml.Id}.spart");
|
||||
SparWriter.Write(asset, outputPath);
|
||||
|
||||
return (true, $" {partYaml.Id}... OK ({variants.Length} variants)");
|
||||
} catch (Exception ex) {
|
||||
return (false, $" {partYaml.Id}... FAILED: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
35
SlaveEngine.AssetBuilder/Models/YamlModels.cs
Normal file
35
SlaveEngine.AssetBuilder/Models/YamlModels.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace SlaveEngine.AssetBuilder.Models;
|
||||
|
||||
public class PartYaml {
|
||||
public string Id { get; set; } = "";
|
||||
public string OriginalKey { get; set; } = "";
|
||||
public string Resource { get; set; } = "";
|
||||
public int MorphX { get; set; }
|
||||
public int MorphY { get; set; }
|
||||
public List<VariantYaml>? Variants { get; set; }
|
||||
public List<FieldYaml>? Fields { get; set; }
|
||||
public List<JointYaml>? Joints { get; set; }
|
||||
}
|
||||
|
||||
public class VariantYaml {
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public string File { get; set; } = "";
|
||||
}
|
||||
|
||||
public class FieldYaml {
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
public class JointYaml {
|
||||
public List<float>? Position { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogYaml {
|
||||
public List<CatalogEntry>? Parts { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogEntry {
|
||||
public string Id { get; set; } = "";
|
||||
public string Path { get; set; } = "";
|
||||
}
|
||||
32
SlaveEngine.AssetBuilder/Program.cs
Normal file
32
SlaveEngine.AssetBuilder/Program.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.CommandLine;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace SlaveEngine.AssetBuilder;
|
||||
|
||||
class Program {
|
||||
static int Main(string[] args) {
|
||||
var inputOption = new Option<DirectoryInfo>("--input", "Path to the input SlaveMatrix/Assets folder");
|
||||
inputOption.SetDefaultValueFactory(() => new DirectoryInfo("./SlaveMatrix/Assets"));
|
||||
|
||||
var outputOption = new Option<DirectoryInfo>("--output", "Path to the output directory for compiled .spart files");
|
||||
outputOption.SetDefaultValueFactory(() => new DirectoryInfo("./Assets"));
|
||||
|
||||
var rootCommand = new RootCommand(
|
||||
"SlaveEngine Asset Builder — compiles YAML + SVG assets into .spart binaries");
|
||||
rootCommand.AddOption(inputOption);
|
||||
rootCommand.AddOption(outputOption);
|
||||
|
||||
rootCommand.SetHandler((DirectoryInfo input, DirectoryInfo output) => {
|
||||
if (!input.Exists) {
|
||||
AnsiConsole.MarkupLine("[red]Error:[/] input directory not found: {0}", input.FullName);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
var compiler = new Compiler(input.FullName, output.FullName);
|
||||
var result = compiler.Run();
|
||||
Environment.Exit(result);
|
||||
}, inputOption, outputOption);
|
||||
|
||||
return rootCommand.Invoke(args);
|
||||
}
|
||||
}
|
||||
20
SlaveEngine.AssetBuilder/SlaveEngine.AssetBuilder.csproj
Normal file
20
SlaveEngine.AssetBuilder/SlaveEngine.AssetBuilder.csproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SlaveEngine.Assets\SlaveEngine.Assets.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Spectre.Console" Version="0.57.0" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageReference Include="YamlDotNet" Version="18.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
73
SlaveEngine.AssetBuilder/SparWriter.cs
Normal file
73
SlaveEngine.AssetBuilder/SparWriter.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Text;
|
||||
using SlaveEngine.Assets;
|
||||
using SlaveEngine.Assets.Models;
|
||||
using SlaveEngine.Assets.Primitives;
|
||||
|
||||
namespace SlaveEngine.AssetBuilder;
|
||||
|
||||
public static class SparWriter {
|
||||
public static void Write(PartAsset asset, string outputPath) {
|
||||
using var stream = File.Create(outputPath);
|
||||
using var writer = new BinaryWriter(stream);
|
||||
|
||||
writer.Write(Encoding.UTF8.GetBytes("SPRT"));
|
||||
writer.Write(1); // version
|
||||
|
||||
WriteString(writer, asset.Id);
|
||||
WriteString(writer, asset.OriginalKey);
|
||||
WriteString(writer, asset.Resource);
|
||||
writer.Write(asset.MorphX);
|
||||
writer.Write(asset.MorphY);
|
||||
|
||||
writer.Write(asset.Fields.Length);
|
||||
foreach (var f in asset.Fields) WriteString(writer, f);
|
||||
|
||||
writer.Write(asset.Joints.Length);
|
||||
foreach (var j in asset.Joints) {
|
||||
writer.Write(j.X);
|
||||
writer.Write(j.Y);
|
||||
}
|
||||
|
||||
writer.Write(asset.Variants.Length);
|
||||
foreach (var variant in asset.Variants) {
|
||||
writer.Write(variant.X);
|
||||
writer.Write(variant.Y);
|
||||
|
||||
writer.Write(variant.Groups.Length);
|
||||
foreach (var group in variant.Groups) {
|
||||
WriteString(writer, group.Name);
|
||||
writer.Write(group.HasTransform);
|
||||
if (group.HasTransform) {
|
||||
writer.Write(group.Tx);
|
||||
writer.Write(group.Ty);
|
||||
writer.Write(group.Angle);
|
||||
writer.Write(group.Sx);
|
||||
writer.Write(group.Sy);
|
||||
writer.Write(group.Bx);
|
||||
writer.Write(group.By);
|
||||
}
|
||||
|
||||
writer.Write(group.Paths.Length);
|
||||
foreach (var path in group.Paths) {
|
||||
WriteString(writer, path.Fill);
|
||||
WriteString(writer, path.Stroke);
|
||||
writer.Write(path.StrokeWidth);
|
||||
writer.Write(path.IsClosed);
|
||||
|
||||
writer.Write(path.Commands.Length);
|
||||
foreach (var cmd in path.Commands) {
|
||||
writer.Write((byte)cmd.Type);
|
||||
writer.Write(cmd.Args.Length);
|
||||
foreach (var arg in cmd.Args) writer.Write(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteString(BinaryWriter writer, string s) {
|
||||
var bytes = Encoding.UTF8.GetBytes(s);
|
||||
writer.Write(bytes.Length);
|
||||
writer.Write(bytes);
|
||||
}
|
||||
}
|
||||
192
SlaveEngine.AssetBuilder/SvgParser.cs
Normal file
192
SlaveEngine.AssetBuilder/SvgParser.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using SlaveEngine.Assets.Models;
|
||||
using SlaveEngine.Assets.Primitives;
|
||||
|
||||
namespace SlaveEngine.AssetBuilder;
|
||||
|
||||
public static class SvgParser {
|
||||
private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;
|
||||
|
||||
public static PathGroup[] Parse(string svgPath) {
|
||||
var doc = XDocument.Load(svgPath);
|
||||
var svg = doc.Root;
|
||||
if (svg == null) return Array.Empty<PathGroup>();
|
||||
|
||||
var groups = new List<PathGroup>();
|
||||
CollectGroups(svg, groups, null);
|
||||
return groups.ToArray();
|
||||
}
|
||||
|
||||
private static void CollectGroups(XElement parent, List<PathGroup> groups, string? inheritedName) {
|
||||
foreach (var child in parent.Elements()) {
|
||||
var name = child.Name.LocalName;
|
||||
if (name != "g" && name != "path") continue;
|
||||
var id = child.Attribute("id")?.Value ?? inheritedName ?? "";
|
||||
|
||||
if (name == "g") {
|
||||
var transformAttr = child.Attribute("transform")?.Value;
|
||||
|
||||
if (transformAttr != null) {
|
||||
ParseTransform(transformAttr,
|
||||
out var tx, out var ty, out var angle,
|
||||
out var sx, out var sy, out var bx, out var by);
|
||||
|
||||
var paths = CollectPaths(child);
|
||||
groups.Add(new PathGroup {
|
||||
Name = id,
|
||||
HasTransform = true,
|
||||
Tx = tx, Ty = ty, Angle = angle,
|
||||
Sx = sx, Sy = sy, Bx = bx, By = by,
|
||||
Paths = paths
|
||||
});
|
||||
} else {
|
||||
CollectGroups(child, groups, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PathData[] CollectPaths(XElement parent) {
|
||||
var paths = new List<PathData>();
|
||||
foreach (var elem in parent.Elements()) {
|
||||
var name = elem.Name.LocalName;
|
||||
if (name == "path") {
|
||||
var d = elem.Attribute("d")?.Value ?? "";
|
||||
var (commands, isClosed) = ParsePathData(d);
|
||||
paths.Add(new PathData {
|
||||
Fill = elem.Attribute("fill")?.Value ?? "none",
|
||||
Stroke = elem.Attribute("stroke")?.Value ?? "none",
|
||||
StrokeWidth = float.Parse(
|
||||
elem.Attribute("stroke-width")?.Value ?? "0", Inv),
|
||||
IsClosed = isClosed,
|
||||
Commands = commands
|
||||
});
|
||||
} else if (name == "g") {
|
||||
paths.AddRange(CollectPaths(elem));
|
||||
}
|
||||
}
|
||||
return paths.ToArray();
|
||||
}
|
||||
|
||||
private static void ParseTransform(string t,
|
||||
out float tx, out float ty, out float angle,
|
||||
out float sx, out float sy, out float bx, out float by) {
|
||||
tx = ty = angle = 0;
|
||||
sx = sy = 1;
|
||||
bx = by = 0;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(t)) return;
|
||||
|
||||
var parts = t.Split(')', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var part in parts) {
|
||||
var trimmed = part.TrimStart();
|
||||
if (trimmed.StartsWith("translate(")) {
|
||||
var args = trimmed["translate(".Length..].Split(',');
|
||||
if (args.Length >= 2) {
|
||||
tx = float.Parse(args[0].Trim(), Inv);
|
||||
ty = float.Parse(args[1].Trim(), Inv);
|
||||
}
|
||||
} else if (trimmed.StartsWith("rotate(")) {
|
||||
var arg = trimmed["rotate(".Length..].Trim();
|
||||
angle = float.Parse(arg, Inv);
|
||||
} else if (trimmed.StartsWith("scale(")) {
|
||||
var args = trimmed["scale(".Length..].Split(',');
|
||||
if (args.Length >= 2) {
|
||||
sx = float.Parse(args[0].Trim(), Inv);
|
||||
sy = float.Parse(args[1].Trim(), Inv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static (BezierCommand[] commands, bool isClosed) ParsePathData(string d) {
|
||||
var commands = new List<BezierCommand>();
|
||||
var tokens = Tokenize(d);
|
||||
var i = 0;
|
||||
float cx = 0, cy = 0;
|
||||
|
||||
while (i < tokens.Count) {
|
||||
var cmd = tokens[i][0];
|
||||
i++;
|
||||
|
||||
switch (cmd) {
|
||||
case 'M': case 'm': {
|
||||
var x = float.Parse(tokens[i++], Inv);
|
||||
var y = float.Parse(tokens[i++], Inv);
|
||||
if (cmd == 'm') { x += cx; y += cy; }
|
||||
commands.Add(new BezierCommand { Type = CommandType.MoveTo, Args = new[] { x, y } });
|
||||
cx = x; cy = y;
|
||||
break;
|
||||
}
|
||||
case 'L': case 'l': {
|
||||
var x = float.Parse(tokens[i++], Inv);
|
||||
var y = float.Parse(tokens[i++], Inv);
|
||||
if (cmd == 'l') { x += cx; y += cy; }
|
||||
commands.Add(new BezierCommand { Type = CommandType.LineTo, Args = new[] { x, y } });
|
||||
cx = x; cy = y;
|
||||
break;
|
||||
}
|
||||
case 'C': case 'c': {
|
||||
var c1x = float.Parse(tokens[i++], Inv);
|
||||
var c1y = float.Parse(tokens[i++], Inv);
|
||||
var c2x = float.Parse(tokens[i++], Inv);
|
||||
var c2y = float.Parse(tokens[i++], Inv);
|
||||
var ex = float.Parse(tokens[i++], Inv);
|
||||
var ey = float.Parse(tokens[i++], Inv);
|
||||
if (cmd == 'c') {
|
||||
c1x += cx; c1y += cy;
|
||||
c2x += cx; c2y += cy;
|
||||
ex += cx; ey += cy;
|
||||
}
|
||||
commands.Add(new BezierCommand {
|
||||
Type = CommandType.CubicBezierTo,
|
||||
Args = new[] { c1x, c1y, c2x, c2y, ex, ey }
|
||||
});
|
||||
cx = ex; cy = ey;
|
||||
break;
|
||||
}
|
||||
case 'Z': case 'z': {
|
||||
commands.Add(new BezierCommand { Type = CommandType.Close, Args = Array.Empty<float>() });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var closed = commands.Count > 0 && commands[^1].Type == CommandType.Close;
|
||||
return (commands.ToArray(), closed);
|
||||
}
|
||||
|
||||
private static List<string> Tokenize(string d) {
|
||||
var tokens = new List<string>();
|
||||
var i = 0;
|
||||
while (i < d.Length) {
|
||||
var c = d[i];
|
||||
if (char.IsWhiteSpace(c) || c == ',') { i++; continue; }
|
||||
if (char.IsLetter(c)) {
|
||||
var upper = char.ToUpperInvariant(c);
|
||||
if (upper is 'M' or 'L' or 'C' or 'Z') {
|
||||
tokens.Add(c.ToString());
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (c is '-' or '+' or '.' || char.IsDigit(c)) {
|
||||
var start = i;
|
||||
if (c is '-' or '+') i++;
|
||||
while (i < d.Length && (char.IsDigit(d[i]) || d[i] == '.')) i++;
|
||||
if (i < d.Length && (d[i] is 'e' or 'E')) {
|
||||
i++;
|
||||
if (i < d.Length && (d[i] is '+' or '-')) i++;
|
||||
while (i < d.Length && char.IsDigit(d[i])) i++;
|
||||
}
|
||||
tokens.Add(d[start..i]);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
6
SlaveEngine.Assets/Asset.cs
Normal file
6
SlaveEngine.Assets/Asset.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public abstract class Asset {
|
||||
public Guid Guid { get; init; }
|
||||
public AssetFileInfo FileInfo { get; init; }
|
||||
}
|
||||
6
SlaveEngine.Assets/AssetFileInfo.cs
Normal file
6
SlaveEngine.Assets/AssetFileInfo.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public struct AssetFileInfo {
|
||||
public string Filename;
|
||||
public string FilePath;
|
||||
}
|
||||
5
SlaveEngine.Assets/AssetProcessor.cs
Normal file
5
SlaveEngine.Assets/AssetProcessor.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public abstract class AssetProcessor {
|
||||
public abstract Asset Process(AssetFileInfo fileInfo , byte[] data);
|
||||
}
|
||||
46
SlaveEngine.Assets/AssetProcessorAttribute.cs
Normal file
46
SlaveEngine.Assets/AssetProcessorAttribute.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public class AssetProcessorAttribute : Attribute {
|
||||
public Type ManagedType { get; init; }
|
||||
public string[] ManagedExtensions { get; init; }
|
||||
|
||||
public AssetProcessorAttribute(Type managedType, string[] managedExtensions)
|
||||
{
|
||||
ManagedType = managedType ?? throw new ArgumentNullException(nameof(managedType));
|
||||
|
||||
// Validate that the provided type is Asset or a subclass of Asset
|
||||
if (!typeof(Asset).IsSubclassOf(managedType))
|
||||
throw new ArgumentException($"The type '{managedType.FullName}' must derive from '{typeof(Asset).FullName}'.", nameof(managedType));
|
||||
|
||||
// Validate extensions: non-null, non-empty, normalized, start with '.', no whitespace, unique
|
||||
if (managedExtensions == null)
|
||||
throw new ArgumentNullException(nameof(managedExtensions));
|
||||
|
||||
if (managedExtensions.Length == 0)
|
||||
throw new ArgumentException("At least one file extension must be provided.", nameof(managedExtensions));
|
||||
|
||||
var normalized = new List<string>(managedExtensions.Length);
|
||||
foreach (var ext in managedExtensions)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ext))
|
||||
throw new ArgumentException("Extensions must be non-empty strings.", nameof(managedExtensions));
|
||||
|
||||
var e = ext.Trim();
|
||||
|
||||
// allow either "png" or ".png" as input, normalize to lowercase with leading dot
|
||||
if (!e.StartsWith('.'))
|
||||
e = "." + e;
|
||||
|
||||
if (e.Length < 2)
|
||||
throw new ArgumentException($"Invalid extension '{ext}'.", nameof(managedExtensions));
|
||||
|
||||
if (e.Any(char.IsWhiteSpace))
|
||||
throw new ArgumentException($"Extension '{ext}' contains whitespace.", nameof(managedExtensions));
|
||||
|
||||
normalized.Add(e.ToLowerInvariant());
|
||||
}
|
||||
|
||||
// ensure uniqueness
|
||||
ManagedExtensions = normalized.Distinct().ToArray();
|
||||
}
|
||||
}
|
||||
14
SlaveEngine.Assets/Models/PartAsset.cs
Normal file
14
SlaveEngine.Assets/Models/PartAsset.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using SlaveEngine.Assets.Primitives;
|
||||
|
||||
namespace SlaveEngine.Assets.Models;
|
||||
|
||||
public sealed class PartAsset : Asset {
|
||||
public required string Id { get; init; }
|
||||
public required string OriginalKey { get; init; }
|
||||
public required string Resource { get; init; }
|
||||
public int MorphX { get; init; }
|
||||
public int MorphY { get; init; }
|
||||
public required string[] Fields { get; init; }
|
||||
public required Joint[] Joints { get; init; }
|
||||
public required VariantAsset[] Variants { get; init; }
|
||||
}
|
||||
11
SlaveEngine.Assets/Models/PathData.cs
Normal file
11
SlaveEngine.Assets/Models/PathData.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using SlaveEngine.Assets.Primitives;
|
||||
|
||||
namespace SlaveEngine.Assets.Models;
|
||||
|
||||
public sealed class PathData {
|
||||
public required string Fill { get; init; }
|
||||
public required string Stroke { get; init; }
|
||||
public float StrokeWidth { get; init; }
|
||||
public bool IsClosed { get; init; }
|
||||
public required BezierCommand[] Commands { get; init; }
|
||||
}
|
||||
14
SlaveEngine.Assets/Models/PathGroup.cs
Normal file
14
SlaveEngine.Assets/Models/PathGroup.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace SlaveEngine.Assets.Models;
|
||||
|
||||
public sealed class PathGroup {
|
||||
public required string Name { get; init; }
|
||||
public bool HasTransform { get; init; }
|
||||
public float Tx { get; init; }
|
||||
public float Ty { get; init; }
|
||||
public float Angle { get; init; }
|
||||
public float Sx { get; init; }
|
||||
public float Sy { get; init; }
|
||||
public float Bx { get; init; }
|
||||
public float By { get; init; }
|
||||
public required PathData[] Paths { get; init; }
|
||||
}
|
||||
7
SlaveEngine.Assets/Models/VariantAsset.cs
Normal file
7
SlaveEngine.Assets/Models/VariantAsset.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace SlaveEngine.Assets.Models;
|
||||
|
||||
public sealed class VariantAsset {
|
||||
public int X { get; init; }
|
||||
public int Y { get; init; }
|
||||
public required PathGroup[] Groups { get; init; }
|
||||
}
|
||||
6
SlaveEngine.Assets/Primitives/BezierCommand.cs
Normal file
6
SlaveEngine.Assets/Primitives/BezierCommand.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace SlaveEngine.Assets.Primitives;
|
||||
|
||||
public readonly struct BezierCommand {
|
||||
public CommandType Type { get; init; }
|
||||
public float[] Args { get; init; }
|
||||
}
|
||||
8
SlaveEngine.Assets/Primitives/CommandType.cs
Normal file
8
SlaveEngine.Assets/Primitives/CommandType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace SlaveEngine.Assets.Primitives;
|
||||
|
||||
public enum CommandType : byte {
|
||||
MoveTo = 0,
|
||||
LineTo = 1,
|
||||
CubicBezierTo = 2,
|
||||
Close = 3
|
||||
}
|
||||
6
SlaveEngine.Assets/Primitives/Joint.cs
Normal file
6
SlaveEngine.Assets/Primitives/Joint.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace SlaveEngine.Assets.Primitives;
|
||||
|
||||
public readonly struct Joint {
|
||||
public float X { get; init; }
|
||||
public float Y { get; init; }
|
||||
}
|
||||
120
SlaveEngine.Assets/Processors/PartProcessor.cs
Normal file
120
SlaveEngine.Assets/Processors/PartProcessor.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System.Text;
|
||||
using SlaveEngine.Assets.Models;
|
||||
using SlaveEngine.Assets.Primitives;
|
||||
|
||||
namespace SlaveEngine.Assets.Processors;
|
||||
|
||||
[AssetProcessor(typeof(PartAsset), managedExtensions: [".spart"])]
|
||||
public sealed class PartProcessor : AssetProcessor {
|
||||
public override Asset Process(AssetFileInfo fileInfo, byte[] data) {
|
||||
using var stream = new MemoryStream(data);
|
||||
using var reader = new BinaryReader(stream);
|
||||
|
||||
var magic = reader.ReadBytes(4);
|
||||
if (magic[0] != 'S' || magic[1] != 'P' || magic[2] != 'R' || magic[3] != 'T')
|
||||
throw new InvalidDataException("Invalid .spart file magic");
|
||||
|
||||
var version = reader.ReadInt32();
|
||||
if (version != 1)
|
||||
throw new InvalidDataException($"Unsupported .spart version: {version}");
|
||||
|
||||
var id = ReadString(reader);
|
||||
var originalKey = ReadString(reader);
|
||||
var resource = ReadString(reader);
|
||||
var morphX = reader.ReadInt32();
|
||||
var morphY = reader.ReadInt32();
|
||||
|
||||
var fieldCount = reader.ReadInt32();
|
||||
var fields = new string[fieldCount];
|
||||
for (var i = 0; i < fieldCount; i++)
|
||||
fields[i] = ReadString(reader);
|
||||
|
||||
var jointCount = reader.ReadInt32();
|
||||
var joints = new Joint[jointCount];
|
||||
for (var i = 0; i < jointCount; i++)
|
||||
joints[i] = new Joint { X = reader.ReadSingle(), Y = reader.ReadSingle() };
|
||||
|
||||
var variantCount = reader.ReadInt32();
|
||||
var variants = new VariantAsset[variantCount];
|
||||
for (var v = 0; v < variantCount; v++) {
|
||||
var vx = reader.ReadInt32();
|
||||
var vy = reader.ReadInt32();
|
||||
|
||||
var groupCount = reader.ReadInt32();
|
||||
var groups = new PathGroup[groupCount];
|
||||
for (var g = 0; g < groupCount; g++) {
|
||||
var groupName = ReadString(reader);
|
||||
var hasTransform = reader.ReadBoolean();
|
||||
float tx = 0, ty = 0, angle = 0, sx = 1, sy = 1, bx = 0, by = 0;
|
||||
if (hasTransform) {
|
||||
tx = reader.ReadSingle();
|
||||
ty = reader.ReadSingle();
|
||||
angle = reader.ReadSingle();
|
||||
sx = reader.ReadSingle();
|
||||
sy = reader.ReadSingle();
|
||||
bx = reader.ReadSingle();
|
||||
by = reader.ReadSingle();
|
||||
}
|
||||
|
||||
var pathCount = reader.ReadInt32();
|
||||
var paths = new PathData[pathCount];
|
||||
for (var p = 0; p < pathCount; p++) {
|
||||
var fill = ReadString(reader);
|
||||
var stroke = ReadString(reader);
|
||||
var strokeWidth = reader.ReadSingle();
|
||||
var isClosed = reader.ReadBoolean();
|
||||
|
||||
var cmdCount = reader.ReadInt32();
|
||||
var cmds = new BezierCommand[cmdCount];
|
||||
for (var c = 0; c < cmdCount; c++) {
|
||||
var cmdType = (CommandType)reader.ReadByte();
|
||||
var argCount = reader.ReadInt32();
|
||||
var args = new float[argCount];
|
||||
for (var a = 0; a < argCount; a++)
|
||||
args[a] = reader.ReadSingle();
|
||||
cmds[c] = new BezierCommand { Type = cmdType, Args = args };
|
||||
}
|
||||
|
||||
paths[p] = new PathData {
|
||||
Fill = fill,
|
||||
Stroke = stroke,
|
||||
StrokeWidth = strokeWidth,
|
||||
IsClosed = isClosed,
|
||||
Commands = cmds
|
||||
};
|
||||
}
|
||||
|
||||
groups[g] = new PathGroup {
|
||||
Name = groupName,
|
||||
HasTransform = hasTransform,
|
||||
Tx = tx, Ty = ty, Angle = angle,
|
||||
Sx = sx, Sy = sy, Bx = bx, By = by,
|
||||
Paths = paths
|
||||
};
|
||||
}
|
||||
|
||||
variants[v] = new VariantAsset {
|
||||
X = vx, Y = vy, Groups = groups
|
||||
};
|
||||
}
|
||||
|
||||
return new PartAsset {
|
||||
Guid = Guid.NewGuid(),
|
||||
FileInfo = fileInfo,
|
||||
Id = id,
|
||||
OriginalKey = originalKey,
|
||||
Resource = resource,
|
||||
MorphX = morphX,
|
||||
MorphY = morphY,
|
||||
Fields = fields,
|
||||
Joints = joints,
|
||||
Variants = variants
|
||||
};
|
||||
}
|
||||
|
||||
private static string ReadString(BinaryReader reader) {
|
||||
var len = reader.ReadInt32();
|
||||
var bytes = reader.ReadBytes(len);
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
}
|
||||
78
SlaveEngine.Assets/ResourceManager.cs
Normal file
78
SlaveEngine.Assets/ResourceManager.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public sealed class ResourceManager {
|
||||
|
||||
public ResourceManager(string basePath) {
|
||||
if (!Path.Exists(basePath))
|
||||
throw new DirectoryNotFoundException($"The base path '{basePath}' does not exist.");
|
||||
BasePath = basePath;
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public static ResourceManager? Instance { get; private set; }
|
||||
public string BasePath { get; private set; }
|
||||
|
||||
private readonly List<AssetProcessor> _assetProcessors = new();
|
||||
private readonly Dictionary<string, string> _assetIndex = new();
|
||||
private readonly Dictionary<string, Asset> _assetCache = new();
|
||||
|
||||
public void Initialize() {
|
||||
ScanDirectory(BasePath);
|
||||
}
|
||||
|
||||
private void ScanDirectory(string directory) {
|
||||
foreach (var file in Directory.GetFiles(directory, "*.spart")) {
|
||||
try {
|
||||
var id = ReadAssetIdFromSpart(file);
|
||||
_assetIndex[id] = file;
|
||||
} catch {
|
||||
// skip corrupt files
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T? GetAsset<T>(string id) where T : Asset {
|
||||
if (_assetCache.TryGetValue(id, out var cached))
|
||||
return (T)cached;
|
||||
|
||||
if (!_assetIndex.TryGetValue(id, out var path))
|
||||
return null;
|
||||
|
||||
var ext = Path.GetExtension(path);
|
||||
var processor = GetProcessorForExtension(ext);
|
||||
if (processor == null)
|
||||
return null;
|
||||
|
||||
var fileInfo = new AssetFileInfo {
|
||||
Filename = Path.GetFileNameWithoutExtension(path),
|
||||
FilePath = path
|
||||
};
|
||||
|
||||
var asset = processor.Process(fileInfo, File.ReadAllBytes(path));
|
||||
_assetCache[id] = asset;
|
||||
return (T)asset;
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<string> GetAssetIds() => _assetIndex.Keys;
|
||||
|
||||
public void RegisterProcessor(AssetProcessor processor) {
|
||||
if (_assetProcessors.All(p => p.GetType() != processor.GetType()))
|
||||
_assetProcessors.Add(processor);
|
||||
}
|
||||
|
||||
private AssetProcessor? GetProcessorForExtension(string extension) =>
|
||||
_assetProcessors.FirstOrDefault(p => p.GetType()
|
||||
.GetCustomAttribute<AssetProcessorAttribute>()?.ManagedExtensions
|
||||
.Contains(extension) == true);
|
||||
|
||||
private static string ReadAssetIdFromSpart(string path) {
|
||||
using var stream = File.OpenRead(path);
|
||||
using var reader = new BinaryReader(stream);
|
||||
reader.ReadBytes(8);
|
||||
var len = reader.ReadInt32();
|
||||
return Encoding.UTF8.GetString(reader.ReadBytes(len));
|
||||
}
|
||||
}
|
||||
13
SlaveEngine.Assets/SlaveEngine.Assets.csproj
Normal file
13
SlaveEngine.Assets/SlaveEngine.Assets.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="YamlDotNet" Version="18.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -277,15 +277,15 @@ class Program
|
||||
["id"] = engKey,
|
||||
["original_key"] = originalKey,
|
||||
["resource"] = name,
|
||||
["morph_x"] = difs.CountX,
|
||||
["morph_y"] = difs.CountY,
|
||||
["morph_x"] = difs.GetCountX(),
|
||||
["morph_y"] = difs.GetCountY(),
|
||||
["variants"] = new JArray(),
|
||||
["fields"] = new JArray()
|
||||
};
|
||||
|
||||
var variants = (JArray)partEntry["variants"];
|
||||
|
||||
for (int x = 0; x < difs.CountX; x++)
|
||||
for (int x = 0; x < difs.GetCountX(); x++)
|
||||
{
|
||||
var dif = difs[x];
|
||||
for (int y = 0; y < dif.Count; y++)
|
||||
@@ -305,7 +305,7 @@ class Program
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string childKey in difs[0][0].Keys)
|
||||
foreach (string childKey in difs[0][0].pars.Keys)
|
||||
{
|
||||
((JArray)partEntry["fields"]).Add(new JObject { ["name"] = childKey });
|
||||
}
|
||||
@@ -432,7 +432,7 @@ class Program
|
||||
|
||||
static void ExportParsToSvgInner(PartGroup PartGroup, StringBuilder sb, List<string> foundJoints)
|
||||
{
|
||||
foreach (string key in PartGroup.Keys)
|
||||
foreach (string key in PartGroup.pars.Keys)
|
||||
{
|
||||
var val = PartGroup[key];
|
||||
if (val is PartGroup childPars)
|
||||
@@ -452,13 +452,13 @@ class Program
|
||||
{
|
||||
if (!ShapePart.Dra) return;
|
||||
|
||||
var bx = ShapePart.BasePoint.X;
|
||||
var by = ShapePart.BasePoint.Y;
|
||||
var px = ShapePart.Position.X;
|
||||
var py = ShapePart.Position.Y;
|
||||
var angle = ShapePart.Angle;
|
||||
var sx = ShapePart.Size * ShapePart.SizeX;
|
||||
var sy = ShapePart.Size * ShapePart.SizeY;
|
||||
var bx = ShapePart.GetBasePoint().X;
|
||||
var by = ShapePart.GetBasePoint().Y;
|
||||
var px = ShapePart.GetPosition().X;
|
||||
var py = ShapePart.GetPosition().Y;
|
||||
var angle = ShapePart.GetAngle();
|
||||
var sx = ShapePart.GetSize() * ShapePart.GetSizeX();
|
||||
var sy = ShapePart.GetSize() * ShapePart.GetSizeY();
|
||||
|
||||
var hasTransform = System.Math.Abs(bx) > 0.001 || System.Math.Abs(by) > 0.001
|
||||
|| System.Math.Abs(px) > 0.001 || System.Math.Abs(py) > 0.001
|
||||
@@ -467,22 +467,22 @@ class Program
|
||||
if (hasTransform)
|
||||
sb.Append($"<g transform=\"translate({F(px)},{F(py)}) rotate({F(angle)}) scale({F(sx)},{F(sy)}) translate({F(-bx)},{F(-by)})\">");
|
||||
|
||||
foreach (var outObj in ShapePart.OP)
|
||||
foreach (var outObj in ShapePart.GetOP())
|
||||
{
|
||||
var points = outObj.ps;
|
||||
if (points.Count < 2) continue;
|
||||
|
||||
var d = BuildSvgPath(points, outObj.Tension, ShapePart.Closed);
|
||||
var fill = ShapePart.Closed ? "#cccccc" : "none";
|
||||
var d = BuildSvgPath(points, outObj.Tension, ShapePart.IsClosed);
|
||||
var fill = ShapePart.IsClosed ? "#cccccc" : "none";
|
||||
var stroke = outObj.Outline ? "#000000" : "none";
|
||||
var sw_val = outObj.Outline ? System.Math.Max(ShapePart.PenWidth, 0.001) : 0.0;
|
||||
var sw_val = outObj.Outline ? System.Math.Max(ShapePart.GetPenWidth(), 0.001) : 0.0;
|
||||
sb.AppendLine($"<path d=\"{d}\" fill=\"{fill}\" stroke=\"{stroke}\" stroke-width=\"{F(sw_val)}\"/>");
|
||||
}
|
||||
|
||||
if (hasTransform)
|
||||
sb.Append("</g>");
|
||||
|
||||
foreach (var joi in ShapePart.JP)
|
||||
foreach (var joi in ShapePart.GetJP())
|
||||
{
|
||||
foundJoints.Add($"{F(joi.Joint.X)},{F(joi.Joint.Y)}");
|
||||
}
|
||||
@@ -579,15 +579,15 @@ class Program
|
||||
|
||||
static JObject ExportDifs(VariantGrid VariantGrid)
|
||||
{
|
||||
int xCount = VariantGrid.CountX;
|
||||
int yCount = VariantGrid.CountY;
|
||||
int xCount = VariantGrid.GetCountX();
|
||||
int yCount = VariantGrid.GetCountY();
|
||||
|
||||
var result = new JObject
|
||||
{
|
||||
["Tag"] = VariantGrid.Tag ?? "",
|
||||
["ValueX"] = VariantGrid.ValueX,
|
||||
["ValueY"] = VariantGrid.ValueY,
|
||||
["CountX"] = xCount,
|
||||
["GetCountX()"] = xCount,
|
||||
["CountY"] = yCount,
|
||||
["VariantGrid"] = new JArray()
|
||||
};
|
||||
@@ -616,7 +616,7 @@ class Program
|
||||
["Children"] = new JObject()
|
||||
};
|
||||
|
||||
foreach (string key in PartGroup.Keys)
|
||||
foreach (string key in PartGroup.pars.Keys)
|
||||
{
|
||||
var val = PartGroup[key];
|
||||
if (val is PartGroup childPars)
|
||||
@@ -638,18 +638,18 @@ class Program
|
||||
{
|
||||
["Tag"] = ShapePart.Tag ?? "",
|
||||
["Dra"] = ShapePart.Dra,
|
||||
["PenWidth"] = ShapePart.PenWidth,
|
||||
["Closed"] = ShapePart.Closed,
|
||||
["BasePoint"] = ExportVec(ShapePart.BasePoint),
|
||||
["Position"] = ExportVec(ShapePart.Position),
|
||||
["Angle"] = ShapePart.Angle,
|
||||
["Size"] = ShapePart.Size,
|
||||
["SizeX"] = ShapePart.SizeX,
|
||||
["SizeY"] = ShapePart.SizeY,
|
||||
["PenWidth"] = ShapePart.GetPenWidth(),
|
||||
["Closed"] = ShapePart.IsClosed,
|
||||
["BasePoint"] = ExportVec(ShapePart.GetBasePoint()),
|
||||
["Position"] = ExportVec(ShapePart.GetPosition()),
|
||||
["Angle"] = ShapePart.GetAngle(),
|
||||
["Size"] = ShapePart.GetSize(),
|
||||
["SizeX"] = ShapePart.GetSizeX(),
|
||||
["SizeY"] = ShapePart.GetSizeY(),
|
||||
["CurveOutline"] = new JArray()
|
||||
};
|
||||
|
||||
foreach (var outObj in ShapePart.OP)
|
||||
foreach (var outObj in ShapePart.GetOP())
|
||||
{
|
||||
var outJ = new JObject
|
||||
{
|
||||
@@ -662,10 +662,10 @@ class Program
|
||||
((JArray)result["CurveOutline"]).Add(outJ);
|
||||
}
|
||||
|
||||
if (ShapePart.JP.Count > 0)
|
||||
if (ShapePart.GetJP().Count > 0)
|
||||
{
|
||||
result["Joints"] = new JArray();
|
||||
foreach (var joi in ShapePart.JP)
|
||||
foreach (var joi in ShapePart.GetJP())
|
||||
((JArray)result["Joints"]).Add(ExportVec(joi.Joint));
|
||||
}
|
||||
|
||||
@@ -676,7 +676,7 @@ class Program
|
||||
{
|
||||
var result = ExportPar(ShapePartT);
|
||||
result["Text"] = ShapePartT.Text ?? "";
|
||||
result["FontSize"] = ShapePartT.FontSize;
|
||||
result["FontSize"] = ShapePartT.GetFontSize();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
28
Solution.sln
28
Solution.sln
@@ -9,6 +9,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "2DGAMELIB", "2DGAMELIB\2DGA
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlaveMatrix.Extract", "SlaveMatrix.Extract\SlaveMatrix.Extract.csproj", "{DAEAB23A-E099-432E-9C85-39D0853BD78A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlaveEngine.Assets", "SlaveEngine.Assets\SlaveEngine.Assets.csproj", "{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlaveEngine.AssetBuilder", "SlaveEngine.AssetBuilder\SlaveEngine.AssetBuilder.csproj", "{DE1800EA-9AB1-4558-BB60-40BAA505F475}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -55,6 +59,30 @@ Global
|
||||
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DE1800EA-9AB1-4558-BB60-40BAA505F475}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Reference in New Issue
Block a user