feat: add external dependencies support

This commit is contained in:
Hugo Pointcheval 2023-11-27 15:27:44 +01:00
parent e7fbdb7a50
commit 2e368a01a3
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
16 changed files with 842 additions and 8 deletions

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<Description>Dali</Description>
<PackageTags>Dali</PackageTags>
<Copyright>Copyright 2018 (c) INSA Rennes. All Right reserved.</Copyright>
<Author>Éric Anquetil</Author>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.1.167</Version>
<AssemblyVersion>1.1.167</AssemblyVersion>
<FileVersion>1.1.167</FileVersion>
<Configurations>Debug;Release;ReleaseObfuscated</Configurations>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>2.0</OldToolsVersion>
</PropertyGroup>
<ItemGroup>
<!-- Need to use dll ref here -->
<Reference Include="../external/Autofac.dll" />
</ItemGroup>
<Target Name="CopyPackage" AfterTargets="Pack">
<Copy SourceFiles="bin\Release\$(PackageId).$(PackageVersion).nupkg" DestinationFolder="Release\" />
</Target>
</Project>

View File

@ -0,0 +1,29 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace DALI.ToolKit
{
public class DaliStroke : IEnumerable<DaliStrokePoint>
{
public Guid Id { get; private set; } = Guid.NewGuid();
private IEnumerable<DaliStrokePoint> _points;
public DaliStroke(IEnumerable<DaliStrokePoint> points)
{
_points = points.ToList();
}
public IEnumerator<DaliStrokePoint> GetEnumerator()
{
return _points.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _points.GetEnumerator();
}
}
}

View File

@ -0,0 +1,79 @@
using System;
namespace DALI.ToolKit
{
public struct DaliStrokePoint: ICloneable
{
public DaliStrokePoint(double x, double y)
{
X = x;
Y = y;
T = TimeSpan.MaxValue;
P = 0.5;
}
public DaliStrokePoint(double x, double y, TimeSpan t, double p)
{
X = x;
Y = y;
P = p;
T = t;
}
public bool IsCoordinatesOnly { get => T == TimeSpan.MaxValue && P == 0.5; }
public double X { get; set; }
public double Y { get; set; }
public double P { get; set; }
public TimeSpan T { get; set; }
public override string ToString()
{
return string.Format("[DaliStrokePoint: X={0}, Y={1}, P={2}, T={3}]", X, Y, P, T);
}
public override bool Equals(object obj)
{
if (!(obj is DaliStrokePoint))
return false;
DaliStrokePoint ds = (DaliStrokePoint)obj;
double epsylon = 0.01;
var diffX = Math.Abs(ds.X - this.X);
var diffY = Math.Abs(ds.Y - this.Y);
if (diffX < epsylon && diffY < epsylon)
{
return true;
}
else
return false;
}
public bool EqualsEpsylon(DaliStrokePoint ds, double epsylon)
{
var diffX = Math.Abs(ds.X - this.X);
var diffY = Math.Abs(ds.Y - this.Y);
if (diffX < epsylon && diffY < epsylon)
{
return true;
}
else
return false;
}
public override int GetHashCode()
{
return this.GetHashCode();
}
public object Clone()
{
return this.MemberwiseClone();
}
}
}

View File

@ -0,0 +1,48 @@
using DALI.ToolKit;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace DALI.ToolKit.IO
{
public static class InkmlExtensions
{
static readonly string inkmlPointFormat = "{0:0.####} {1:0.####}";
static readonly string inkmlPointFormatPressure = "{0:0.####} {1:0.####} {2:0.####}";
static readonly string inkmlPointFormatPressureTime = "{0:0.####} {1:0.####} {2:0.####} {3:0}";
static readonly string inkmlSeparator = ", ";
public static string ToInkml(this IEnumerable<DaliStrokePoint> trace, bool pressure = true)
{
return String.Join(inkmlSeparator, trace.Select(p => p.ToInkml(pressure)));
}
public static string ToInkml(this DaliStrokePoint point, bool pressure = true)
{
if (pressure)
{
return String.Format(CultureInfo.InvariantCulture, inkmlPointFormatPressureTime, point.X, point.Y, point.P, point.T.TotalMilliseconds);
}
else
return String.Format(CultureInfo.InvariantCulture, inkmlPointFormat, point.X, point.Y);
}
public static DaliStroke ToTrace(this string inkml, DateTime? startTime = null)
{
return new DaliStroke(inkml.Split(',').Select(ps => InkmlExtensions.ToTracePoint(ps)));//.ToTrace(startTime);
}
public static DaliStrokePoint ToTracePoint(this string inkml, bool isPenUp = false)
{
var split = inkml.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (split.Length == 4)
return new DaliStrokePoint(Double.Parse(split[0], CultureInfo.InvariantCulture), Double.Parse(split[1], CultureInfo.InvariantCulture), TimeSpan.FromMilliseconds(Int32.Parse(split[3])), Single.Parse(split[2], CultureInfo.InvariantCulture));
else
throw new InvalidOperationException("Wrong format " + inkml);
}
}
}

