diff --git a/CADability.Avalonia/CADability.Avalonia.csproj b/CADability.Avalonia/CADability.Avalonia.csproj
new file mode 100644
index 00000000..7010143d
--- /dev/null
+++ b/CADability.Avalonia/CADability.Avalonia.csproj
@@ -0,0 +1,24 @@
+
+
+ net8.0
+ enable
+ app.manifest
+ true
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
+
+
+
+
+
diff --git a/CADability.Avalonia/CadCanvas.axaml b/CADability.Avalonia/CadCanvas.axaml
new file mode 100644
index 00000000..2579c1b5
--- /dev/null
+++ b/CADability.Avalonia/CadCanvas.axaml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/CADability.Avalonia/CadCanvas.axaml.cs b/CADability.Avalonia/CadCanvas.axaml.cs
new file mode 100644
index 00000000..aeafc8e5
--- /dev/null
+++ b/CADability.Avalonia/CadCanvas.axaml.cs
@@ -0,0 +1,83 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Controls;
+using CADability.GeoObject;
+using CADability.Substitutes;
+using CADability.UserInterface;
+using System;
+using System.Numerics;
+using static Avalonia.OpenGL.GlConsts;
+
+namespace CADability.Avalonia
+{
+ public partial class CadCanvas: UserControl, ICanvas
+ {
+ private static CADability.Substitutes.Rectangle Subst(Rect v)
+ {
+ return new Substitutes.Rectangle((int)v.X, (int)v.Y, (int)v.Width, (int)v.Height);
+ }
+
+ private PaintToOpenGL paintTo3D;
+ private IFrame frame;
+ private IView view;
+ private String currentCursor;
+
+ public CadCanvas()
+ {
+ InitializeComponent();
+ // CadCanvasControl canvasControl = new CadCanvasControl();
+ paintTo3D = new PaintToOpenGL();
+ this.Content = paintTo3D;
+ }
+
+ void ICanvas.Invalidate() {}
+
+ Rectangle ICanvas.ClientRectangle => Subst(base.Bounds);
+
+ IFrame ICanvas.Frame
+ {
+ get { return frame; }
+ }
+
+ string ICanvas.Cursor
+ {
+ get { return currentCursor; }
+ set { currentCursor = value; } //TODO
+ }
+
+ IPaintTo3D ICanvas.PaintTo3D
+ {
+ get { return paintTo3D; }
+ }
+
+ public event Action OnPaintDone;
+
+ void ICanvas.ShowView(IView toShow)
+ {
+ view = toShow;
+ // TODO init paintTo3D here or in constructor?
+ paintTo3D.View = view;
+ // TODO view.Connect needed?
+ }
+
+ IView ICanvas.GetView()
+ {
+ return view;
+ }
+
+ Substitutes.Point ICanvas.PointToClient(Substitutes.Point mousePosition)
+ {
+ throw new NotImplementedException();
+ }
+
+ void ICanvas.ShowContextMenu(MenuWithHandler[] contextMenu, Substitutes.Point viewPosition, System.Action collapsed) {}
+
+ Substitutes.DragDropEffects ICanvas.DoDragDrop(GeoObjectList dragList, Substitutes.DragDropEffects all)
+ {
+ throw new NotImplementedException();
+ }
+
+ void ICanvas.ShowToolTip(string toDisplay) {}
+ }
+}
diff --git a/CADability.Avalonia/CadForm.axaml b/CADability.Avalonia/CadForm.axaml
new file mode 100644
index 00000000..7e85cbb3
--- /dev/null
+++ b/CADability.Avalonia/CadForm.axaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CADability.Avalonia/CadForm.axaml.cs b/CADability.Avalonia/CadForm.axaml.cs
new file mode 100644
index 00000000..d00f7073
--- /dev/null
+++ b/CADability.Avalonia/CadForm.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia.Controls;
+
+namespace CADability.Avalonia
+{
+ public partial class CadForm : UserControl
+ {
+ // TODO this class is currently unused, we directly use CadCanvas
+ public CadForm()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/CADability.Avalonia/CadFrame.cs b/CADability.Avalonia/CadFrame.cs
new file mode 100644
index 00000000..6dbf8e97
--- /dev/null
+++ b/CADability.Avalonia/CadFrame.cs
@@ -0,0 +1,507 @@
+using CADability.GeoObject;
+using CADability.UserInterface;
+using System;
+using System.Collections.Generic;
+// using System.Drawing;
+using System.Drawing.Printing;
+using System.IO;
+using System.Threading;
+using Action = CADability.Actions.Action;
+
+namespace CADability.Avalonia
+{
+ /* TODO: change IFrame for "KernelOnly" KO
+ * Step by Step implement missing methods (copy implementation from SingleDocumentFrame)
+ * and remove interface methods from IFrame with "#if KO" ...
+ */
+ ///
+ /// Implementation of the abstract FrameImpl, doing things, you cannot do in .NET Core
+ ///
+ public class CadFrame : FrameImpl, IUIService
+ {
+ #region PRIVATE FIELDS
+
+ private ICommandHandler commandHandler;
+
+ private const string ClipFormat = "CADability.GeoObjectList.Json";
+
+ // Optional: COM-freie Verfügbarkeitsprüfung (robuster im Idle)
+ // TODO reimplement in Avalonia
+ // static class NativeClipboard
+ // {
+ // [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ // private static extern uint RegisterClipboardFormat(string lpszFormat);
+
+ // [DllImport("user32.dll")]
+ // private static extern bool IsClipboardFormatAvailable(uint format);
+
+ // private static uint _fmtId;
+ // public static bool HasMyFormat()
+ // {
+ // if (_fmtId == 0) _fmtId = RegisterClipboardFormat(ClipFormat);
+ // return IsClipboardFormatAvailable(_fmtId);
+ // }
+ // }
+
+ #endregion PRIVATE FIELDS
+
+ #region PUBLIC PROPERTIES
+
+ ///
+ /// The parent form menu
+ ///
+ // TODO reimplement in Avalonia
+ // public MenuStrip FormMenu { set; private get; }
+
+ ///
+ /// Action that delegate the progress.
+ /// In this way we can use a custom progress ui.
+ ///
+ public Action ProgressAction { get; set; }
+
+ #endregion PUBLIC PROPERTIES
+
+ ///
+ /// Constructor without form dependency.
+ ///
+ ///
+ ///
+ ///
+ // TODO reimplement in Avalonia
+ // public CadFrame(PropertiesExplorer propertiesExplorer, CadCanvas cadCanvas, ICommandHandler commandHandler)
+ // : base(propertiesExplorer, cadCanvas)
+ // {
+ // this.commandHandler = commandHandler;
+ // }
+ ///
+ /// Constructor without form dependency.
+ ///
+ ///
+ ///
+ //
+ // TODO add propertyExplorer to parameters and add to base call
+ // allows cadFrame.ControlCenter to work
+ public CadFrame(CadCanvas cadCanvas, ICommandHandler commandHandler)
+ : base(cadCanvas)
+ {
+ this.commandHandler = commandHandler;
+ }
+
+ #region FrameImpl override
+
+ public override bool OnCommand(string MenuId)
+ {
+ if (commandHandler != null && commandHandler.OnCommand(MenuId)) return true;
+ return base.OnCommand(MenuId);
+ }
+
+ public override bool OnUpdateCommand(string MenuId, CommandState CommandState)
+ {
+ if (commandHandler != null && commandHandler.OnUpdateCommand(MenuId, CommandState)) return true;
+ return base.OnUpdateCommand(MenuId, CommandState);
+ }
+
+ public override void UpdateMRUMenu(string[] mruFiles)
+ {
+ // if (this.FormMenu != null)
+ // {
+ // TODO reimplement in Avalonia
+ // foreach (ToolStripMenuItem mi in this.FormMenu.Items)
+ // {
+ // UpdateMRUMenu(mi, mruFiles);
+ // }
+ // }
+ }
+
+ // TODO reimplement in Avalonia
+ // private void UpdateMRUMenu(ToolStripMenuItem mi, string[] mruFiles)
+ // {
+ // if (mi.HasDropDownItems)
+ // {
+ // foreach (ToolStripItem tsi in mi.DropDownItems)
+ // {
+ // if (tsi is ToolStripMenuItem mmi) UpdateMRUMenu(mmi, mruFiles);
+ // }
+ // }
+ // else
+ // {
+ // if (mi is MenuItemWithHandler mid)
+ // {
+ // if (mid.Tag is MenuWithHandler mwh)
+ // {
+ // string MenuId = mwh.ID;
+ // if (MenuId.StartsWith("MenuId.File.Mru.File"))
+ // {
+ // string filenr = MenuId.Substring("MenuId.File.Mru.File".Length);
+ // try
+ // {
+ // int n = int.Parse(filenr);
+ // if (n <= mruFiles.Length && n > 0)
+ // {
+ // string[] parts = mruFiles[mruFiles.Length - n].Split(';');
+ // if (parts.Length > 1)
+ // mid.Text = parts[0];
+ // }
+ // }
+ // catch (FormatException) { }
+ // catch (OverflowException) { }
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ #endregion FrameImpl override
+
+ #region IUIService implementation
+ // TODO reimplement in Avalonia
+ public override IUIService UIService => this;
+ GeoObjectList IUIService.GetDataPresent(object data)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // if (data is IDataObject idata)
+ // {
+ // if (idata.GetDataPresent(System.Windows.Forms.DataFormats.Serializable))
+ // {
+ // return idata.GetData(System.Windows.Forms.DataFormats.Serializable) as GeoObjectList;
+ // }
+ // }
+ // return null;
+ // }
+ // Substitutes.Keys IUIService.ModifierKeys => (Substitutes.Keys)Control.ModifierKeys;
+ // Substitutes.Point IUIService.CurrentMousePosition => Subst(Control.MousePosition);
+ Substitutes.Keys IUIService.ModifierKeys
+ {
+ get { throw new NotImplementedException(); }
+ }
+ Substitutes.Point IUIService.CurrentMousePosition
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ private Substitutes.Point Subst(System.Drawing.Point mousePosition)
+ {
+ return new Substitutes.Point(mousePosition.X, mousePosition.Y);
+ }
+
+ private static Dictionary directories = new Dictionary();
+ // TODO reimplement in Avalonia
+ Substitutes.DialogResult IUIService.ShowOpenFileDlg(string id, string title, string filter, ref int filterIndex, out string fileName)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // OpenFileDialog openFileDialog = new OpenFileDialog();
+ // openFileDialog.Filter = filter;
+ // openFileDialog.FilterIndex = filterIndex;
+ // if (!string.IsNullOrWhiteSpace(title)) openFileDialog.Title = title;
+ // if (!string.IsNullOrWhiteSpace(id) && directories.TryGetValue(id, out string directory))
+ // {
+ // openFileDialog.InitialDirectory = directory;
+ // }
+ // else
+ // {
+ // openFileDialog.RestoreDirectory = true;
+ // }
+
+ // Substitutes.DialogResult res = (Substitutes.DialogResult)openFileDialog.ShowDialog(Application.OpenForms[0]);
+ // if (res == Substitutes.DialogResult.OK)
+ // {
+ // filterIndex = openFileDialog.FilterIndex;
+ // fileName = openFileDialog.FileName;
+ // if (!string.IsNullOrWhiteSpace(id))
+ // {
+ // directory = System.IO.Path.GetDirectoryName(fileName);
+ // directories[id] = directory;
+ // }
+ // }
+ // else
+ // {
+ // fileName = null;
+ // }
+ // return res;
+ // }
+ // TODO reimplement in Avalonia
+ Substitutes.DialogResult IUIService.ShowSaveFileDlg(string id, string title, string filter, ref int filterIndex, ref string fileName)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // SaveFileDialog saveFileDialog = new SaveFileDialog();
+ // saveFileDialog.Filter = filter;
+ // saveFileDialog.FilterIndex = filterIndex;
+ // if (!string.IsNullOrWhiteSpace(title)) saveFileDialog.Title = title;
+ // if (!string.IsNullOrWhiteSpace(id) && directories.TryGetValue(id, out string directory))
+ // {
+ // saveFileDialog.InitialDirectory = directory;
+ // }
+ // else
+ // {
+ // saveFileDialog.RestoreDirectory = true;
+ // }
+ // if (!string.IsNullOrWhiteSpace(fileName))
+ // {
+ // saveFileDialog.FileName = fileName;
+ // }
+ // Substitutes.DialogResult res = (Substitutes.DialogResult)saveFileDialog.ShowDialog(Application.OpenForms[0]);
+ // if (res == Substitutes.DialogResult.OK)
+ // {
+ // filterIndex = saveFileDialog.FilterIndex;
+ // fileName = saveFileDialog.FileName;
+ // if (!string.IsNullOrWhiteSpace(id))
+ // {
+ // directory = System.IO.Path.GetDirectoryName(fileName);
+ // directories[id] = directory;
+ // }
+ // }
+ // return res;
+ // }
+ // TODO reimplement in Avalonia
+ Substitutes.DialogResult IUIService.ShowMessageBox(string text, string caption, Substitutes.MessageBoxButtons buttons)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // return (Substitutes.DialogResult)MessageBox.Show(Application.OpenForms[0], text, caption, (System.Windows.Forms.MessageBoxButtons)buttons);
+ // }
+ // TODO reimplement in Avalonia
+ Substitutes.DialogResult IUIService.ShowColorDialog(ref Substitutes.Color color)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // ColorDialog colorDialog = new ColorDialog();
+ // colorDialog.Color = Drawing(color);
+ // Substitutes.DialogResult dlgres = (Substitutes.DialogResult)colorDialog.ShowDialog(Application.OpenForms[0]);
+ // color = Subst(colorDialog.Color);
+ // return dlgres;
+ // }
+
+ private Substitutes.Color Drawing(Substitutes.Color color)
+ {
+ return Substitutes.Color.FromArgb(color.ToArgb());
+ }
+
+ private Substitutes.Color Subst(Substitutes.Color color) // TODO correct argument type
+ {
+ return Substitutes.Color.FromArgb(color.ToArgb());
+ }
+
+ // TODO reimplement in Avalonia
+ void IUIService.ShowProgressBar(bool show, double percent, string title)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // this.ProgressAction?.Invoke(show, percent, title);
+ // }
+ ///
+ /// Returns a bitmap from the specified embeded resource. the name is in the form filename:index
+ ///
+ ///
+ ///
+ // TODO reimplement in Avalonia
+ object IUIService.GetBitmap(string name)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // string[] parts = name.Split(':');
+ // if (parts.Length == 2)
+ // {
+ // ImageList il = BitmapTable.GetImageList(parts[0], 15, 15);
+ // if (il != null)
+ // {
+ // try
+ // {
+ // int ind = int.Parse(parts[1]);
+ // return il.Images[ind] as Bitmap;
+ // }
+ // catch (FormatException) { }
+ // catch (OverflowException) { }
+ // catch (ArgumentOutOfRangeException) { }
+ // }
+ // }
+ // return null;
+ // }
+ // TODO reimplement in Avalonia
+ IPaintTo3D IUIService.CreatePaintInterface(object paintToBitmap, double precision)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // PaintToOpenGL paintTo3D = new PaintToOpenGL(precision);
+ // paintTo3D.Init(paintToBitmap as Bitmap);
+ // return paintTo3D;
+ // }
+ // TODO reimplement in Avalonia
+ Substitutes.DialogResult IUIService.ShowPageSetupDlg(object printDocument, object pageSettings, out int width, out int height, out bool landscape)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // PageSetupDialog psd = new PageSetupDialog();
+ // psd.AllowPrinter = true;
+ // psd.EnableMetric = true;
+ // psd.Document = (PrintDocument?)printDocument;
+ // Substitutes.DialogResult res = (Substitutes.DialogResult)(int)psd.ShowDialog();
+ // if (res == Substitutes.DialogResult.OK)
+ // {
+ // psd.Document.OriginAtMargins = false;
+ // printDocument = psd.Document;
+ // width = psd.PageSettings.PaperSize.Width;
+ // height = psd.PageSettings.PaperSize.Height;
+ // landscape = psd.PageSettings.Landscape;
+ // }
+ // else
+ // {
+ // width = height = 0;
+ // landscape = false;
+ // }
+ // return res;
+ // }
+ // TODO reimplement in Avalonia
+ Substitutes.DialogResult IUIService.ShowPrintDlg(object printDocument)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // PrintDialog printDialog = new PrintDialog();
+ // printDialog.Document = (PrintDocument?)printDocument;
+ // printDialog.AllowSomePages = false;
+ // printDialog.AllowCurrentPage = false;
+ // printDialog.AllowSelection = false;
+ // printDialog.AllowPrintToFile = false;
+ // Substitutes.DialogResult res = (Substitutes.DialogResult)printDialog.ShowDialog();
+ // if (res == Substitutes.DialogResult.OK)
+ // {
+ // printDocument = printDialog.Document;
+ // }
+ // return res;
+ // }
+ // TODO reimplement in Avalonia
+ void IUIService.SetClipboardData(GeoObjectList objects, bool copy)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // // -> JSON in Byte-Array serialisieren
+ // byte[] payload;
+ // using (var ms = new MemoryStream())
+ // {
+ // var js = new JsonSerialize();
+ // js.ToStream(ms, objects, closeStream: false);
+ // payload = ms.ToArray();
+ // }
+
+ // // DataObject mit eigenem Format befüllen (ohne Auto-Konvertierung)
+ // var dob = new DataObject();
+ // dob.SetData(ClipFormat, false, payload);
+
+ // // Optional: eine menschenlesbare Text-Notiz (damit Paste in Notepad nicht leer ist)
+ // dob.SetText($"CADability GeoObjectList ({objects?.Count ?? 0} items)");
+
+ // // copy=true: Inhalte bleiben erhalten, auch wenn die App endet
+ // Clipboard.SetDataObject(dob, copy);
+ // }
+ // TODO reimplement in Avalonia
+ object IUIService.GetClipboardData(Type typeOfdata)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // try
+ // {
+ // // defensiv prüfen, ohne GetDataObject()
+ // if (!Clipboard.ContainsData(ClipFormat))
+ // return null;
+
+ // var data = Clipboard.GetData(ClipFormat);
+ // if (data is byte[] bytes)
+ // {
+ // using var ms = new MemoryStream(bytes);
+ // var js = new JsonSerialize();
+ // return js.FromStream(ms); // -> GeoObjectList
+ // }
+
+ // // Falls ein Stream geliefert wird (kann je nach Host passieren)
+ // if (data is Stream s)
+ // {
+ // using var ms = new MemoryStream();
+ // s.CopyTo(ms);
+ // ms.Position = 0;
+ // var js = new JsonSerialize();
+ // return js.FromStream(ms);
+ // }
+
+ // // Worst case: String (sollten wir nicht bekommen, aber behandeln)
+ // if (data is string json)
+ // {
+ // return JsonSerialize.FromString(json);
+ // }
+
+ // return null;
+ // }
+ // catch (ExternalException)
+ // {
+ // // Clipboard gerade gelockt o.ä. – einfach "kein Inhalt" signalisieren
+ // return null;
+ // }
+ // }
+ // TODO reimplement in Avalonia
+ bool IUIService.HasClipboardData(Type typeOfdata)
+ {
+ throw new NotImplementedException();
+ }
+ // {
+ // try
+ // {
+ // // Leichtgewichtige .NET-Variante:
+ // if (Clipboard.ContainsData(ClipFormat)) return true;
+
+ // // Optional, noch robuster & COM-frei (empfohlen im Idle):
+ // // return NativeClipboard.HasMyFormat();
+
+ // return false;
+ // }
+ // catch (ExternalException)
+ // {
+ // // z. B. wenn gerade ein anderer Prozess das Clipboard geöffnet hat
+ // return false;
+ // }
+ // }
+
+ public Substitutes.FontFamily GetFontFamily(string fontFamilyName)
+ {
+ throw new NotImplementedException();
+ // if (string.IsNullOrEmpty(fontFamilyName)) return new FontFamilyImpl();
+ // else return new FontFamilyImpl(fontFamilyName);
+ }
+
+ public string[] GetFontFamilies()
+ {
+ throw new NotImplementedException();
+ // FontFamily[] ffs = FontFamily.Families;
+ // List result = new List();
+ // for (int i = 0; i < ffs.Length; i++) result.Add(ffs[i].Name);
+ // return result.ToArray();
+ }
+
+ // TODO reimplement in Avalonia
+ event EventHandler IUIService.ApplicationIdle
+ {
+ add
+ {
+ // Application.Idle += value;
+ }
+
+ remove
+ {
+ // Application.Idle -= value;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/CADability.Avalonia/PaintToOpenGL.cs b/CADability.Avalonia/PaintToOpenGL.cs
new file mode 100644
index 00000000..c175e937
--- /dev/null
+++ b/CADability.Avalonia/PaintToOpenGL.cs
@@ -0,0 +1,470 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using Avalonia.Controls;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Controls;
+using CADability;
+using CADability.Attribute;
+using CADability.GeoObject;
+using CADability.Substitutes;
+
+using static Avalonia.OpenGL.GlConsts;
+
+namespace CADability.Avalonia
+{
+ class PaintToOpenGL : OpenGlControlBase, IPaintTo3D
+ {
+ public const int GL_POINTS = 0;
+ public const int GL_TRUE = 1;
+ public const int GL_FALSE = 0;
+
+ private int _shaderProgram;
+ private int _vertexShader;
+ private int _fragmentShader;
+ private int _vertexBufferObject;
+ private int _vertexArrayObject;
+ private int _indexBufferObject;
+
+ private IView _view;
+ private GlInterface _gl;
+ private Color _backgroundColor;
+
+ private bool paintSurfaces;
+ private bool paintEdges;
+ private bool paintSurfaceEdges;
+ private bool useLineWidth;
+ private bool selectMode;
+ private bool delayText;
+ private bool delayAll;
+ private bool triangulateText;
+ private bool dontRecalcTriangulation;
+ private bool isBitmap;
+ private double precision;
+ private double pixelToWorld;
+ private Color selectColor;
+ private PaintCapabilities capabilities;
+
+ private static void GlCheckError(GlInterface gl)
+ {
+ int err;
+ while ((err = gl.GetError()) != GL_NO_ERROR) {
+ Console.WriteLine(err);
+ }
+ }
+
+ private string GetShader(string shader)
+ {
+ string data = "#version 330\n";
+ if (GlVersion.Type == GlProfileType.OpenGLES) {
+ data += "precision mediump float;\n";
+ }
+ data += shader;
+ return data;
+ }
+
+ private void ConfigureShaders(GlInterface gl)
+ {
+ _shaderProgram = gl.CreateProgram();
+
+ _vertexShader = gl.CreateShader(GL_VERTEX_SHADER);
+ Console.WriteLine(gl.CompileShaderAndGetError(_vertexShader, VertexShaderSource));
+ gl.AttachShader(_shaderProgram, _vertexShader);
+
+ _fragmentShader = gl.CreateShader(GL_FRAGMENT_SHADER);
+ Console.WriteLine(gl.CompileShaderAndGetError(_fragmentShader, FragmentShaderSource));
+ gl.AttachShader(_shaderProgram, _fragmentShader);
+
+ Console.WriteLine(gl.LinkProgramAndGetError(_shaderProgram));
+ gl.UseProgram(_shaderProgram);
+ }
+
+ private unsafe void CreateVertexBuffer(GlInterface gl)
+ {
+ Vector3[] vertices = new Vector3[]
+ {
+ new Vector3(-1.0f, -1.0f, 0.0f),
+ new Vector3(1.0f, -1.0f, 0.0f),
+ new Vector3(0.0f, 1.0f, 0.0f),
+ };
+
+ _vertexBufferObject = gl.GenBuffer();
+ gl.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
+
+ fixed(void * pData = vertices)
+ gl.BufferData(GL_ARRAY_BUFFER, new IntPtr(sizeof(Vector3) * vertices.Length),
+ new IntPtr(pData), GL_STATIC_DRAW);
+
+ _vertexArrayObject = gl.GenVertexArray();
+ gl.BindVertexArray(_vertexArrayObject);
+ gl.VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3), IntPtr.Zero);
+ gl.EnableVertexAttribArray(0);
+ }
+
+ protected override void OnOpenGlInit(GlInterface gl)
+ {
+ base.OnOpenGlInit(gl);
+
+ ConfigureShaders(gl);
+ CreateVertexBuffer(gl);
+
+ GlCheckError(gl);
+ }
+
+ protected override void OnOpenGlDeinit(GlInterface gl)
+ {
+ base.OnOpenGlDeinit(gl);
+
+ gl.BindBuffer(GL_ARRAY_BUFFER, 0);
+ gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ gl.BindVertexArray(0);
+ gl.UseProgram(0);
+ gl.DeleteBuffer(_vertexBufferObject);
+ gl.DeleteVertexArray(_vertexArrayObject);
+ gl.DeleteProgram(_shaderProgram);
+ gl.DeleteShader(_vertexShader);
+ gl.DeleteShader(_fragmentShader);
+
+ GlCheckError(gl);
+ }
+
+ protected override void OnOpenGlRender(GlInterface gl, int fb)
+ {
+ // gl.ClearColor(0.85f, 0.90f, 0.98f, 1.0f);
+ // gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // gl.Viewport(0, 0, (int)Bounds.Width, (int)Bounds.Height);
+ // gl.DrawArrays(GL_TRIANGLES, 0, new IntPtr(3));
+
+
+ // TODO, concept
+ // _view.OnPaint() -> ... -> this.
+ // PaintToBitmap seems to be a API only, not used by ShapeIt
+ _gl = gl; // save so it does not have to be passed through
+
+ if (_view != null) {
+ // TODO calculate clip rectangle correctly
+ Substitutes.PaintEventArgs paintEventArgs = new Substitutes.PaintEventArgs()
+ {
+ ClipRectangle = new Substitutes.Rectangle(0, 0, (int)Bounds.Width, (int)Bounds.Height),
+ // Graphics = null // TODO should be fine, is there a better solution?
+ };
+ _view.OnPaint(paintEventArgs);
+ }
+ GlCheckError(gl);
+ }
+
+ string VertexShaderSource => GetShader(@"
+ layout(location = 0) in vec3 Position;
+
+ void main()
+ {
+ gl_Position.xyz = Position;
+ gl_Position.w = 1.0;
+ }
+ ");
+
+ string FragmentShaderSource => GetShader(@"
+ void main()
+ {
+ gl_FragColor = vec4(1, 0, 1, 1);
+ }
+ ");
+
+ bool IPaintTo3D.PaintSurfaces
+ {
+ get { return paintSurfaces; }
+ }
+
+ bool IPaintTo3D.PaintEdges
+ {
+ get { return paintEdges; }
+ }
+
+ bool IPaintTo3D.PaintSurfaceEdges
+ {
+ get { return paintSurfaceEdges; }
+ set { paintSurfaceEdges = value; }
+ }
+
+ bool IPaintTo3D.UseLineWidth
+ {
+ get { return useLineWidth; }
+ set { useLineWidth = value; }
+ }
+
+ double IPaintTo3D.Precision
+ {
+ get { return precision; }
+ set { precision = value; }
+ }
+
+ double IPaintTo3D.PixelToWorld
+ {
+ get { return pixelToWorld; }
+ }
+
+ bool IPaintTo3D.SelectMode
+ {
+ get { return selectMode; }
+ set { selectMode = value; }
+ }
+
+ Color IPaintTo3D.SelectColor
+ {
+ get { return selectColor; }
+ set { selectColor = value; }
+ }
+
+ bool IPaintTo3D.DelayText
+ {
+ get { return delayText; }
+ set { delayText = value; }
+ }
+
+ bool IPaintTo3D.DelayAll
+ {
+ get { return delayAll; }
+ set { delayAll = value; }
+ }
+
+ bool IPaintTo3D.TriangulateText
+ {
+ get { return triangulateText; }
+ set { triangulateText = value; }
+ }
+
+ bool IPaintTo3D.DontRecalcTriangulation
+ {
+ get { return dontRecalcTriangulation; }
+ set { dontRecalcTriangulation = value; }
+ }
+
+ PaintCapabilities IPaintTo3D.Capabilities
+ {
+ get { return capabilities; }
+ }
+
+ bool IPaintTo3D.IsBitmap
+ {
+ get { return isBitmap; }
+ }
+ IDisposable IPaintTo3D.FacesBehindEdgesOffset {
+ get { return null; }
+ // TODO
+ }
+
+ void IPaintTo3D.MakeCurrent()
+ {
+ // noop, handled by Avalonia
+ }
+ void IPaintTo3D.SetColor(Color color, int lockColor = 0)
+ {
+ }
+ void IPaintTo3D.AvoidColor(Color color)
+ {
+ _backgroundColor = color;
+ }
+ void IPaintTo3D.SetLineWidth(LineWidth lineWidth)
+ {
+
+ }
+ void IPaintTo3D.SetLinePattern(LinePattern pattern)
+ {
+
+ }
+ void IPaintTo3D.Polyline(GeoPoint[] points)
+ {
+
+ }
+ void IPaintTo3D.FilledPolyline(GeoPoint[] points)
+ {
+
+ }
+ void IPaintTo3D.Points(GeoPoint[] points, float size, PointSymbol pointSymbol)
+ {
+
+ }
+ unsafe void IPaintTo3D.Triangle(GeoPoint[] vertices, GeoVector[] normals, int[] indextriples)
+ {
+
+ int vao = _gl.GenVertexArray();
+ int vbo = _gl.GenBuffer();
+ int ebo = _gl.GenBuffer();
+
+ _gl.BindVertexArray(vao);
+
+ _gl.BindBuffer(GL_ARRAY_BUFFER, vbo);
+ fixed(void * pData = vertices)
+ _gl.BufferData(GL_ARRAY_BUFFER, new IntPtr(sizeof(GeoPoint) * vertices.Length),
+ new IntPtr(pData), GL_STATIC_DRAW);
+
+ _gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
+ fixed(void * pData = indextriples)
+ _gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(sizeof(int) * indextriples.Length),
+ new IntPtr(pData), GL_STATIC_DRAW);
+
+ _gl.VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GeoPoint), IntPtr.Zero);
+ _gl.EnableVertexAttribArray(0);
+
+ _gl.DrawArrays(GL_TRIANGLES, 0, new IntPtr(indextriples.Length));
+
+ _gl.BindVertexArray(0);
+
+ GlCheckError(_gl);
+ }
+ void IPaintTo3D.PrepareText(string fontName, string textString, object fontStyle)
+ {
+
+ }
+ void IPaintTo3D.PreparePointSymbol(PointSymbol pointSymbol)
+ {
+
+ }
+ void IPaintTo3D.PrepareIcon(object icon)
+ {
+
+ }
+ void IPaintTo3D.PrepareBitmap(object bitmap, int xoffset, int yoffset)
+ {
+
+ }
+ void IPaintTo3D.PrepareBitmap(object bitmap)
+ {
+
+ }
+ void IPaintTo3D.RectangularBitmap(object bitmap, GeoPoint location, GeoVector directionWidth, GeoVector directionHeight)
+ {
+
+ }
+ void IPaintTo3D.Text(GeoVector lineDirection, GeoVector glyphDirection, GeoPoint location, string fontName, string textString, object fontStyle, CADability.GeoObject.Text.AlignMode alignment, CADability.GeoObject.Text.LineAlignMode lineAlignment)
+ {
+
+ }
+ void IPaintTo3D.List(IPaintTo3DList paintThisList)
+ {
+
+ }
+ void IPaintTo3D.SelectedList(IPaintTo3DList paintThisList, int wobbleRadius)
+ {
+
+ }
+ void IPaintTo3D.Nurbs(GeoPoint[] poles, double[] weights, double[] knots, int degree)
+ {
+
+ }
+ void IPaintTo3D.Line2D(int sx, int sy, int ex, int ey)
+ {
+
+ }
+ void IPaintTo3D.Line2D(PointF p1, PointF p2)
+ {
+
+ }
+ void IPaintTo3D.FillRect2D(PointF p1, PointF p2)
+ {
+
+ }
+ void IPaintTo3D.Point2D(int x, int y)
+ {
+
+ }
+ void IPaintTo3D.DisplayIcon(GeoPoint p, object icon)
+ {
+
+ }
+ void IPaintTo3D.DisplayBitmap(GeoPoint p, object bitmap)
+ {
+
+ }
+ void IPaintTo3D.SetProjection(Projection projection, BoundingCube boundingCube)
+ {
+
+ }
+ void IPaintTo3D.Clear(Color background)
+ {
+ _backgroundColor = background;
+ Console.WriteLine("PaintToOpenGL, background: " + background);
+ _gl.Viewport(0, 0, (int)Bounds.Width, (int)Bounds.Height);
+ _gl.ClearColor(background.R / 255.0f, background.G / 255.0f, background.B / 255.0f, 1.0f);
+ _gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ GlCheckError(_gl);
+ }
+ void IPaintTo3D.Resize(int width, int height)
+ {
+
+ }
+ void IPaintTo3D.OpenList(string name = null)
+ {
+
+ }
+ IPaintTo3DList IPaintTo3D.CloseList()
+ {
+ throw new NotImplementedException();
+ }
+ IPaintTo3DList IPaintTo3D.MakeList(List sublists)
+ {
+ throw new NotImplementedException();
+ }
+ void IPaintTo3D.OpenPath()
+ {
+ throw new NotSupportedException("OpenGL does not support paths");
+ }
+ void IPaintTo3D.ClosePath(Color color)
+ {
+ throw new NotSupportedException("OpenGL does not support paths");
+ }
+ void IPaintTo3D.CloseFigure()
+ {
+ throw new NotSupportedException("OpenGL does not support paths");
+ }
+ void IPaintTo3D.Arc(GeoPoint center, GeoVector majorAxis, GeoVector minorAxis, double startParameter, double sweepParameter)
+ {
+ throw new NotSupportedException("OpenGL does not support paths");
+ }
+ void IPaintTo3D.FreeUnusedLists()
+ {
+ }
+ void IPaintTo3D.UseZBuffer(bool use)
+ {
+ }
+ void IPaintTo3D.Blending(bool on)
+ {
+ }
+ void IPaintTo3D.FinishPaint()
+ {
+ }
+ void IPaintTo3D.PaintFaces(PaintTo3D.PaintMode paintMode)
+ {
+ }
+ void IPaintTo3D.Dispose()
+ {
+
+ }
+ void IPaintTo3D.PushState()
+ {
+
+ }
+ void IPaintTo3D.PopState()
+ {
+
+ }
+ void IPaintTo3D.PushMultModOp(ModOp insertion)
+ {
+
+ }
+ void IPaintTo3D.PopModOp()
+ {
+
+ }
+ void IPaintTo3D.SetClip(Rectangle clipRectangle)
+ {
+
+ }
+
+ public IView View {
+ set => _view = value;
+ }
+ }
+}
diff --git a/CADability.Avalonia/app.manifest b/CADability.Avalonia/app.manifest
new file mode 100644
index 00000000..09474690
--- /dev/null
+++ b/CADability.Avalonia/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CADability/BooleanOperation.cs b/CADability/BooleanOperation.cs
index 21587cc2..f0069aea 100644
--- a/CADability/BooleanOperation.cs
+++ b/CADability/BooleanOperation.cs
@@ -5,7 +5,9 @@
using CADability.Substitutes;
using Point = CADability.GeoObject.Point;
using MathNet.Numerics;
+#if !AVALONIA
using Microsoft.VisualStudio.DebuggerVisualizers;
+#endif
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -20,8 +22,8 @@ namespace CADability
/* This is the attempt to simplify the implementation of BRepIntersection/BRepOperation
* There were too many exceptions and special cases in BRepOperation, especially concerning the overlapping faces, both
* same oriantation and opposite orientation. I think, we don't need to consider overlapping faces if we consequently intersect faces
- * which have edges on faces of the other shell.
- *
+ * which have edges on faces of the other shell.
+ *
* - put all faces (of both shells) in a single octtree
* - use the octtree nodes to find pairs of faces (of different shells) which intersect each other and compute the intersection edges
* - check whether edges of one face intersect with the other face
@@ -49,13 +51,13 @@ namespace CADability
*/
///
- ///
+ ///
///
public class BooleanOperation : OctTree
{
private Shell shell1; // the two shells, which are intersected
private Shell shell2; // either shell1 and shell2 are set or multipleFaces is set
- private List multipleFaces; // the faces, which are intersected.
+ private List multipleFaces; // the faces, which are intersected.
private Dictionary originalToClonedEdges; // points from the original edges to the clones we are working on
private Dictionary originalToClonedVertices; // points from the original vertices to the clones we are working on
private Dictionary originalToClonedFaces; // points from the original faces to the clones we are working on
@@ -80,7 +82,7 @@ public class BooleanOperation : OctTree
Dictionary<(Edge edge, Face face), (List vertices, bool curveIsInSurface)> FaceEdgeIntersections; // to avoid duplicate calls to GetFaceEdgeIntersection
HashSet commonOverlappingFaces; // Faces, which have been newly created as common parts of overlapping faces
- HashSet cancelledfaces; // Faces, which cancel each other, they have the same area but are opposite oriented
+ HashSet cancelledfaces; // Faces, which cancel each other, they have the same area but are opposite oriented
Dictionary> faceToIntersectionEdges; // faces of both shells with their intersection edges
Dictionary> faceToCommonFaces; // faces which have overlapping common parts on them
Dictionary> edgesToSplit;
@@ -572,7 +574,7 @@ from j in Enumerable.Range(i + 1, list.Count - i - 1)
// Problme wäre die Genauigkeit, wenn beim BooleanOperation.generateCycles die Richtung genommen wird...
if (con1 is InterpolatedDualSurfaceCurve.ProjectedCurve pon1 && con2 is InterpolatedDualSurfaceCurve.ProjectedCurve pon2 &&
tr is InterpolatedDualSurfaceCurve idsc)
- { // con1 und con2 müssen auf tr verweisen, sonst kann man das Face später nicht mit "ReverseOrientation" umdrehen. Dort wird nämlich die
+ { // con1 und con2 müssen auf tr verweisen, sonst kann man das Face später nicht mit "ReverseOrientation" umdrehen. Dort wird nämlich die
// surface verändert, und die muss bei allen Kurven die selbe sein
pon1.SetCurve3d(idsc);
pon2.SetCurve3d(idsc);
@@ -629,7 +631,7 @@ from j in Enumerable.Range(i + 1, list.Count - i - 1)
}
//TODO: not implemented yet:
// if we arrive here with edgeFound==false, there is a tangential intersection which is not an edge on one of the two faces. Now we have two cases: it is either
- // a face touching the other face (like a cylinder touches a plane, or it is a real intersection, where one face goes through the other face
+ // a face touching the other face (like a cylinder touches a plane, or it is a real intersection, where one face goes through the other face
// like an S-curve touches and crosses a line in the middle.
//we try to find two points close to the middlepoint of the intersection curve where we get stable normals which are not parallel
if (normalsCrossedMiddle.Length < 100 * Precision.eps)
@@ -1133,7 +1135,7 @@ public enum Operation { union, intersection, difference, clip, connectMultiple,
// createEdgeFaceIntersections(); // find intersection of edges with faces from different shells
// splitEdges(); // split the edges at the found intersection positions
// combineVerticesMultipleFaces(); // combine geometric close vertices
- // removeIdenticalOppositeFaces(); //
+ // removeIdenticalOppositeFaces(); //
// createNewEdges(); // populate faceToIntersectionEdges : for each face a list of intersection curves
// createInnerFaceIntersections(); // find additional intersection curves where faces intersect, but edges don't intersect (rare)
// TrimmIntersectionEdges();
@@ -1832,7 +1834,7 @@ public static (Shell[] upperPart, Shell[] lowerPart) SplitByFace(Shell toSplit,
}
///
/// Chamfer or bevel the provided edges. the edges must be connected and only two edges may have a common vertex. The edges must build a path.
- /// We have two distances from the edge to make chamfers with different angles. All edges must belong to the .
+ /// We have two distances from the edge to make chamfers with different angles. All edges must belong to the .
/// The first distance is on the primary face, the second on the other face (in most cases the distances are equal).
///
///
@@ -1882,7 +1884,7 @@ public static Shell ChamferEdges(Face primaryFace, Edge[] edges, double primaryD
// List fillets = new List(); // faces, that make the fillet
// Dictionary> tangentialIntersectionEdges = new Dictionary>();
// Dictionary> rawFillets = new Dictionary>(); // the axis curve and simple fillets for each edge without joints
- // Dictionary> joiningVertices = new Dictionary>(); // for each vertex there may be up to three involved edges
+ // Dictionary> joiningVertices = new Dictionary>(); // for each vertex there may be up to three involved edges
// // 1.: create the simple fillets
// foreach (Edge edg in edges)
// {
@@ -2101,7 +2103,7 @@ public static Shell ChamferEdges(Face primaryFace, Edge[] edges, double primaryD
// Shell[] bores = bo.Result();
// if (bores != null && bores.Length == 1)
// {
- // if (bores[0].Faces.Length == 2) // this should be the case:
+ // if (bores[0].Faces.Length == 2) // this should be the case:
// {
// GeoObjectList fcs = bores[0].Decompose();
// if (fcs.Count == 2)
@@ -2758,7 +2760,7 @@ public static bool TryFindIntersectingEdges(IEnumerable edges, out Edge e1
}
listAB.Add(edge);
- // fB → fA
+ // fB → fA
if (!adj.TryGetValue(fB, out var nbrB))
{
nbrB = new Dictionary>();
@@ -2800,11 +2802,11 @@ public static bool TryFindIntersectingEdges(IEnumerable edges, out Edge e1
public Shell[] Result()
{
// this method relies on
- // - faceToIntersectionEdges: contains all faces, which intersect with other faces as keys and all the intersection edges,
+ // - faceToIntersectionEdges: contains all faces, which intersect with other faces as keys and all the intersection edges,
// which are produced by other faces on this face as values.
// - overlappingFaces and oppositeFaces: they contain pairs of faces, which overlap, with either same or oppostie orientation.
- //
- // The main algorithm does the following: split (or trimm or cut) a face according to the intersection edges on this face.
+ //
+ // The main algorithm does the following: split (or trimm or cut) a face according to the intersection edges on this face.
// All edges are oriented, so it is possible to find outer edges and holes. A face might produce zero, one or multiple trimmed faces.
// When all faces are trimmed, connect the trimmed faces with the untouched faces. this is the result.
//
@@ -3167,7 +3169,7 @@ public Shell[] Result()
++dbgc;
}
#endif
- // each outline (only one in most cases) creates a new Face.
+ // each outline (only one in most cases) creates a new Face.
for (int i = 0; i < numOutlines; i++)
{
Face fc = Face.Construct();
@@ -3199,7 +3201,7 @@ public Shell[] Result()
if (operation == Operation.clip) return ClippedParts(trimmedFaces);
if (dontReturnShell2) discardedFaces.UnionWith(shell2.Faces);
- // Now trimmedFaces contains all faces which are cut by faces of the relative other shell, even those, where the other shell cuts
+ // Now trimmedFaces contains all faces which are cut by faces of the relative other shell, even those, where the other shell cuts
// exactly along existing edges and nothing has been created.
// The faces, which have been cut, i.e. faceToIntersectionEdges.Keys, are invalid now, we disconnect all edges from these faces
#if DEBUG // show all trimmed faces
@@ -3295,7 +3297,7 @@ public Shell[] Result()
}
}
else if (edg.SecondaryFace == null || !trimmedFaces.Contains(edg.SecondaryFace) || !trimmedFaces.Contains(edg.PrimaryFace))
- { // only those edges, which
+ { // only those edges, which
if (trimmedEdges.TryGetValue(dvk, out Edge other))
{
if (other == edg) continue;
@@ -3451,7 +3453,7 @@ public Shell[] Result()
}
ConnectOpenEdges(openEdges);
// What about "nonManifoldEdges"?
- //
+ //
List res = new List(); // the result of this method.
// allFaces now contains all the trimmed faces plus the faces, which are (directly or indirectly) connected (via edges) to the trimmed faces
List nonManifoldParts = new List();
@@ -4014,7 +4016,7 @@ private List FindLoop(Edge edg, Vertex startVertex, Face onThisFace, HashS
if (connected.Count == 0 || intersectionEdgeEndHere)
{
// (connected.Count == 0): dead end, no connection at endVertex
- // intersectionEdgeEndHere: cannot go on, because we are following original edges and crossing at a vertex,
+ // intersectionEdgeEndHere: cannot go on, because we are following original edges and crossing at a vertex,
// where an intersection edge ends. This is not allowed (e.g.: breps4)
intersectionEdges.ExceptWith(res);
originalEdges.ExceptWith(res);
@@ -4339,7 +4341,7 @@ private Dictionary, List>> SortLoopsTopologically(List
- /// Takes a set of edges and tries to connect them to closed loops.
+ /// Takes a set of edges and tries to connect them to closed loops.
///
/// work on this set, which will be emptied
/// orientation in respect to this face
@@ -4448,8 +4450,8 @@ private List> FindPath(HashSet set, Vertex vertex1, Vertex vert
}
internal static bool SameEdge(Edge e1, Edge e2, double precision)
- { // it is assumed that the two edges connect the same vertices
- // it is tested whether they have the same geometry (but maybe different directions)
+ { // it is assumed that the two edges connect the same vertices
+ // it is tested whether they have the same geometry (but maybe different directions)
// (two half circles may connect the same vertices but are not geometrically identical when they describe differnt parts of the same circle)
if (e1.Curve3D != null && e2.Curve3D != null) return e1.Curve3D.DistanceTo(e2.Curve3D.PointAt(0.5)) < 10 * precision; // nur precision war zu knapp
return false;
@@ -4573,7 +4575,7 @@ private void createNewEdges()
faceToIntersectionEdges = new Dictionary>();
edgesNotToUse = new HashSet();
// wir haben eine Menge Schnittpunkte, die Face-Paaren zugeordnet sind. Für jedes Face-Paar, welches Schnittpunkte enthält sollen hier die neuen Kanten bestimmt werden
- // Probleme dabei sind:
+ // Probleme dabei sind:
// - es ist bei mehr als 2 Schnittpunkten nicht klar, welche Abschnitte dazugehören
// - zwei Surfaces können mehr als eine Schnittkurve haben
HashSet created = new HashSet(new EdgeComparerByVertexAndFace());
@@ -4745,8 +4747,8 @@ private void createNewEdges()
// aus den mehreren Punkten (meist 2) unter Berücksichtigung der Richtungen Kanten erzeugen
// bei nicht geschlossenen Kurven sollten immer zwei aufeinanderfolgende Punkte einen gültigen Kurvenabschnitt bilden.
// Obwohl auch hierbei ein doppelt auftretenden Punkt ein Problem machen könnte (Fälle sind schwer vorstellbar).
- // Im schlimmsten Fall müssten man für alle Abschnitte (nicht nur die geraden)
- // Bei geschlossenen Kurven ist nicht klar, welche Punktepaare gültige Kurvenabschnitte erzeugen.
+ // Im schlimmsten Fall müssten man für alle Abschnitte (nicht nur die geraden)
+ // Bei geschlossenen Kurven ist nicht klar, welche Punktepaare gültige Kurvenabschnitte erzeugen.
if (c3ds[i].IsClosed)
{
if (item.Key.face1.Area.Contains(crvsOnSurface1[i].StartPoint, false) && item.Key.face2.Area.Contains(crvsOnSurface2[i].StartPoint, false))
@@ -4828,7 +4830,7 @@ private void createNewEdges()
// Problme wäre die Genauigkeit, wenn beim BooleanOperation.generateCycles die Richtung genommen wird...
if (con1 is InterpolatedDualSurfaceCurve.ProjectedCurve && con2 is InterpolatedDualSurfaceCurve.ProjectedCurve &&
tr is InterpolatedDualSurfaceCurve)
- { // con1 und con2 müssen auf tr verweisen, sonst kann man das Face später nicht mit "ReverseOrientation" umdrehen. Dort wird nämlich die
+ { // con1 und con2 müssen auf tr verweisen, sonst kann man das Face später nicht mit "ReverseOrientation" umdrehen. Dort wird nämlich die
// surface verändert, und die muss bei allen Kurven die selbe sein
(con1 as InterpolatedDualSurfaceCurve.ProjectedCurve).SetCurve3d(tr as InterpolatedDualSurfaceCurve);
(con2 as InterpolatedDualSurfaceCurve.ProjectedCurve).SetCurve3d(tr as InterpolatedDualSurfaceCurve);
@@ -4886,7 +4888,7 @@ private void createNewEdges()
HashSet onFace1 = existingEdges.Intersect(item.Key.face1.Edges).ToHashSet();
HashSet onFace2 = existingEdges.Intersect(item.Key.face2.Edges).ToHashSet();
//bool edgFound = false;
- //// it was Precision.eps before, but a tangential intersection at "Difference2.cdb.json" failed, which should have been there
+ //// it was Precision.eps before, but a tangential intersection at "Difference2.cdb.json" failed, which should have been there
//// in many cases we are close to an edge on one of the faces or both.
//// find this edge and step a little inside into this face
//foreach (Edge edg in onFace1)
diff --git a/CADability/CADability.csproj b/CADability/CADability.csproj
index 71eb2da9..2ada9c72 100644
--- a/CADability/CADability.csproj
+++ b/CADability/CADability.csproj
@@ -1,4 +1,9 @@
+
+ false
+ false
+
+
netstandard2.0
@@ -22,21 +27,22 @@
false
latest
+ AVALONIA
true
- NET4X;TRACE;DEBUG;xTESTNEWCONTEXTMENU, xUSENONPRIODICSURFACES
+ $(DefineConstants);NET4X;TRACE;DEBUG;xTESTNEWCONTEXTMENU, xUSENONPRIODICSURFACES
true
- TRACE;xPARALLEL, xTESTNEWCONTEXTMENU, xUSENONPRIODICSURFACES, WEBASSEMBLY
+ $(DefineConstants);TRACE;xPARALLEL, xTESTNEWCONTEXTMENU, xUSENONPRIODICSURFACES, WEBASSEMBLY
true
- NET4X;xTESTNEWCONTEXTMENU
+ $(DefineConstants);NET4X;xTESTNEWCONTEXTMENU
True
full
true
@@ -45,7 +51,7 @@
true
- xPARALLEL, xTESTNEWCONTEXTMENU
+ $(DefineConstants);xPARALLEL, xTESTNEWCONTEXTMENU
false
full
true
@@ -59,7 +65,7 @@
full
-
+
diff --git a/CADability/Frame.cs b/CADability/Frame.cs
index 29570e85..b3523338 100644
--- a/CADability/Frame.cs
+++ b/CADability/Frame.cs
@@ -52,7 +52,7 @@ public class ActiveFrame
/// Set to true, if you have processed this command and no further action is required, leave unmodified if CADability should process this command
public delegate void ProcessCommandDelegate(string MenuId, ref bool Processed);
///
- ///
+ ///
///
///
///
@@ -102,7 +102,7 @@ public interface IFrame
///
void RemoveActiveAction();
///
- /// Returns the currently active action. Call to find out more about
+ /// Returns the currently active action. Call to find out more about
/// that action.
///
Action ActiveAction { get; }
@@ -120,7 +120,7 @@ public interface IFrame
///
event ProcessCommandDelegate ProcessCommandEvent;
///
- /// Provide a event handler if you want to control the appearnce of commands in the menu or toolbar
+ /// Provide a event handler if you want to control the appearnce of commands in the menu or toolbar
/// (set the enabled and check flags).
///
event UpdateCommandDelegate UpdateCommandEvent;
@@ -142,7 +142,7 @@ public interface IFrame
///
event ProcessContextMenuDelegate ProcessContextMenuEvent;
///
- /// Provide a event handler if you want to control the appearance of commands in a context the menu
+ /// Provide a event handler if you want to control the appearance of commands in a context the menu
/// (set the enabled and check flags).
///
event UpdateContextMenuDelegate UpdateContextMenuEvent;
@@ -178,7 +178,7 @@ public interface IFrame
///
Settings GlobalSettings { get; set; }
///
- /// Gets the for the provided . First the s settings are
+ /// Gets the for the provided . First the s settings are
/// checked, if it is not defined there, the global settings will be queried.
///
///
@@ -615,7 +615,7 @@ public Settings GlobalSettings
}
}
private void OnSettingChanged(string Name, object NewValue)
- {
+ {
object o = this.GetSetting(Name);
SettingChangedEvent?.Invoke(Name, NewValue);
// }
@@ -756,7 +756,7 @@ public bool PreProcessMouseWheel(MouseEventArgs e)
}
public virtual void PreProcessKeyDown(KeyEventArgs e)
{
- // it is difficult to find an order of processing:
+ // it is difficult to find an order of processing:
// the construct-action processes the enter key, no chance to use it in a list-box for the selection
// the property page processes the tab key, no chance to use it in the action for "next modal input field"
// we need to replace the KeyEventArgs class by a CADability class anyhow, we could introduce a first-pass, second-pass member
@@ -2407,7 +2407,7 @@ private void OnFileOpen(string fileName)
{
// get a raw model extent to know a precision for the triangulation
// parallel triangulate all faces with this precision to show a progress bar
- // then zoom total
+ // then zoom total
ModelView mv = FirstModelView;
// display the first ModelView
if (mv != null)
diff --git a/CADability/ModelView.cs b/CADability/ModelView.cs
index 5914e349..b771ffcc 100644
--- a/CADability/ModelView.cs
+++ b/CADability/ModelView.cs
@@ -59,7 +59,7 @@ public interface ICanvas
///
IView GetView();
///
- ///
+ ///
///
///
///
@@ -158,7 +158,7 @@ void OnLayerAdded(LayerList sender, Layer added)
}
#region IShowProperty Members
///
- /// Overrides ,
+ /// Overrides ,
/// returns .
///
public override ShowPropertyEntryType EntryType
@@ -179,7 +179,7 @@ public override ShowPropertyLabelFlags LabelType
}
}
///
- /// Overrides ,
+ /// Overrides ,
/// returns the number of subentries in this property view.
///
public override int SubEntriesCount
@@ -191,7 +191,7 @@ public override int SubEntriesCount
}
IShowProperty[] subEntries;
///
- /// Overrides ,
+ /// Overrides ,
/// returns the subentries in this property view.
///
public override IShowProperty[] SubEntries
@@ -1372,7 +1372,7 @@ void IView.Invalidate(PaintBuffer.DrawingAspect aspect, Rectangle ToInvalidate)
}
void IView.InvalidateAll()
{
- // ForceInvalidateAll(); // das ist definitiv zuviel, bei FeedbackObjekten wird das aufgerufen und es bräuchte nur ein
+ // ForceInvalidateAll(); // das ist definitiv zuviel, bei FeedbackObjekten wird das aufgerufen und es bräuchte nur ein
// neuzeichnen der Feedback Objekte
canvas?.Invalidate();
}
@@ -1524,7 +1524,7 @@ public override string LabelText
}
}
///
- /// Overrides ,
+ /// Overrides ,
/// returns .
///
public override ShowPropertyEntryType EntryType
@@ -1570,7 +1570,7 @@ public override void EndEdit(bool aborted, bool modified, string newValue)
}
}
///
- /// Overrides ,
+ /// Overrides ,
/// returns the context menu with the id "MenuId.ModelView".
/// (see )
///
@@ -1586,7 +1586,7 @@ public override MenuWithHandler[] ContextMenu
IShowProperty[] subEntries;
///
- /// Overrides ,
+ /// Overrides ,
/// returns the number of subentries in this property view.
///
public override int SubEntriesCount
@@ -1597,7 +1597,7 @@ public override int SubEntriesCount
}
}
///
- /// Overrides ,
+ /// Overrides ,
/// returns the subentries in this property view.
///
public override IShowProperty[] SubEntries
diff --git a/ShapeIt/ChamferEdgesAction.cs b/ShapeIt/ChamferEdgesAction.cs
index 937ed8e7..9bcd31b3 100644
--- a/ShapeIt/ChamferEdgesAction.cs
+++ b/ShapeIt/ChamferEdgesAction.cs
@@ -3,7 +3,9 @@
using CADability.Actions;
using CADability.Attribute;
using CADability.GeoObject;
+#if !AVALONIA
using ExCSS;
+#endif
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/ShapeIt/FeedbackArrow.cs b/ShapeIt/FeedbackArrow.cs
index c0d8d550..82d9820e 100644
--- a/ShapeIt/FeedbackArrow.cs
+++ b/ShapeIt/FeedbackArrow.cs
@@ -7,7 +7,9 @@
using System.Threading.Tasks;
using static CADability.Projection;
using System.Globalization;
+#if !AVALONIA
using CADability.Forms;
+#endif
using CADability.Attribute;
using CADability.Substitutes;
@@ -268,7 +270,7 @@ public static GeoObjectList MakeArcArrow(Shell onThisShell, Axis axis, GeoVector
else startArrow.ColorDef = blackParts;
if (flags.HasFlag(ArrowFlags.secondRed)) endArrow.ColorDef = redParts;
else endArrow.ColorDef = blackParts;
- res.Add(startArrow);
+ res.Add(startArrow);
res.Add(endArrow);
Text txt = Text.Construct();
diff --git a/ShapeIt/MainForm.Designer.cs b/ShapeIt/MainForm.Designer.cs
index 0ced6b6f..676f09c1 100644
--- a/ShapeIt/MainForm.Designer.cs
+++ b/ShapeIt/MainForm.Designer.cs
@@ -1,4 +1,5 @@
-namespace ShapeIt
+#if !AVALONIA
+namespace ShapeIt
{
partial class MainForm
{
@@ -37,4 +38,4 @@ private void InitializeComponent()
#endregion
}
}
-
+#endif
diff --git a/ShapeIt/MainForm.axaml b/ShapeIt/MainForm.axaml
new file mode 100644
index 00000000..28fe6b7b
--- /dev/null
+++ b/ShapeIt/MainForm.axaml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ShapeIt/MainForm.axaml.cs b/ShapeIt/MainForm.axaml.cs
new file mode 100644
index 00000000..5dd03238
--- /dev/null
+++ b/ShapeIt/MainForm.axaml.cs
@@ -0,0 +1,791 @@
+using Avalonia.Controls;
+using CADability.Avalonia;
+using CADability;
+using CADability.Actions;
+using CADability.Attribute;
+using CADability.GeoObject;
+using CADability.UserInterface;
+using MathNet.Numerics.LinearAlgebra.Factorization;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using System.Reflection;
+using System.Linq;
+using System.IO;
+using System.Xml;
+using System.Xml.Linq;
+using System.Text;
+
+namespace ShapeIt
+{
+
+ // TODO move gui related things to CADability.Avalonia.CadForm and inherit from that here?
+ // or just implement ICommandHandler here?
+ public partial class MainForm : Window, ICommandHandler
+ {
+ // public MainForm()
+ // {
+ // InitializeComponent();
+ // cadFrame = new CadFrame(propertiesExplorer, cadCanvas, this);
+ // }
+ // TODO
+ // create CAD project
+ // create FrameImpl (or implement own subclass)
+ // in CadForm (here named CadControl?): create Frame/cadFrame (type x -> FrameImpl -> IFrame) and
+ // set cadCanvas.Frame = cadFrame (in CadForm)
+ //
+
+ // currently here because we dont inherit from CadForm
+ private CadFrame cadFrame;
+
+ // private PictureBox logoBox;
+ private ModellingPropertyEntries modellingPropertyEntries;
+ private DateTime lastSaved; // time, when the current file has been saved the last time, see OnIdle
+ private bool modifiedSinceLastAutosave = false;
+ bool projectionChanged = false; // to handle projection changes in OnIdle
+ bool crashChecked = false;
+
+ // TODO needed? how to do in Avalonia?
+ // private Control FindControlByName(Control parent, string name)
+ // {
+ // foreach (Control child in parent.Controls)
+ // {
+ // if (child.Name == name)
+ // return child;
+
+ // Control found = FindControlByName(child, name);
+ // if (found != null)
+ // return found;
+ // }
+
+ // return null;
+ // }
+
+ // TODO reimplement in Avalonia
+ // void FadeOutPictureBox(PictureBox pb)
+ // {
+ // var timer = new System.Windows.Forms.Timer();
+ // timer.Interval = 50;
+ // double alpha = 1.0;
+
+ // Image original = pb.Image;
+ // Bitmap faded = new Bitmap(original.Width, original.Height);
+
+ // timer.Tick += (s, e) =>
+ // {
+ // alpha -= 0.01;
+ // if (alpha <= 0)
+ // {
+ // timer.Stop();
+ // pb.Parent.Controls.Remove(pb);
+ // //pb.Visible = false;
+ // pb.Dispose();
+ // return;
+ // }
+
+ // using (Graphics g = Graphics.FromImage(faded))
+ // {
+ // g.Clear(Color.Transparent);
+ // ColorMatrix matrix = new ColorMatrix
+ // {
+ // Matrix33 = (float)alpha // Alpha-Kanal
+ // };
+ // ImageAttributes attributes = new ImageAttributes();
+ // attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+
+ // g.DrawImage(original,
+ // new Rectangle(0, 0, faded.Width, faded.Height),
+ // 0, 0, original.Width, original.Height,
+ // GraphicsUnit.Pixel,
+ // attributes);
+ // }
+
+ // pb.Image = (Image)faded.Clone(); // neues Bild setzen
+ // };
+
+ // timer.Start();
+ // }
+
+ // TODO reimplement in Avalonia
+ // private void ShowLogo()
+ // {
+ // Control pex = FindControlByName(this, "propertiesExplorer");
+ // // Create PictureBox
+ // logoBox = new PictureBox();
+ // Assembly ThisAssembly = Assembly.GetExecutingAssembly();
+ // using (System.IO.Stream str = ThisAssembly.GetManifestResourceStream("ShapeIt.Resources.ShapeIt2.png"))
+ // {
+ // logoBox.Image = new Bitmap(str);
+ // }
+ // logoBox.SizeMode = PictureBoxSizeMode.Zoom;
+
+ // double aspectRatio = (double)logoBox.Image.Height / logoBox.Image.Width;
+
+ // // Zielbreite übernehmen
+ // int targetWidth = pex.ClientSize.Width - 4;
+ // int berechneteHoehe = (int)(targetWidth * aspectRatio);
+
+ // // Größe setzen
+ // logoBox.Size = new Size(targetWidth, berechneteHoehe);
+
+ // // Position am unteren Rand
+ // logoBox.Location = new Point(2, pex.ClientSize.Height - berechneteHoehe - 2);
+
+ // // Logo zum Ziel-Control hinzufügen
+ // pex.Controls.Add(logoBox);
+ // logoBox.BringToFront();
+
+ // FadeOutPictureBox(logoBox);
+
+ // pex.Resize += (s, e) =>
+ // {
+ // int newWidth = pex.ClientSize.Width - 4;
+ // int newHeight = (int)(newWidth * aspectRatio);
+ // logoBox.Size = new Size(newWidth, newHeight);
+ // logoBox.Location = new Point(2, pex.ClientSize.Height - newHeight - 2);
+ // };
+ // }
+
+ private string ReadEmbeddedVersion()
+ {
+ var asm = typeof(Program).Assembly;
+ using var s = asm.GetManifestResourceStream("App.Version");
+ using var sr = new StreamReader(s!);
+ return sr.ReadToEnd().Trim();
+ }
+
+ // TODO taken from CadForm, as we don't inherit from it
+
+ public bool OnCommand(string menuId)
+ {
+ return false;
+ }
+ public bool OnUpdateCommand(string menuId, CommandState commandState)
+ {
+ return false;
+ }
+ public void OnSelected(MenuWithHandler selectedMenu, bool selected)
+ {
+
+ }
+
+ // public PropertiesExplorer PropertiesExplorer => propertiesExplorer;
+ public CadCanvas CadCanvas => cadCanvas;
+ public CadFrame CadFrame => cadFrame;
+
+ public MainForm(string[] args) // TODO inherit from CadForm? : base(args)
+ { // interpret the command line arguments as a name of a file, which should be opened
+
+ InitializeComponent();
+ cadFrame = new CadFrame(cadCanvas, this);
+ // ShowLogo(); TODO
+ // this.Icon = Properties.Resources.Icon;
+ Assembly ThisAssembly = Assembly.GetExecutingAssembly();
+ System.IO.Stream str;
+ // TODO
+ // using (str = ThisAssembly.GetManifestResourceStream("ShapeIt.Resources.Icon.ico"))
+ // {
+ // this.Icon = new System.Drawing.Icon(str);
+ // }
+
+ string fileName = "";
+ for (int i = 0; i < args.Length; i++)
+ {
+ if (!args[i].StartsWith("-"))
+ {
+ fileName = args[i];
+ break;
+ }
+ }
+ Project toOpen = null;
+ if (!String.IsNullOrWhiteSpace(fileName))
+ {
+ try
+ {
+ toOpen = Project.ReadFromFile(fileName);
+ }
+ catch { }
+ }
+ if (toOpen == null) CadFrame.GenerateNewProject();
+ else CadFrame.Project = toOpen;
+
+ string version = ReadEmbeddedVersion(); // version from version.txt
+ // this.Text = $"ShapeIt with CADability – Version: {version}";
+
+ if (!Settings.GlobalSettings.ContainsSetting("UserInterface"))
+ {
+ Settings UserInterface = new Settings("UserInterface");
+ Settings.GlobalSettings.AddSetting("UserInterface", UserInterface);
+ IntegerProperty ToolbarButtonSize = new IntegerProperty("ToolbarButtonSize", "ToolbarButtonSize");
+ ToolbarButtonSize.IntegerValue = 0;
+ UserInterface.AddSetting("ToolbarButtonSize", ToolbarButtonSize);
+ IntegerProperty MenuSize = new IntegerProperty("MenuSize", "MenuSize");
+ MenuSize.IntegerValue = 0;
+ UserInterface.AddSetting("MenuSize", MenuSize);
+ }
+ Settings.GlobalSettings.SetValue("Construct.3D_Delete2DBase", false);
+ bool exp = Settings.GlobalSettings.GetBoolValue("Experimental.TestNewContextMenu", false);
+ bool tst = Settings.GlobalSettings.GetBoolValue("ShapeIt.Initialized", false);
+ Settings.GlobalSettings.SetValue("ShapeIt.Initialized", true);
+ CadFrame.FileNameChangedEvent += (name) =>
+ {
+ // if (string.IsNullOrEmpty(name)) this.Text = "ShapeIt with CADability";
+ // else this.Text = "ShapeIt -- " + name;
+ lastSaved = DateTime.Now; // a new file has been opened
+ };
+ CadFrame.ProjectClosedEvent += OnProjectClosed;
+ CadFrame.ProjectOpenedEvent += OnProjectOpened;
+ // CadFrame.UIService.ApplicationIdle += OnIdle; TODO
+ CadFrame.ViewsChangedEvent += OnViewsChanged;
+ if (CadFrame.ActiveView != null) OnViewsChanged(CadFrame);
+ // CadFrame.ControlCenter.RemovePropertyPage("View"); TODO
+ using (str = ThisAssembly.GetManifestResourceStream("ShapeIt.StringTableDeutsch.xml"))
+ {
+ XmlDocument stringXmlDocument = new XmlDocument();
+ stringXmlDocument.Load(str);
+ StringTable.AddStrings(stringXmlDocument);
+ }
+
+ using (str = ThisAssembly.GetManifestResourceStream("ShapeIt.StringTableEnglish.xml"))
+ {
+ XmlDocument stringXmlDocument = new XmlDocument();
+ stringXmlDocument.Load(str);
+ StringTable.AddStrings(stringXmlDocument);
+ }
+ using (str = ThisAssembly.GetManifestResourceStream("ShapeIt.MenuResource.xml"))
+ {
+ XmlDocument menuDocument = new XmlDocument();
+ menuDocument.Load(str);
+#if DEBUG
+ // inject an additional menu "Debug", which we can use to directly execute some debugging code
+ XmlNode mainMenu = menuDocument.SelectSingleNode("Menus/MainMenu/Popup[@MenuId='MenuId.Extras']");
+ if (mainMenu != null)
+ {
+ // Create a new MenuItem element.
+ XmlElement debugMenuItem = menuDocument.CreateElement("MenuItem");
+ // Set the MenuId attribute to "MenuId.Debug".
+ debugMenuItem.SetAttribute("MenuId", "MenuId.Debug");
+ // Append the new MenuItem element to the MainMenu node.
+ mainMenu.AppendChild(debugMenuItem);
+ }
+#endif
+ XmlNode toolbar = menuDocument.SelectSingleNode("Menus/Popup[@MenuId='Toolbar']");
+ // SetToolbar(toolbar); // TODO CadForm
+ MenuResource.SetMenuResource(menuDocument);
+ // ResetMainMenu(null); // TODO CadForm
+ }
+
+ lastSaved = DateTime.Now;
+ AppDomain.CurrentDomain.UnhandledException += (sender, exobj) =>
+ {
+ // exobj.ExceptionObject as Exception;
+ try
+ {
+ string path = System.IO.Path.GetTempPath();
+ path = System.IO.Path.Combine(path, "ShapeIt");
+ DirectoryInfo dirInfo = Directory.CreateDirectory(path);
+ string currentFileName = CadFrame.Project.FileName;
+ if (string.IsNullOrEmpty(CadFrame.Project.FileName))
+ {
+ path = System.IO.Path.Combine(path, "crash_" + DateTime.Now.ToString("yyMMddHHmm") + ".cdb.json");
+ currentFileName = "unknown";
+ }
+ else
+ {
+ string crashFileName = System.IO.Path.GetFileNameWithoutExtension(CadFrame.Project.FileName);
+ if (crashFileName.EndsWith(".cdb")) crashFileName = System.IO.Path.GetFileNameWithoutExtension(crashFileName); // we usually have two extensions: .cdb.json
+ path = System.IO.Path.Combine(path, crashFileName + "_X.cdb.json");
+ }
+ CadFrame.Project.WriteToFile(path);
+ File.WriteAllText(System.IO.Path.Combine(System.IO.Path.GetTempPath(), @"ShapeIt\Crash.txt"), currentFileName + "\n" + path);
+ }
+ catch (Exception) { }
+ ;
+ };
+ // the following installs the property page for modelling. This connects all modelling
+ // tasks of ShapeIt with CADability
+ // IPropertyPage modellingPropPage = CadFrame.ControlCenter.AddPropertyPage("Modelling", 6); TODO
+ // modellingPropertyEntries = new ModellingPropertyEntries(CadFrame); TODO currently crashes
+ // modellingPropPage.Add(modellingPropertyEntries, false); TODO implement properties explorer implementing IControlCenter
+ // CadFrame.ControlCenter.ShowPropertyPage("Modelling"); TODO
+ }
+
+ private void OnViewsChanged(IFrame theFrame)
+ {
+ theFrame.ActiveView.Projection.ProjectionChangedEvent -= OnProjectionChanged; // not to double it?
+ theFrame.ActiveView.Projection.ProjectionChangedEvent += OnProjectionChanged;
+ }
+
+ private void OnProjectionChanged(Projection sender, EventArgs args)
+ {
+ projectionChanged = true;
+ }
+
+// // TODO reimplement in Avalonia
+// protected override void OnShown(EventArgs e)
+// {
+// // check for crash
+// if (!crashChecked)
+// {
+// crashChecked = true;
+// string crashPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), @"ShapeIt\Crash.txt");
+// // if (File.Exists(crashPath))
+// // {
+// // string[] lines = File.ReadAllLines(System.IO.Path.Combine(System.IO.Path.GetTempPath(), @"ShapeIt\Crash.txt"));
+// // if (lines.Length == 2)
+// // {
+// // string ask = StringTable.GetFormattedString("ShapeIt.RestoreAfterCrash", lines[0]);
+// // if (CadFrame.UIService.ShowMessageBox(ask, "ShapeIt", CADability.Substitutes.MessageBoxButtons.YesNo) == CADability.Substitutes.DialogResult.Yes)
+// // {
+// // CadFrame.Project = Project.ReadFromFile(lines[1]);
+// // CadFrame.Project.FileName = lines[0];
+
+// // this.Text = "ShapeIt -- " + lines[0];
+// // }
+// // }
+// // File.Delete(crashPath);
+// // }
+// #if DEBUG
+// AutoDebug();
+// #endif
+// }
+// base.OnActivated(e);
+// }
+#if DEBUG
+ private void AutoDebug()
+ {
+ return;
+ string? filename = @"C:\Users\gerha\Documents\Zeichnungen\RoundEdgesTest2.cdb.json";
+ // add code here to be executed automatically upon start in debug mode
+ // there is no mouse interaction before this code is finished
+ if (string.IsNullOrEmpty(filename))
+ {
+ string[] mru = MRUFiles.GetMRUFiles();
+ if (mru.Length > 0) filename = mru.Last().Split(';')[0];
+ }
+ if (!string.IsNullOrEmpty(filename)) CadFrame.Project = Project.ReadFromFile(filename);
+
+ string command = "";
+ List slds = new List();
+ Solid operand1 = null, operand2 = null;
+ List difference = new List();
+ List intersection = new List();
+ List union = new List();
+ List edgeMarkers = new List();
+ foreach (CADability.GeoObject.IGeoObject go in CadFrame.Project.GetActiveModel().AllObjects)
+ {
+ if (go is CADability.GeoObject.Solid sld)
+ {
+ slds.Add(sld);
+ if (sld.Style != null)
+ {
+ if (sld.Style.Name == "Operand1") operand1 = sld;
+ else if (sld.Style.Name == "Operand2") operand2 = sld;
+ else if (sld.Style.Name == "Difference") difference.Add(sld);
+ else if (sld.Style.Name == "Union") union.Add(sld);
+ else if (sld.Style.Name == "Intersection") intersection.Add(sld);
+ }
+ }
+ if (go is ICurve curve)
+ {
+ if (go.Style!=null && go.Style.Name == "EdgeMarker")
+ {
+ edgeMarkers.Add(curve);
+ }
+ }
+ if (go is Text txt)
+ {
+ command = txt.TextString; // there sould only be one
+ }
+ }
+ if (operand1 != null && operand2 != null)
+ {
+ //if (difference.Count > 0)
+ //{
+ // Solid[] sres = NewBooleanOperation.Subtract(operand1, operand2);
+ // if (sres.Length > 0)
+ // {
+ // Project proj = Project.CreateSimpleProject();
+ // proj.GetActiveModel().Add(sres);
+ // proj.WriteToFile("c:\\Temp\\subtract.cdb.json");
+ // }
+ //}
+ if (command.StartsWith("Difference",StringComparison.OrdinalIgnoreCase))
+ {
+ Solid[] sres = NewBooleanOperation.Subtract(operand1, operand2);
+ }
+ if (command.Equals("Union", StringComparison.OrdinalIgnoreCase) || command.Equals("Unite", StringComparison.OrdinalIgnoreCase))
+ {
+ Solid sres = NewBooleanOperation.Unite(operand1, operand2);
+ }
+ }
+ if (slds.Count == 2)
+ {
+ //Solid un = NewBooleanOperation.Unite(slds[0], slds[1]);
+ //Solid[] sld;
+ //if (slds[0].Volume(0.1) > slds[1].Volume(0.1))
+ //{
+ // sld = NewBooleanOperation.Subtract(slds[0], slds[1]);
+ //}
+ //else
+ //{
+ // sld = NewBooleanOperation.Subtract(slds[1], slds[0]);
+ //}
+ //if (sld.Length > 0)
+ //{
+ // Project proj = Project.CreateSimpleProject();
+ // proj.GetActiveModel().Add(sld);
+ // proj.WriteToFile("c:\\Temp\\subtract.cdb.json");
+ //}
+ }
+ if (edgeMarkers.Count > 0)
+ {
+ List edgesToRound = [];
+ Shell? shellToRound = null;
+ foreach (Solid s in slds)
+ {
+ foreach (Edge edg in s.Shells[0].Edges)
+ {
+ foreach (ICurve em in edgeMarkers)
+ {
+ if (edg.Curve3D.SameGeometry(em, 0.1))
+ {
+ edgesToRound.Add(edg);
+ shellToRound = s.Shells[0];
+ }
+ }
+ }
+ }
+ if (command.StartsWith("RoundEdges", StringComparison.OrdinalIgnoreCase))
+ {
+ string[] parts = command.Split(':');
+ if (parts.Length==2)
+ {
+ double d = double.Parse(parts[1]);
+ if (d>0)
+ {
+ Shell? rounded = shellToRound?.RoundEdges(edgesToRound, d);
+ }
+ }
+ }
+ if (command.StartsWith("ChamferEdges", StringComparison.OrdinalIgnoreCase))
+ {
+ string[] parts = command.Split(':');
+ if (parts.Length == 2)
+ {
+ double d = double.Parse(parts[1]);
+ if (d > 0)
+ {
+ Shell? rounded = shellToRound?.ChamferEdges(edgesToRound, d, d);
+ // CadFrame.Project.GetActiveModel().Add(rounded);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ ///
+ /// Filter the escape key for the modelling property page
+ ///
+ ///
+ ///
+ ///
+// // TODO reimplement in Avalonia
+ // protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ // {
+ // Keys nmKeyData = (Keys)((int)keyData & 0x0FFFF);
+ // CADability.Substitutes.KeyEventArgs e = new CADability.Substitutes.KeyEventArgs((CADability.Substitutes.Keys)keyData);
+ // if (nmKeyData == Keys.Escape)
+ // {
+ // if (modellingPropertyEntries.OnEscape()) return true;
+ // }
+ // return base.ProcessCmdKey(ref msg, keyData);
+ // }
+ ///
+ /// Called when CADability is idle. We use it to save the current project data to a temp file in case of a crash
+ ///
+ ///
+ ///
+ // slowdown OnIdle polling:
+ readonly Stopwatch _idleSw = Stopwatch.StartNew();
+ const int MinIdleCheckMs = 250;
+ bool _idleBusy;
+ void OnIdle(object sender, EventArgs e)
+ {
+ if (_idleBusy) return;
+ if (_idleSw.ElapsedMilliseconds < MinIdleCheckMs) return;
+
+ _idleBusy = true;
+ _idleSw.Restart();
+ try
+ {
+ if (projectionChanged)
+ {
+ projectionChanged = false;
+ modellingPropertyEntries.OnProjectionChanged(); // to update the feedback objects, which are projection dependant
+ }
+ if (modifiedSinceLastAutosave && (DateTime.Now - lastSaved).TotalMinutes > 2)
+ {
+ modifiedSinceLastAutosave = false;
+ string path = System.IO.Path.GetTempPath();
+ path = System.IO.Path.Combine(path, "ShapeIt");
+ DirectoryInfo dirInfo = Directory.CreateDirectory(path);
+ string currentFileName = CadFrame.Project.FileName;
+ if (string.IsNullOrEmpty(CadFrame.Project.FileName)) path = System.IO.Path.Combine(path, "noname.cdb.json");
+ else
+ {
+ string fileName = System.IO.Path.GetFileNameWithoutExtension(CadFrame.Project.FileName);
+ if (fileName.EndsWith(".cdb")) fileName = System.IO.Path.GetFileNameWithoutExtension(fileName); // we usually have two extensions: .cdb.json
+ path = System.IO.Path.Combine(path, fileName + "_.cdb.json");
+ }
+ CadFrame.Project.WriteToFile(path);
+ CadFrame.Project.FileName = currentFileName; // Project.WriteToFile changes the Project.FileName, restore the current name
+ lastSaved = DateTime.Now;
+ }
+ }
+ finally { _idleBusy = false; }
+ }
+ void OnProjectClosed(Project theProject, IFrame theFrame)
+ {
+ // manage autosave OnIdle, remove autosaved files
+ }
+ ///
+ /// When a new or exisiting project has been opened
+ ///
+ ///
+ ///
+ private void OnProjectOpened(Project theProject, IFrame theFrame)
+ {
+ theProject.ProjectModifiedEvent += (Project sender) =>
+ { // register modifications of the project to manage autosave
+ if (sender == theProject) modifiedSinceLastAutosave = true;
+ };
+ }
+// // TODO reimplement in Avalonia
+ // protected override void OnLoad(EventArgs e)
+ // {
+ // base.OnLoad(e);
+
+ // // this is for recording the session with 1280x720 pixel.
+ // this.Size = new Size(1294, 727);
+
+ // }
+ ///
+ /// Give the user a chance to save the modified project
+ ///
+ ///
+// // TODO reimplement in Avalonia
+ // protected override void OnFormClosing(FormClosingEventArgs e)
+ // {
+ // if (!CadFrame.Project.SaveModified()) e.Cancel = true;
+ // base.OnFormClosing(e);
+ // }
+// // TODO reimplement in Avalonia
+// public override bool OnCommand(string MenuId)
+// {
+// // forward to modellingPropertyEntries first
+// if (modellingPropertyEntries.OnCommand(MenuId)) return true;
+// if (MenuId == "MenuId.App.Exit")
+// { // this command cannot be handled by CADability.dll
+// Application.Exit();
+// return true;
+// }
+// #if DEBUG
+// else if (MenuId == "MenuId.Debug")
+// {
+// Debug();
+// return true;
+// }
+// #endif
+// else return base.OnCommand(MenuId);
+// }
+// // TODO reimplement in Avalonia
+ // public override bool OnUpdateCommand(string MenuId, CommandState CommandState)
+ // {
+ // // forward to modellingPropertyEntries first
+ // if (modellingPropertyEntries.OnUpdateCommand(MenuId, CommandState)) return true;
+ // return base.OnUpdateCommand(MenuId, CommandState);
+ // }
+// // TODO reimplement in Avalonia
+ // public override void OnSelected(MenuWithHandler selectedMenuItem, bool selected)
+ // {
+ // modellingPropertyEntries.OnSelected(selectedMenuItem, selected);
+ // base.OnSelected(selectedMenuItem, selected);
+ // }
+#if DEBUG
+ private static Random rnd = new Random();
+ private GeoVector RandomVector(double len)
+ {
+ double fx = 1.0, fy = 1.0, fz = 1.0;
+ if (rnd.NextDouble() < 0.5) fx = -fx;
+ if (rnd.NextDouble() < 0.5) fy = -fy;
+ if (rnd.NextDouble() < 0.5) fz = -fz;
+ return len * (new GeoVector(fx * rnd.NextDouble(), fy * rnd.NextDouble(), fz * rnd.NextDouble())).Normalized;
+ }
+ private double RandomDouble(double min, double max)
+ {
+ return min + (max - min) * rnd.NextDouble();
+ }
+ ///
+ /// Here we can add some debug code
+ ///
+ private void DebugX()
+ {
+ Model model = CadFrame.Project.GetActiveModel();
+ Style stl = CadFrame.Project.StyleList.GetDefault(CADability.Attribute.Style.EDefaultFor.Solids);
+ for (int i = 0; i < 64; i++)
+ {
+ double fx = 1.0, fy = 1.0, fz = 1.0;
+ if ((i & 0x1) != 0) fx = -fx;
+ if ((i & 0x2) != 0) fy = -fy;
+ if ((i & 0x4) != 0) fz = -fz;
+ GeoVector dir = RandomVector(RandomDouble(40, 60));
+ GeoPoint center = GeoPoint.Origin + dir;
+ Solid sld = Make3D.MakeSphere(center, 10 + 20 * rnd.NextDouble());
+ ModOp rotate = ModOp.Rotate(center, RandomVector(1.0), SweepAngle.Deg(rnd.NextDouble() * 360.0));
+ sld.Modify(rotate);
+ sld.Style = stl;
+ model.Add(sld);
+ }
+ Solid mainSphere = Make3D.MakeSphere(GeoPoint.Origin, 50);
+ mainSphere.Style = stl;
+ model.Add(mainSphere);
+ }
+
+
+ private void DebugY()
+ {
+ List slds = new List();
+ CADability.GeoObject.Solid sldbig = null;
+ Face cone = null;
+ Face upper = null;
+ Face lower = null;
+ foreach (CADability.GeoObject.IGeoObject go in CadFrame.Project.GetActiveModel().AllObjects)
+ {
+ if (go is CADability.GeoObject.Solid sld)
+ {
+ if (sld.Volume(0.1) > 500000) sldbig = sld;
+ else slds.Add(sld);
+ }
+ }
+
+ var rng = new Random(71);
+ var order = Enumerable.Range(0, slds.Count).ToArray();
+
+ // Fisher–Yates
+ for (int i = slds.Count - 1; i > 0; i--)
+ {
+ int j = rng.Next(i + 1);
+ (order[i], order[j]) = (order[j], order[i]);
+ }
+
+
+ if (slds.Count > 1 && sldbig != null)
+ {
+
+ for (int i = 0; i < slds.Count; i++)
+ {
+ Solid[] res = NewBooleanOperation.Subtract(sldbig, slds[order[i]]);
+ if (res != null && res.Length >= 1) sldbig = res[0];
+ else { }
+ //System.Diagnostics.Debug.WriteLine("Unite " + i.ToString() + " -> " + sldbig.GetExtent(0.0).Size.ToString("F0"));
+ }
+ }
+ }
+ private void Debug()
+ {
+ List slds = new List();
+ Solid operand1 = null, operand2 = null;
+ List difference = new List();
+ List intersection = new List();
+ List union = new List();
+ List edgeMarkers = new List();
+ foreach (CADability.GeoObject.IGeoObject go in CadFrame.Project.GetActiveModel().AllObjects)
+ {
+ if (go is CADability.GeoObject.Solid sld)
+ {
+ slds.Add(sld);
+ if (sld.Style != null)
+ {
+ if (sld.Style.Name == "Operand1") operand1 = sld;
+ else if (sld.Style.Name == "Operand2") operand2 = sld;
+ else if (sld.Style.Name == "Difference") difference.Add(sld);
+ else if (sld.Style.Name == "Union") union.Add(sld);
+ else if (sld.Style.Name == "Intersection") intersection.Add(sld);
+ }
+ }
+ if (go is ICurve curve)
+ {
+ if (go.Style.Name == "EdgeMarker")
+ {
+ edgeMarkers.Add(curve);
+ }
+ }
+ }
+ if (operand1 != null && operand2 != null)
+ {
+ if (difference.Count > 0)
+ {
+ Solid[] sres = NewBooleanOperation.Subtract(operand1, operand2);
+ if (sres.Length > 0)
+ {
+ Project proj = Project.CreateSimpleProject();
+ proj.GetActiveModel().Add(sres);
+ proj.WriteToFile("c:\\Temp\\subtract.cdb.json");
+ }
+ }
+ }
+ if (slds.Count == 2)
+ {
+ //Solid un = NewBooleanOperation.Unite(slds[0], slds[1]);
+ Solid[] sld;
+ if (slds[0].Volume(0.1) > slds[1].Volume(0.1))
+ {
+ sld = NewBooleanOperation.Subtract(slds[0], slds[1]);
+ }
+ else
+ {
+ sld = NewBooleanOperation.Subtract(slds[1], slds[0]);
+ }
+ //if (sld.Length > 0)
+ //{
+ // Project proj = Project.CreateSimpleProject();
+ // proj.GetActiveModel().Add(sld);
+ // proj.WriteToFile("c:\\Temp\\subtract.cdb.json");
+ //}
+ }
+ if (edgeMarkers.Count > 0)
+ {
+ foreach (Solid s in slds)
+ {
+ foreach (Edge edg in s.Shells[0].Edges)
+ {
+ foreach (ICurve em in edgeMarkers)
+ {
+ if (edg.Curve3D.SameGeometry(em, 0.1))
+ {
+ Shell rounded = s.Shells[0].RoundEdges(new Edge[] { edg }, 2);
+ }
+ }
+ }
+ }
+ }
+ //if (slds.Count == 1)
+ //{
+ // Shell shell = slds[0].Shells[0];
+ // foreach (var vtx in shell.Vertices)
+ // {
+ // if ((vtx.Position | new GeoPoint(58, 12, 17)) < 3)
+ // {
+ // Shell rounded = shell.RoundEdges(vtx.Edges, 2);
+ // }
+ // }
+ //}
+ }
+#endif
+ }
+}
diff --git a/ShapeIt/MainForm.cs b/ShapeIt/MainForm.cs
index 8345c977..dcb1e666 100644
--- a/ShapeIt/MainForm.cs
+++ b/ShapeIt/MainForm.cs
@@ -1,4 +1,5 @@
-using CADability;
+#if !AVALONIA
+using CADability;
using CADability.Actions;
using CADability.Attribute;
using CADability.Forms.NET8;
@@ -99,7 +100,7 @@ void FadeOutPictureBox(PictureBox pb)
private void ShowLogo()
{
Control pex = FindControlByName(this, "propertiesExplorer");
- // Create PictureBox
+ // Create PictureBox
logoBox = new PictureBox();
Assembly ThisAssembly = Assembly.GetExecutingAssembly();
using (System.IO.Stream str = ThisAssembly.GetManifestResourceStream("ShapeIt.Resources.ShapeIt2.png"))
@@ -531,7 +532,7 @@ protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
- // this is for recording the session with 1280x720 pixel.
+ // this is for recording the session with 1280x720 pixel.
this.Size = new Size(1294, 727);
}
@@ -746,3 +747,4 @@ private void Debug()
#endif
}
}
+#endif
diff --git a/ShapeIt/MateFacesAction.cs b/ShapeIt/MateFacesAction.cs
index 546d787e..169e21fd 100644
--- a/ShapeIt/MateFacesAction.cs
+++ b/ShapeIt/MateFacesAction.cs
@@ -7,7 +7,9 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+#if !AVALONIA
using System.Windows.Forms;
+#endif
namespace ShapeIt
{
diff --git a/ShapeIt/ModellingPropertyEntries.cs b/ShapeIt/ModellingPropertyEntries.cs
index dadcb2a6..cabecfa2 100644
--- a/ShapeIt/ModellingPropertyEntries.cs
+++ b/ShapeIt/ModellingPropertyEntries.cs
@@ -14,8 +14,10 @@
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
+#if !AVALONIA
using System.Windows.Forms.Design;
using System.Windows.Forms.VisualStyles;
+#endif
using System.Xml.Linq;
using Wintellect.PowerCollections;
using static CADability.Projection;
@@ -42,7 +44,7 @@ internal class ModellingPropertyEntries : PropertyEntryImpl, ICommandHandler
private bool isDragging = false; // the user is dragging a selection rectangle
private bool isMoving = false; // the user is moving the selected objects
private bool isHotspotMoving = false; // the user is movind a hotspot
- private HashSet selectedObjects = new HashSet(); // the currently selected root objects
+ private HashSet selectedObjects = new HashSet(); // the currently selected root objects
private GeoObjectList movingObjects = new GeoObjectList(); // a copy of the selected objects, used for moving
private GeoPoint moveObjectsDownPoint;
private bool downOnSelectedObjects;
@@ -407,7 +409,7 @@ internal bool OnEscape()
#endregion
#region PropertyEntry implementation
- public override PropertyEntryType Flags => PropertyEntryType.Selectable | PropertyEntryType.GroupTitle | PropertyEntryType.Checkable | PropertyEntryType.HasSubEntries; // PropertyEntryType.ContextMenu |
+ public override PropertyEntryType Flags => PropertyEntryType.Selectable | PropertyEntryType.GroupTitle | PropertyEntryType.Checkable | PropertyEntryType.HasSubEntries; // PropertyEntryType.ContextMenu |
public override IPropertyEntry[] SubItems => subEntries.ToArray();
public override string Value
{
@@ -575,7 +577,7 @@ private string GetCursor(Point location, IView vw)
private string resourceIdOfLastSelectedCategory = String.Empty; // what was selected last time? edge, face, solid etc.
private void ComposeModellingEntries(GeoObjectList objectsUnderCursor, IView vw, PickArea? pickArea, bool alsoParent = true, bool addRemove = false)
- { // a mouse left button up took place. Compose all the entries for objects, which can be handled by
+ { // a mouse left button up took place. Compose all the entries for objects, which can be handled by
// the object(s) under the mouse cursor
// what to focus after the selection changed?
IPropertyEntry pe = propertyPage.GetCurrentSelection();
@@ -689,7 +691,7 @@ private void ComposeModellingEntries(GeoObjectList objectsUnderCursor, IView vw,
}
}
- // show actions for all vertices, edges, faces and curves in
+ // show actions for all vertices, edges, faces and curves in
// distibute objects into their categories
List faces = new List();
List shells = new List(); // only Shells, which are not part of a Solid
@@ -860,7 +862,7 @@ private void ComposeModellingEntries(GeoObjectList objectsUnderCursor, IView vw,
}
else if (edges.Any()) subEntries.AddIfNotNull(GetEdgeProperties(vw, edges.First(), clickBeam));
- // add the menus for Solids
+ // add the menus for Solids
if (solids.Count > 1)
{
SelectEntry multipleSolids = new SelectEntry("MultipleSolids.Properties", true);
@@ -1164,7 +1166,7 @@ private IPropertyEntry[] GetEdgesProperties(List curves)
{
List res = new List();
List edges = new List(curves.Select(c => (c as IGeoObject).Owner as Edge));
- DirectMenuEntry makeCurves = new DirectMenuEntry("MenuId.EdgesToCurves"); // too bad, no icon yet,
+ DirectMenuEntry makeCurves = new DirectMenuEntry("MenuId.EdgesToCurves"); // too bad, no icon yet,
makeCurves.ExecuteMenu = (frame) =>
{
GeoObjectList allEdgesAsCurves = new GeoObjectList(curves.Select(c => (c as IGeoObject).Clone())); // need to clone, since the curves ARE the actual edges!
@@ -1185,7 +1187,7 @@ private IPropertyEntry[] GetEdgesProperties(List curves)
return true;
};
res.Add(makeCurves);
- DirectMenuEntry makeFillet = new DirectMenuEntry("MenuId.Fillet"); // too bad, no icon yet,
+ DirectMenuEntry makeFillet = new DirectMenuEntry("MenuId.Fillet"); // too bad, no icon yet,
makeFillet.ExecuteMenu = (frame) =>
{
cadFrame.ControlCenter.ShowPropertyPage("Action");
@@ -1538,7 +1540,7 @@ private void Clear()
if (selection != null)
{
selection.UnSelected(selection); // to regenerate the feedback display
- // and by passing selected as "previousSelected" parameter, they can only regenerate projection dependant feedback
+ // and by passing selected as "previousSelected" parameter, they can only regenerate projection dependant feedback
}
subEntries.Clear();
pp.Remove(this); // to reflect this newly composed entry
@@ -1672,7 +1674,7 @@ private IPropertyEntry GetCurveProperties(IView vw, ICurve curve, bool suppresRu
if ((sphere != null))
{
cadFrame.Project.StyleList.GetDefault(Style.EDefaultFor.Solids)?.Apply(sphere);
- vw.Model.Add(sphere); // add the sphere to the model
+ vw.Model.Add(sphere); // add the sphere to the model
ComposeModellingEntries(new GeoObjectList(sphere), frame.ActiveView, null); // and show it in the control center
}
return true;
@@ -2021,7 +2023,7 @@ private IPropertyEntry GetSolidProperties(IView vw, Solid sld, List fromFa
if (fromBox[i] is Solid solid && sld != solid && solid.GetExtent(0.0).Interferes(sld.GetExtent(0.0))) otherSolids.Add(solid);
}
if (otherSolids.Count > 0)
- { // there are other solids close to this solid, it is not guaranteed that these other solids interfere with this solid
+ { // there are other solids close to this solid, it is not guaranteed that these other solids interfere with this solid
Func ShowThisAndAll = (selected, frame) =>
{ // this is the standard selection behaviour for BRep operations
feedback.Clear();
@@ -2376,7 +2378,7 @@ private IPropertyEntry GetEdgeProperties(IView vw, Edge edg, Axis clickBeam)
}
}
DirectMenuEntry mhdist = new DirectMenuEntry("MenuId.Parametrics.DistanceTo");
- // mhdist.Target = new ParametricsDistanceActionOld(edg, selectAction.Frame);
+ // mhdist.Target = new ParametricsDistanceActionOld(edg, selectAction.Frame);
// TODO!
edgeMenus.Add(mhdist);
@@ -3006,10 +3008,10 @@ private IPropertyEntry GetFeatureProperties(IView vw, IEnumerable featureF
}
///
/// There is a face pointed at by . We try to find a loop of curves on faces
- /// passing through pa. This loop msut be perpendicular to all edges it crosses, so that it could have been used
+ /// passing through pa. This loop msut be perpendicular to all edges it crosses, so that it could have been used
/// in an extrusion operation to build all faces, which are touched by the loop curves. To each loop curve there is a face,
- /// on which this curve resides. There may be several (or none) loops and sets of faces as a result. For each result there
- /// is also a plane, in which all loop curves reside and which is the plane at which the shell can be split to extent or shrink
+ /// on which this curve resides. There may be several (or none) loops and sets of faces as a result. For each result there
+ /// is also a plane, in which all loop curves reside and which is the plane at which the shell can be split to extent or shrink
/// the shape along the edges.
///
///
@@ -3146,7 +3148,7 @@ internal void OnProjectionChanged()
if (selection != null)
{
selection.Selected(selection); // to regenerate the feedback display
- // and by passing selected as "previousSelected" parameter, they can only regenerate projection dependant feedback
+ // and by passing selected as "previousSelected" parameter, they can only regenerate projection dependant feedback
}
}
diff --git a/ShapeIt/ParametricPositionAction.cs b/ShapeIt/ParametricPositionAction.cs
index 450221f6..7178ad24 100644
--- a/ShapeIt/ParametricPositionAction.cs
+++ b/ShapeIt/ParametricPositionAction.cs
@@ -7,13 +7,15 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+#if !AVALONIA
using System.Windows.Forms;
+#endif
namespace ShapeIt
{
///
/// This action starts with a object to position. This might be one or more faces (like a feature) and a touching point or axis where to meassure.
- ///
+ ///
///
internal class ParametricPositionAction : ConstructAction
{
diff --git a/ShapeIt/Program.cs b/ShapeIt/Program.cs
index 263061b3..0da30cd5 100644
--- a/ShapeIt/Program.cs
+++ b/ShapeIt/Program.cs
@@ -1,14 +1,33 @@
+#if AVALONIA
+using Avalonia;
+#endif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+#if !AVALONIA
using System.Windows.Forms;
+#endif
namespace ShapeIt
{
internal static class Program
{
+#if AVALONIA
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace();
+
+#else
///
/// The main entry point for the application.
///
@@ -20,5 +39,6 @@ static void Main(string[] args)
ApplicationConfiguration.Initialize();
Application.Run(new MainForm(args));
}
+#endif
}
-}
\ No newline at end of file
+}
diff --git a/ShapeIt/Properties/Settings.Designer.cs b/ShapeIt/Properties/Settings.Designer.cs
index 35a2d457..901d2cf5 100644
--- a/ShapeIt/Properties/Settings.Designer.cs
+++ b/ShapeIt/Properties/Settings.Designer.cs
@@ -7,16 +7,17 @@
// the code is regenerated.
//
//------------------------------------------------------------------------------
+#if !AVALONIA
namespace ShapeIt.Properties {
-
-
+
+
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
-
+
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-
+
public static Settings Default {
get {
return defaultInstance;
@@ -24,3 +25,4 @@ public static Settings Default {
}
}
}
+#endif
diff --git a/ShapeIt/RoundEdges.cs b/ShapeIt/RoundEdges.cs
index 9710dc2f..b7a83c57 100644
--- a/ShapeIt/RoundEdges.cs
+++ b/ShapeIt/RoundEdges.cs
@@ -7,7 +7,9 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+#if !AVALONIA
using System.Windows.Forms.VisualStyles;
+#endif
using static ShapeIt.ShellExtensions;
namespace ShapeIt
@@ -486,7 +488,7 @@ private bool curveIntersectsShell(ICurve curve)
#endif
Face sweptFace;
// how to test for correct orientation of the sweptCircle?
- // The normal of the swept circle must point towards the filletAxisCurve, it must be a concave surface
+ // The normal of the swept circle must point towards the filletAxisCurve, it must be a concave surface
GeoPoint facnt = filletAxisCurve.Curve3D.PointAt(0.5);
GeoPoint lecnt = leadingEdge.PointAt(0.5);
GeoPoint2D facnt2d = sweptCircle.PositionOf(new GeoPoint(facnt, lecnt));
diff --git a/ShapeIt/ShapeIt.axaml b/ShapeIt/ShapeIt.axaml
new file mode 100644
index 00000000..75d818b7
--- /dev/null
+++ b/ShapeIt/ShapeIt.axaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/ShapeIt/ShapeIt.axaml.cs b/ShapeIt/ShapeIt.axaml.cs
new file mode 100644
index 00000000..fd18a867
--- /dev/null
+++ b/ShapeIt/ShapeIt.axaml.cs
@@ -0,0 +1,23 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+namespace ShapeIt;
+
+public partial class ShapeIt : Application
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainForm(desktop.Args);
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+}
diff --git a/ShapeIt/ShapeIt.csproj b/ShapeIt/ShapeIt.csproj
index c30ae605..a42b098e 100644
--- a/ShapeIt/ShapeIt.csproj
+++ b/ShapeIt/ShapeIt.csproj
@@ -1,12 +1,39 @@
-
+
WinExe
net8.0-windows
enable
true
disable
+
+ false
+ false
+
+
+
+
+ WinExe
+ net8.0
+ enable
+ false
+ disable
+ true
+ AVALONIA
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
True
@@ -37,11 +64,16 @@
-
+
+
+
+
+
+
True
@@ -107,4 +139,4 @@
-
\ No newline at end of file
+
diff --git a/ShapeIt/ShellExtensions.cs b/ShapeIt/ShellExtensions.cs
index 65bf4b04..dd4369a5 100644
--- a/ShapeIt/ShellExtensions.cs
+++ b/ShapeIt/ShellExtensions.cs
@@ -15,7 +15,9 @@
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
+#if !AVALONIA
using System.Windows.Forms.VisualStyles;
+#endif
using Wintellect.PowerCollections;
namespace ShapeIt
@@ -703,14 +705,14 @@ public static Shell[] GetOffsetNew(this Shell shell, double offset)
// or the fillets with the spherical faces. Since each breop operation creates new faces and edges, we attach this information to the user data of the original parts
// and retrieve them after the brep operation is done.
HashSet<(Edge, Face)> dontIntersect = new HashSet<(Edge, Face)>(); // NOT USED ANY MORE, these pairs will be connected and there is no need to calculate the intersection
- // set UserData with the original face and edge references to
+ // set UserData with the original face and edge references to
foreach (Face face in shell.Faces)
{ // makeparallel faces with the provided offset
ISurface offsetSurface = face.Surface.GetOffsetSurface(offset);
if (offsetSurface == null) continue; // a sphere, cylinder or torus shrinking to 0
GeoPoint2D cnt = face.Domain.GetCenter();
// if the orentation is reversed (e.g. a cylinder will have a negativ radius) or the surface disappears, don't use it
- if (offsetSurface != null) //
+ if (offsetSurface != null) //
{
List outline = new List();
foreach (Edge edge in face.OutlineEdges)