Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
be0a8da09This file is pinned to the selected release. Download the original to open it in your design tool.
using System;
using System.Collections.Generic;
using System.Linq;
using GodotExt;
using OpenScadGraphEditor.Library.External;
using OpenScadGraphEditor.Library.IO;
using OpenScadGraphEditor.Utils;
namespace OpenScadGraphEditor.Library
{
public class ScadProject : IReferenceResolver
{
private readonly IReferenceResolver _parentResolver;
private readonly Dictionary<string, FunctionDescription> _projectFunctionDescriptions =
new Dictionary<string, FunctionDescription>();
private readonly Dictionary<string, ModuleDescription> _projectModuleDescriptions =
new Dictionary<string, ModuleDescription>();
private readonly Dictionary<string, VariableDescription> _projectVariables =
new Dictionary<string, VariableDescription>();
private readonly Dictionary<string, ExternalReference> _externalReferences = new Dictionary<string, ExternalReference>();
private readonly HashSet<ScadGraph> _modules = new HashSet<ScadGraph>();
private readonly HashSet<ScadGraph> _functions = new HashSet<ScadGraph>();
public IEnumerable<ScadGraph> Modules => _modules.OrderBy(x => x.Description.Name);
public IEnumerable<ScadGraph> Functions => _functions.OrderBy(x => x.Description.Name);
public IEnumerable<ScadGraph> AllDeclaredInvokables => Functions.Concat(Modules).Append(MainModule);
public IEnumerable<VariableDescription> Variables => _projectVariables.Values.OrderBy(x => x.Name);
public IEnumerable<ExternalReference> ExternalReferences => _externalReferences.Values.OrderBy(x => x.IncludePath);
public ScadGraph MainModule { get; private set; }
public string ProjectPath { get; private set; }
public ScadProject(IReferenceResolver parentResolver)
{
_parentResolver = parentResolver;
var mainModuleGraph = new ScadGraph();
mainModuleGraph.Main();
MainModule = mainModuleGraph;
}
public FunctionDescription ResolveFunctionReference(string id)
{
if (_projectFunctionDescriptions.TryGetValue(id, out var functionDescription))
{
return functionDescription;
}
var fromExternal = _externalReferences
.Values
.SelectMany(it => it.Functions)
.FirstOrDefault(it => it.Id == id);
return fromExternal ?? _parentResolver.ResolveFunctionReference(id);
}
public ModuleDescription ResolveModuleReference(string id)
{
if (_projectModuleDescriptions.TryGetValue(id, out var moduleDescription))
{
return moduleDescription;
}
var fromExternal = _externalReferences
.Values
.SelectMany(it => it.Modules)
.FirstOrDefault(it => it.Id == id);
return fromExternal ?? _parentResolver.ResolveModuleReference(id);
}
public VariableDescription ResolveVariableReference(string id)
{
if (_projectVariables.TryGetValue(id, out var variableDescription))
{
return variableDescription;
}
var fromExternal = _externalReferences
.Values
.SelectMany(it => it.Variables)
.FirstOrDefault(it => it.Id == id);
return fromExternal ?? _parentResolver.ResolveVariableReference(id);
}
public ExternalReference ResolveExternalReference(string id)
{
if (_externalReferences.TryGetValue(id, out var result))
{
return result;
}
return _parentResolver.ResolveExternalReference(id);
}
private void Clear()
{
_projectFunctionDescriptions.Clear();
_projectModuleDescriptions.Clear();
_projectVariables.Clear();
_modules.ForAll(it => it.Discard());
_functions.ForAll(it => it.Discard());
MainModule.Discard();
MainModule = null;
_modules.Clear();
_functions.Clear();
_externalReferences.Clear();
}
public void Load(SavedProject project, string projectPath)
{
Clear();
ProjectPath = projectPath;
// Step 1: load function descriptions so we can resolve them in step 4
foreach (var function in project.Functions)
{
_projectFunctionDescriptions[function.Description.Id] =
(FunctionDescription) function.Description.FromSavedState();
}
foreach (var module in project.Modules)
{
_projectModuleDescriptions[module.Description.Id] =
(ModuleDescription) module.Description.FromSavedState();
}
// Step 2: load variable descriptions so we can resolve them in step 4
foreach (var variable in project.Variables)
{
_projectVariables.Add(variable.Id, variable.FromSavedState());
}
// Step 3: load external references so we can resolve them in step 4
foreach (var external in project.ExternalReferences)
{
_externalReferences.Add(external.Id, external.FromSavedState());
}
// Step 4: load the actual graphs, which can now resolve references to other functions.
foreach (var function in project.Functions)
{
var functionContext = new ScadGraph();
functionContext.LoadFrom(function, _projectFunctionDescriptions[function.Description.Id], this);
_functions.Add(functionContext);
}
foreach (var module in project.Modules)
{
var moduleContext = new ScadGraph();
moduleContext.LoadFrom(module, _projectModuleDescriptions[module.Description.Id], this);
_modules.Add(moduleContext);
}
MainModule = new ScadGraph();
MainModule.LoadFrom(project.MainModule, project.MainModule.Description.FromSavedState(), this);
}
public SavedProject Save()
{
var result = Prefabs.New<SavedProject>();
_externalReferences.ForAll(it => result.ExternalReferences.Add(it.Value.ToSavedState()));
foreach (var function in _functions)
{
result.Functions.Add(function.ToSavedState());
}
foreach (var module in _modules)
{
result.Modules.Add(module.ToSavedState());
}
foreach (var variable in _projectVariables.Values)
{
result.Variables.Add(variable.ToSavedState());
}
{
result.MainModule = MainModule.ToSavedState();
}
return result;
}
public string Render()
{
return string.Join("\n",
_externalReferences.Select(it => it.Value.Render())
.Concat(_modules.Select(it => it.Render()))
.Concat(_functions.Select(it => it.Render()))
.Append(MainModule.Render())
.Where(it => it.Length > 0)
);
}
public void Discard()
{
_modules.ForAll(it => it.Discard());
_functions.ForAll(it => it.Discard());
MainModule?.Discard();
}
public ScadGraph AddInvokable(InvokableDescription invokableDescription)
{
var graph = new ScadGraph();
graph.NewFromDescription(invokableDescription);
switch (invokableDescription)
{
case FunctionDescription functionDescription:
_functions.Add(graph);
_projectFunctionDescriptions[functionDescription.Id] = functionDescription;
break;
case ModuleDescription moduleDescription:
_modules.Add(graph);
_projectModuleDescriptions[moduleDescription.Id] = moduleDescription;
break;
default:
throw new ArgumentOutOfRangeException();
}
return graph;
}
public void RemoveInvokable(InvokableDescription description)
{
var graph = FindDefiningGraph(description);
switch (description)
{
case FunctionDescription _:
_functions.Remove(graph);
_projectFunctionDescriptions.Remove(description.Id);
graph.Discard();
break;
case ModuleDescription _:
_modules.Remove(graph);
_projectModuleDescriptions.Remove(description.Id);
graph.Discard();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public void AddVariable(VariableDescription variableDescription)
{
_projectVariables[variableDescription.Id] = variableDescription;
}
public void RemoveVariable(VariableDescription variableDescription)
{
_projectVariables.Remove(variableDescription.Id);
}
public ScadGraph FindDefiningGraph(InvokableDescription invokableDescription)
{
return AllDeclaredInvokables.First(it => it.Description.Id == invokableDescription.Id);
}
public void AddExternalReference(ExternalReference reference)
{
GdAssert.That(!_externalReferences.ContainsKey(reference.Id), "Tried to add an external reference twice.");
_externalReferences[reference.Id] = reference;
}
public void RemoveExternalReference(ExternalReference externalReference)
{
var removed = _externalReferences.Remove(externalReference.Id);
GdAssert.That(removed, "Tried to remove an external reference that was not present.");
}
public bool IsDefinedInThisProject(InvokableDescription invokableDescription)
{
switch (invokableDescription)
{
case FunctionDescription functionDescription:
return IsDefinedInThisProject(functionDescription);
case ModuleDescription moduleDescription:
return IsDefinedInThisProject(moduleDescription);
case MainModuleDescription mainModuleDescription:
return MainModule.Description.Id == mainModuleDescription.Id;
default:
throw new ArgumentOutOfRangeException();
}
}
public bool TryGetExternalReferenceHolding(InvokableDescription description, out ExternalReference result)
{
foreach (var reference in _externalReferences.Values)
{
switch (description)
{
case FunctionDescription _ when reference.Functions.Any(it => it.Id == description.Id):
result = reference;
return true;
case ModuleDescription _ when reference.Modules.Any(it => it.Id == description.Id):
result = reference;
return true;
}
}
result = default;
return false;
}
public bool TryGetExternalReferenceHolding(VariableDescription description, out ExternalReference result)
{
foreach (var externalReference in _externalReferences.Values
.Where(externalReference => externalReference.Variables.Any(it => it.Id == description.Id)))
{
result = externalReference;
return true;
}
result = default;
return false;
}
private bool IsDefinedInThisProject(FunctionDescription description)
{
return _projectFunctionDescriptions.ContainsKey(description.Id);
}
private bool IsDefinedInThisProject(ModuleDescription description)
{
return _projectModuleDescriptions.ContainsKey(description.Id);
}
public bool IsDefinedInThisProject(VariableDescription description)
{
return _projectVariables.ContainsKey(description.Id);
}
}
}