View File

@ -0,0 +1,78 @@
using DALI.ToolKit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
namespace DALI.ToolKit.IO
{
/// <summary>
/// InkmlFile use for Architecture project
///
/// To do : refactoring for more generic type (Geometry, Architecture ...)
/// </summary>
public class InkmlFile
{
string filepath;
Dictionary<string, DaliStroke> pool = new Dictionary<string, DaliStroke>();
List<Input> inputs = new List<Input>();
public InkmlFile(string filepath)
{
this.filepath = filepath;
Name = Path.GetFileName(filepath);
}
public string Name
{ get; private set; }
public string Filepath
{ get { return filepath; } }
public IEnumerable<Input> Inputs { get { return inputs; } }
public void Load()
{
var doc = new XmlDocument();
doc.Load(filepath);
XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
manager.AddNamespace("i", @"http://www.w3.org/2003/InkML");
// discard annotations
/* foreach (XmlNode annotation in doc.SelectNodes("i:ink/i:annotation", manager))
{
var key = annotation.Attributes["type"].InnerText;
annotations.Add(new Annotation(key, annotation.InnerText));
}*/
foreach (XmlNode stroke in doc.SelectNodes("i:ink/i:trace", manager))
{
var id = stroke.Attributes["id"].InnerText;
if (stroke.Attributes["timeOffset"] != null)
{
long timeOffsetParsed;
if (Int64.TryParse(stroke.Attributes["timeOffset"].InnerText, out timeOffsetParsed))
{
var timeOffset = Input.DateTimeFromUnixMilliSeconds(timeOffsetParsed);
pool.Add(id, InkmlExtensions.ToTrace(stroke.InnerText, timeOffset));
}
}
else
{
pool.Add(id, InkmlExtensions.ToTrace(stroke.InnerText));
}
}
foreach (XmlNode node in doc.SelectNodes("i:ink/i:traceGroup", manager))
{
Input input = new Input(node, manager, pool);
inputs.Add(input);
}
}
}
}

151
Dali.Toolkit/IO/Input.cs Normal file
View File

@ -0,0 +1,151 @@
using DALI.ToolKit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace DALI.ToolKit.IO
{
public class Input
{
static readonly DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0);
public static DateTime DateTimeFromUnixMilliSeconds(long timestamp)
{
return unixEpoch.AddMilliseconds(timestamp);
}
List<DaliStroke> traces = new List<DaliStroke>();
public Input(XmlNode node, XmlNamespaceManager manager, Dictionary<string, DaliStroke> pool)
{
Id = String.Empty;
var attr = node.Attributes["xml:id"];
if (attr != null)
Id = attr.InnerText;
var attrTimeOffset = node.Attributes["timeOffset"];
if (attrTimeOffset != null)
{
long timeOffsetParsed;
if (Int64.TryParse(attrTimeOffset.InnerText, out timeOffsetParsed))
TimeOffset = timeOffsetParsed;
}
/** discard annotations */
/*foreach (XmlNode annotation in node.SelectNodes("i:annotation", manager))
{
var key = annotation.Attributes["type"].InnerText;
annotations.Add(new Annotation(key, annotation.InnerText));
}
*/
/* discard traceview */
/* foreach (XmlNode view in node.SelectNodes("i:traceView", manager))
{
var dataref = view.Attributes["traceDataRef"].InnerText;
if (pool.ContainsKey(dataref))
traces.Add(pool[dataref]);
else
refs.Add(new Reference() { ReferenceId = dataref });
}*/
foreach (XmlNode trace in node.SelectNodes("i:trace", manager))
{
var traceType = trace.Attributes["type"];
var traceErased = trace.Attributes["erased"];
bool isPenUpTrace = traceType != null && String.Equals(traceType.Value, "penUp");
bool isErasedTrace = traceErased != null && String.Equals(traceErased.Value, "true");
if (trace.Attributes["timeOffset"] != null)
{
long timeOffsetParsed;
if (Int64.TryParse(trace.Attributes["timeOffset"].InnerText, out timeOffsetParsed))
{
var timeOffset = DateTimeFromUnixMilliSeconds(timeOffsetParsed);
traces.Add(InkmlExtensions.ToTrace(trace.InnerText, timeOffset));
}
}
else
{
traces.Add(InkmlExtensions.ToTrace(trace.InnerText));
}
}
}
public Input(string id, IEnumerable<DaliStroke> traces)
{
Id = id;
this.traces = new List<DaliStroke>(traces);
}
private string id;
public string Id
{
get
{
return id;
}
set
{
id = value;
}
}
private long timeOffset;
public long TimeOffset
{
get
{
return timeOffset;
}
set
{
timeOffset = value;
}
}
public IList<DaliStroke> Traces
{
get { return traces; }
set
{
traces.Clear();
foreach (var trace in value)
traces.Add(trace);
}
}
public event EventHandler DeleteRequested;
//ICommand extract;
//public ICommand Extract
//{
// get
// {
// if (extract == null)
// extract = new RelayCommand(() =>
// {
// strokefeatures.Clear();
// foreach (var trace in traces)
// {
// strokefeatures.Add(extractor.Extract(getPoints(trace)).ToList());
// }
// features = extractor.Extract(getPoints()).ToList();
// RaisePropertyChanged(vm => vm.Features);
// RaisePropertyChanged(vm => vm.StrokeFeatures);
// });
// return extract;
// }
//}
}
}

View File

@ -0,0 +1,24 @@
using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DALI.Utils
{
/// <summary>
/// Container for Autofac for Dali engine
/// </summary>
public class ContainerSingleton
{
public static IContainer Container { get; private set; }
public static ILifetimeScope Scope { get; private set; }
public ContainerSingleton(IContainer container)
{
Container = container;
Scope = container.BeginLifetimeScope();
}
}
}

View File

@ -0,0 +1,184 @@
using System;
using System.Linq;
namespace DALI.Toolkit.Utils
{
/// <summary>
/// Value name for a point
/// </summary>
public class NamePoint : IEquatable<NamePoint>
{
public char Letter { get; set; }
public int Index { get; set; }
public bool HavePrime { get; set; }
public NamePoint()
{
Letter = '*';
Index = 0;
HavePrime = false;
}
public NamePoint(string name)
{
if (name.Length > 0)
{
Letter = name.First();
char lastChar = name.Last();
if (lastChar.ToString().Equals(ParamNamePoint.PrimeValue))
{
HavePrime = true;
}
else
{
HavePrime = false;
}
if (HavePrime)
{
//Case A'
if (name.Length <= 2)
{
Index = 0;
}
//Cas A1'
else
{
string indexString = name.Substring(1, name.Length - 2);
Index = Int32.Parse(indexString);
}
}
else
{
//Case A
if (name.Length <= 1)
{
Index = 0;
}
//Case A1
else
{
string indexString = name.Substring(1, name.Length - 1);
Index = Int32.Parse(indexString);
}
}
}
else
{
throw new Exception("Error name empty for point");
}
}
/// <summary>
/// Allow to sorted => A A' B B' A1 B1
/// </summary>
/// <param name="nametoCompate"></param>
/// <returns></returns>
public bool NameIsStrictUpTo(NamePoint nametoCompate)
{
if (this.Index > nametoCompate.Index)
{
return true;
}
else if (this.Index == nametoCompate.Index)
{
if (this.Letter != nametoCompate.Letter)
{
if (this.Letter < nametoCompate.Letter)
{
return false;
}
else
{
return true;
}
}
else
{
//A1 and A1'
if (this.HavePrime && !nametoCompate.HavePrime)
{
return true;
}
else
{
return false;
}
}
}
else
{
return false;
}
}
public NamePoint GetNextName()
{
char nextLetter = Letter;
if(nextLetter == 'Z')
{
nextLetter = 'A';
int nextIndex = Index + 1;
string nextName = nextLetter.ToString() + nextIndex;
return new NamePoint(nextName);
}
else
{
nextLetter++;
string nextName = nextLetter.ToString() + Index;
return new NamePoint(nextName);
}
}
public override string ToString()
{
if(Index != 0)
{
if(HavePrime)
{
return Letter.ToString() + Index + ParamNamePoint.PrimeValue;
}
else
{
return Letter.ToString() + Index;
}
}
else
{
if (HavePrime)
{
return Letter.ToString() + ParamNamePoint.PrimeValue;
}
else
{
return Letter.ToString();
}
}
}
public override int GetHashCode()
{
return Letter.GetHashCode() + Index.GetHashCode() + HavePrime.GetHashCode();
}
public bool Equals(NamePoint other)
{
if (this.Letter == other.Letter && this.Index == other.Index && this.HavePrime.Equals(other.HavePrime))
{
return true;
}
else
{
return false;
}
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace DALI.Toolkit.Utils
{
/// <summary>
/// Define constant for Name Point => to avoid typo mystake
/// </summary>
public static class ParamNamePoint
{
//Param for prime for name point
public const string PrimeValue = "'";
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DALI.Utils
{
/// <summary>
/// Define param for each segmentor
/// </summary>
public static class ParamSegmentation
{
//Param for Douglas Peucker segmentor
public const Double ToleranceDouglasPeucker =10;
//Angle Param for Douglas Peucker segmentor, to retreive angular points
public const Double AngularTolerance = 150;
public const Double NbSegmentationMax = 7;
}
}

View File

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DALI.Toolkit.Utils
{
public class VerticesName
{
private SortedDictionary<NamePoint, int> _names;
public static VerticesName _instance = null;
public static VerticesName Instance
{
get
{
if (_instance == null)
{
_instance = new VerticesName()
{
_names = new SortedDictionary<NamePoint, int>(new SortNamePointComparer())
};
}
return _instance;
}
}
public VerticesName()
{
}
public string GetNextNamePoint(bool addNextName)
{
if (_names.Count == 0)
{
NamePoint first = new NamePoint("A");
if (addNextName)
{
_names.Add(first, 1);
}
return first.ToString();
}
else
{
var last = _names.Last().Key;
NamePoint nextName = last.GetNextName();
if (addNextName)
{
_names.Add(nextName, 1);
}
return nextName.ToString();
}
}
public void AddName(string name)
{
NamePoint n = new NamePoint(name);
if (_names.ContainsKey(n))
{
var newCountName = _names[n];
newCountName++;
_names[n] = newCountName;
}
else
{
_names.Add(n, 1);
}
}
public void AddNameSubSegment(string name)
{
NamePoint n = new NamePoint(name);
if (_names.ContainsKey(n))
{
}
else
{
_names.Add(n, 1);
}
}
public void RemoveName(string name)
{
NamePoint n = new NamePoint(name);
if (_names.ContainsKey(n))
{
var countName = _names[n];
countName--;
if (countName == 0)
{
_names.Remove(n);
}
}
}
public void ResetCounter()
{
_names.Clear();
}
}
public class SortNamePointComparer : IComparer<NamePoint>
{
public int Compare(NamePoint n1, NamePoint n2)
{
if(n1.NameIsStrictUpTo(n2))
{
return 1;
}
else if(n1.Equals(n2))
{
return 0;
}
else
{
return -1;
}
}
}
}

View File

@ -23,7 +23,7 @@ Simply run the build script
More advanced usage:
> Context: You want to build a library named `AwesomeFeature` for Android only, and you have the Android NDK installed in `$ANDROID_NDK_ROOT` but not in `$ANDROID_NDK_HOME`.
> Context: You want to build a library named `AwesomeFeature` for Android only, and you have the Android NDK installed in `$ANDROID_NDK_ROOT` but not in `$ANDROID_NDK_HOME` .
```bash
./build.sh -i AwesomeFeature --ndk $ANDROID_NDK_ROOT --no-ios
@ -40,14 +40,15 @@ Usage: ./build.sh [options]
Options:
-h, --help Print this help and exit
-i Input project name (required)
-g Generated project name (default: <input-project-name>.Generated)
-g Generated project name (default: <input-project-name>. Generated)
-o Output folder name (default: libs)
-y Assume yes to all prompts
--ndk Android NDK path (default: ANDROID_NDK_HOME environment variable)
--no-android Disable Android build (default: false)
--no-ios Disable iOS build (default: false)
--input-spec Input project .NET spec (default: net8.0)
--output-spec Output project .NET spec (default: net8.0)
--input-spec Input project . NET spec (default: net8.0)
--output-spec Output project . NET spec (default: net8.0)
```
## What is this?
@ -62,3 +63,40 @@ This script:
* iOS:
* Run `beyondnetgen` and follow full process to get xframework
* Copy the generated files to the output folder
## Notes for external dependencies
Sometimes, you need to use external dependencies in your library. For example, your library may use Autofac or Newtonsoft.Json.
In this case, you need to add the dependencies as **DLL assemblies** in the `external` folder.
> **Warning** You must modify the csproj of the original library to reference the external dependencies.
```xml
<!-- In Dali.Toolkit for example, replace original references by this -->
<ItemGroup>
<!-- Need to use dll ref here -->
<Reference Include="../external/Autofac.dll" />
</ItemGroup>
```
Then during the build, the script will automatically add the external dependencies to the generated project.
### iOS
For iOS you also need to add path to each dll in `template.beyondnetgen.ios.config.json`:
```json
{
"AssemblyPath": "INPUT_PROJECT_NAME/bin/Release/INPUT_NET_SPEC/INPUT_PROJECT_NAME.dll",
"CSharpUnmanagedOutputPath": "GENERATED_PROJECT_NAME/Generated.cs",
"COutputPath": "GENERATED_PROJECT_NAME/Generated.h",
// Add this
"AssemblySearchPaths": [
"external/Autofac.dll"
],
"Build": {
"Target": "apple-universal"
}
}
```

View File

@ -98,6 +98,19 @@ build_android() {
cd $generated_project_dir
# For each .dll in external folder add it as a reference in the generated project
search_dir="$script_dir/external"
for entry in "$search_dir"/*; do
dll_name=$(basename $entry)
log "Adding reference to $dll_name"
# Add reference to the generated project by editing the csproj file
# Search for the line <!-- ADD REFERENCES HERE (DO NOT DELETE THIS)-->
# and add a reference to the dll below it
sed -i '' "/<!-- ADD REFERENCES HERE (DO NOT DELETE THIS)-->/a \\
<Reference Include=\""$entry"\" />\\
" "$generated_project_dir/$generated_project_name.csproj"
done
declare -A abis
abis["arm64"]="arm64-v8a"
abis["x64"]="x86_64"

BIN
external/Autofac.dll vendored Normal file

Binary file not shown.

View File

@ -2,9 +2,6 @@
"AssemblyPath": "INPUT_PROJECT_NAME/bin/Release/INPUT_NET_SPEC/INPUT_PROJECT_NAME.dll",
"CSharpUnmanagedOutputPath": "GENERATED_PROJECT_NAME/Generated.cs",
"COutputPath": "GENERATED_PROJECT_NAME/Generated.h",
"AssemblySearchPaths": [
"MathLib/bin/Release/net8.0/publish/MathLib.dll"
],
"Build": {
"Target": "apple-universal"
}

View File

@ -90,7 +90,7 @@
<!-- Project References -->
<ItemGroup>
<ProjectReference Include="..\INPUT_PROJECT_NAME\INPUT_PROJECT_NAME.csproj" />
<Reference Include="../MathLib/bin/Release/net8.0/publish/MathLib.dll" />
<!-- ADD REFERENCES HERE (DO NOT DELETE THIS)-->
</ItemGroup>
</Project>