diff --git a/exposed/CsDemo/.gitignore b/exposed/CsDemo/.gitignore
new file mode 100644
index 000000000..81861ae73
--- /dev/null
+++ b/exposed/CsDemo/.gitignore
@@ -0,0 +1,369 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Oo]ut/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+/Services/ControlService/ControlService.xml
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
diff --git a/exposed/CsDemo/CsDemo.cs b/exposed/CsDemo/CsDemo.cs
new file mode 100644
index 000000000..102dc732e
--- /dev/null
+++ b/exposed/CsDemo/CsDemo.cs
@@ -0,0 +1,373 @@
+// -----------------------------------------------------------------------------
+// Some sample code for slvs.dll. We draw some geometric entities, provide
+// initial guesses for their positions, and then constrain them. The solver
+// calculates their new positions, in order to satisfy the constraints.
+//
+// The library is distributed as a DLL, but the functions are designed to
+// be usable from .net languages through a P/Invoke. This file contains an
+// example of that process, and a wrapper class around those P/Invoke'd
+// functions that you may wish to use a starting point in your own
+// application.
+//
+// Copyright 2008-2013 Jonathan Westhues.
+// -----------------------------------------------------------------------------
+
+namespace CsDemo;
+
+using SolveSpace;
+
+public static class CsDemo
+{
+ // Call our example functions, which set up some kind of sketch, solve
+ // it, and then print the result.
+ public static void Main()
+ {
+ Console.WriteLine("EXAMPLE IN 3d (by objects):");
+ Example3dWithObjects();
+ Console.WriteLine("");
+
+ Console.WriteLine("EXAMPLE IN 2d (by objects):");
+ Example2dWithObjects();
+ Console.WriteLine("");
+
+ Console.WriteLine("EXAMPLE IN 3d (by handles):");
+ Example3dWithHandles();
+ Console.WriteLine("");
+
+ Console.WriteLine("EXAMPLE IN 2d (by handles):");
+ Example2dWithHandles();
+ Console.WriteLine("");
+ }
+
+ // This is the simplest way to use the library. A set of wrapper
+ // classes allow us to represent entities (e.g., lines and points)
+ // as .net objects. So we create an Solver object, which will contain
+ // the entire sketch, with all the entities and constraints.
+ //
+ // We then create entity objects (for example, points and lines)
+ // associated with that sketch, indicating the initial positions of
+ // those entities and any hierarchical relationships among them (e.g.,
+ // defining a line entity in terms of endpoint entities). We also add
+ // constraints associated with those entities.
+ //
+ // Finally, we solve, and print the new positions of the entities. If the
+ // solution succeeded, then the entities will satisfy the constraints. If
+ // not, then the solver will suggest problematic constraints that, if
+ // removed, would render the sketch solvable.
+
+ private static void Example3dWithObjects()
+ {
+ var slv = new Solver();
+
+ // This will contain a single group, which will arbitrarily number 1.
+ uint g = 1;
+
+ // A point, initially at (x y z) = (10 10 10)
+ var p1 = slv.NewPoint3d(g, 10.0, 10.0, 10.0);
+ // and a second point at (20 20 20)
+ var p2 = slv.NewPoint3d(g, 20.0, 20.0, 20.0);
+
+ // and a line segment connecting them.
+ var ln = slv.NewLineSegment(g, slv.FreeIn3d(), p1, p2);
+
+ // The distance between the points should be 30.0 units.
+ slv.AddConstraint(1, g, Solver.SLVS_C_PT_PT_DISTANCE, slv.FreeIn3d(), 30.0, p1, p2, null, null);
+
+ // Let's tell the solver to keep the second point as close to constant
+ // as possible, instead moving the first point.
+ slv.Solve(g, p2, true);
+
+ if ((slv.GetResult() == Solver.SLVS_RESULT_OKAY))
+ {
+ // We call the GetX(), GetY(), and GetZ() functions to see
+ // where the solver moved our points to.
+ Console.WriteLine($"okay; now at ({p1.GetX():F3}, {p1.GetY():F3}, {p1.GetZ():F3})");
+ Console.WriteLine($" ({p2.GetX():F3}, {p2.GetY():F3}, {p2.GetZ():F3})");
+ Console.WriteLine(slv.GetDof() + " DOF");
+ }
+ else
+ Console.WriteLine("solve failed");
+ }
+
+ private static void Example2dWithObjects()
+ {
+ var slv = new Solver();
+
+ uint g = 1;
+
+ // First, we create our workplane. Its origin corresponds to the origin
+ // of our base frame (x y z) = (0 0 0)
+ var origin = slv.NewPoint3d(g, 0.0, 0.0, 0.0);
+ // and it is parallel to the xy plane, so it has basis vectors (1 0 0)
+ // and (0 1 0).
+ var normal = slv.NewNormal3d(g, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0);
+
+ var wrkpl = slv.NewWorkplane(g, origin, normal);
+
+ // Now create a second group. We'll solve group 2, while leaving group 1
+ // constant; so the workplane that we've created will be locked down,
+ // and the solver can't move it.
+ g = 2;
+ // These points are represented by their coordinates (u v) within the
+ // workplane, so they need only two parameters each.
+ var pl1 = slv.NewPoint2d(g, wrkpl, 10.0, 20.0);
+ var pl2 = slv.NewPoint2d(g, wrkpl, 20.0, 10.0);
+
+ // And we create a line segment with those endpoints.
+ var ln = slv.NewLineSegment(g, wrkpl, pl1, pl2);
+
+ // Now three more points.
+ var pc = slv.NewPoint2d(g, wrkpl, 100.0, 120.0);
+ var ps = slv.NewPoint2d(g, wrkpl, 120.0, 110.0);
+ var pf = slv.NewPoint2d(g, wrkpl, 115.0, 115.0);
+
+ // And arc, centered at point pc, starting at point ps, ending at
+ // point pf.
+ var arc = slv.NewArcOfCircle(g, wrkpl, normal, pc, ps, pf);
+
+ // Now one more point, and a distance
+ var pcc = slv.NewPoint2d(g, wrkpl, 200.0, 200.0);
+ var r = slv.NewDistance(g, wrkpl, 30.0);
+
+ // And a complete circle, centered at point pcc with radius equal to
+ // distance r. The normal is the same as for our workplane.
+ var circle = slv.NewCircle(g, wrkpl, pcc, normal, r);
+
+ // The length of our line segment is 30.0 units.
+ slv.AddConstraint(1, g, Solver.SLVS_C_PT_PT_DISTANCE, wrkpl, 30.0, pl1, pl2, null, null);
+
+ // And the distance from our line segment to the origin is 10.0 units.
+ slv.AddConstraint(2, g, Solver.SLVS_C_PT_LINE_DISTANCE, wrkpl, 10.0, origin, null, ln, null);
+
+ // And the line segment is vertical.
+ slv.AddConstraint(3, g, Solver.SLVS_C_VERTICAL, wrkpl, 0.0, null, null, ln, null);
+
+ // And the distance from one endpoint to the origin is 15.0 units.
+ slv.AddConstraint(4, g, Solver.SLVS_C_PT_PT_DISTANCE, wrkpl, 15.0, pl1, origin, null, null);
+
+ // And same for the other endpoint; so if you add this constraint then
+ // the sketch is overconstrained and will signal an error.
+ // slv.AddConstraint(5, g, Solver.SLVS_C_PT_PT_DISTANCE, _
+ // wrkpl, 18.0, pl2, origin, Nothing, Nothing)
+
+ // The arc and the circle have equal radius.
+ slv.AddConstraint(6, g, Solver.SLVS_C_EQUAL_RADIUS, wrkpl, 0.0, null, null, arc, circle);
+
+ // The arc has radius 17.0 units.
+ slv.AddConstraint(7, g, Solver.SLVS_C_DIAMETER, wrkpl, 2 * 17.0, null, null, arc, null);
+
+ // If the solver fails, then ask it to report which constraints caused
+ // the problem.
+ slv.Solve(g, true);
+
+ if (slv.GetResult() == Solver.SLVS_RESULT_OKAY)
+ {
+ Console.WriteLine("solved okay");
+ // We call the GetU(), GetV(), and GetDistance() functions to
+ // see where the solver moved our points and distances to.
+ Console.WriteLine($"line from ({pl1.GetU():F3} {pl1.GetV():F3}) to ({pl2.GetU():F3} {pl2.GetV():F3})");
+ Console.WriteLine("arc center ({0:F3} {1:F3}) start ({2:F3} {3:F3}) " + "finish ({4:F3} {5:F3})", pc.GetU(), pc.GetV(), ps.GetU(), ps.GetV(), pf.GetU(), pf.GetV());
+ Console.WriteLine($"circle center ({pcc.GetU():F3} {pcc.GetV():F3}) radius {r.GetDistance():F3}");
+
+ Console.WriteLine(slv.GetDof() + " DOF");
+ }
+ else
+ {
+ Console.Write("solve failed; problematic constraints are:");
+ foreach (var t in slv.GetFaileds())
+ {
+ Console.Write(" " + t);
+ }
+
+ Console.WriteLine("");
+ if ((slv.GetResult() == Solver.SLVS_RESULT_INCONSISTENT))
+ {
+ Console.WriteLine("system inconsistent");
+ }
+ else
+ {
+ Console.WriteLine("system nonconvergent");
+ }
+ }
+ }
+
+ // This is a lower-level way to use the library. Internally, the library
+ // represents parameters, entities, and constraints by integer handles.
+ // Here, those handles are assigned manually, and not by the wrapper
+ // classes.
+
+ private static void Example3dWithHandles()
+ {
+ var slv = new Solver();
+
+ // This will contain a single group, which will arbitrarily number 1.
+ uint g = 1;
+
+ // A point, initially at (x y z) = (10 10 10)
+ slv.AddParam(1, g, 10.0);
+ slv.AddParam(2, g, 10.0);
+ slv.AddParam(3, g, 10.0);
+ slv.AddPoint3d(101, g, 1, 2, 3);
+
+ // and a second point at (20 20 20)
+ slv.AddParam(4, g, 20.0);
+ slv.AddParam(5, g, 20.0);
+ slv.AddParam(6, g, 20.0);
+ slv.AddPoint3d(102, g, 4, 5, 6);
+
+ // and a line segment connecting them.
+ slv.AddLineSegment(200, g, Solver.SLVS_FREE_IN_3D, 101, 102);
+
+ // The distance between the points should be 30.0 units.
+ slv.AddConstraint(1, g, Solver.SLVS_C_PT_PT_DISTANCE, Solver.SLVS_FREE_IN_3D, 30.0, 101, 102, 0, 0);
+
+ // Let's tell the solver to keep the second point as close to constant
+ // as possible, instead moving the first point. That's parameters
+ // 4, 5, and 6.
+ slv.Solve(g, 4, 5, 6, 0, true);
+
+ if ((slv.GetResult() == Solver.SLVS_RESULT_OKAY))
+ {
+ // Note that we are referring to the parameters by their handles,
+ // and not by their index in the list. This is a difference from
+ // the C example.
+ Console.WriteLine($"okay; now at ({slv.GetParamByHandle(1):F3}, {slv.GetParamByHandle(2):F3}, {slv.GetParamByHandle(3):F3})");
+ Console.WriteLine($" ({slv.GetParamByHandle(4):F3}, {slv.GetParamByHandle(5):F3}, {slv.GetParamByHandle(6):F3})");
+ Console.WriteLine(slv.GetDof() + " DOF");
+ }
+ else
+ {
+ Console.WriteLine("solve failed");
+ }
+ }
+
+ private static void Example2dWithHandles()
+ {
+ double qw = 0, qx = 0, qy = 0, qz = 0;
+
+ var slv = new Solver();
+
+ uint g = 1;
+
+ // First, we create our workplane. Its origin corresponds to the origin
+ // of our base frame (x y z) = (0 0 0)
+ slv.AddParam(1, g, 0.0);
+ slv.AddParam(2, g, 0.0);
+ slv.AddParam(3, g, 0.0);
+ slv.AddPoint3d(101, g, 1, 2, 3);
+ // and it is parallel to the xy plane, so it has basis vectors (1 0 0)
+ // and (0 1 0).
+ slv.MakeQuaternion(1, 0, 0, 0, 1, 0, ref qw, ref qx, ref qy, ref qz);
+ slv.AddParam(4, g, qw);
+ slv.AddParam(5, g, qx);
+ slv.AddParam(6, g, qy);
+ slv.AddParam(7, g, qz);
+ slv.AddNormal3d(102, g, 4, 5, 6, 7);
+
+ slv.AddWorkplane(200, g, 101, 102);
+
+ // Now create a second group. We'll solve group 2, while leaving group 1
+ // constant; so the workplane that we've created will be locked down,
+ // and the solver can't move it.
+ g = 2;
+ // These points are represented by their coordinates (u v) within the
+ // workplane, so they need only two parameters each.
+ slv.AddParam(11, g, 10.0);
+ slv.AddParam(12, g, 20.0);
+ slv.AddPoint2d(301, g, 200, 11, 12);
+
+ slv.AddParam(13, g, 20.0);
+ slv.AddParam(14, g, 10.0);
+ slv.AddPoint2d(302, g, 200, 13, 14);
+
+ // And we create a line segment with those endpoints.
+ slv.AddLineSegment(400, g, 200, 301, 302);
+
+ // Now three more points.
+ slv.AddParam(15, g, 100.0);
+ slv.AddParam(16, g, 120.0);
+ slv.AddPoint2d(303, g, 200, 15, 16);
+
+ slv.AddParam(17, g, 120.0);
+ slv.AddParam(18, g, 110.0);
+ slv.AddPoint2d(304, g, 200, 17, 18);
+
+ slv.AddParam(19, g, 115.0);
+ slv.AddParam(20, g, 115.0);
+ slv.AddPoint2d(305, g, 200, 19, 20);
+
+ // And arc, centered at point 303, starting at point 304, ending at
+ // point 305.
+ slv.AddArcOfCircle(401, g, 200, 102, 303, 304, 305);
+
+ // Now one more point, and a distance
+ slv.AddParam(21, g, 200.0);
+ slv.AddParam(22, g, 200.0);
+ slv.AddPoint2d(306, g, 200, 21, 22);
+
+ slv.AddParam(23, g, 30.0);
+ slv.AddDistance(307, g, 200, 23);
+
+ // And a complete circle, centered at point 306 with radius equal to
+ // distance 307. The normal is 102, the same as our workplane.
+ slv.AddCircle(402, g, 200, 306, 102, 307);
+
+ // The length of our line segment is 30.0 units.
+ slv.AddConstraint(1, g, Solver.SLVS_C_PT_PT_DISTANCE, 200, 30.0, 301, 302, 0, 0);
+
+ // And the distance from our line segment to the origin is 10.0 units.
+ slv.AddConstraint(2, g, Solver.SLVS_C_PT_LINE_DISTANCE, 200, 10.0, 101, 0, 400, 0);
+
+ // And the line segment is vertical.
+ slv.AddConstraint(3, g, Solver.SLVS_C_VERTICAL, 200, 0.0, 0, 0, 400, 0);
+
+ // And the distance from one endpoint to the origin is 15.0 units.
+ slv.AddConstraint(4, g, Solver.SLVS_C_PT_PT_DISTANCE, 200, 15.0, 301, 101, 0, 0);
+
+ // And same for the other endpoint; so if you add this constraint then
+ // the sketch is overconstrained and will signal an error.
+ // slv.AddConstraint(5, g, Solver.SLVS_C_PT_PT_DISTANCE, _
+ // 200, 18.0, 302, 101, 0, 0)
+
+ // The arc and the circle have equal radius.
+ slv.AddConstraint(6, g, Solver.SLVS_C_EQUAL_RADIUS, 200, 0.0, 0, 0, 401, 402);
+
+ // The arc has radius 17.0 units.
+ slv.AddConstraint(7, g, Solver.SLVS_C_DIAMETER, 200, 2 * 17.0, 0, 0, 401, 0);
+
+ // If the solver fails, then ask it to report which constraints caused
+ // the problem.
+ slv.Solve(g, 0, 0, 0, 0, true);
+
+ if ((slv.GetResult() == Solver.SLVS_RESULT_OKAY))
+ {
+ Console.WriteLine("solved okay");
+ // Note that we are referring to the parameters by their handles,
+ // and not by their index in the list. This is a difference from
+ // the C example.
+ Console.WriteLine($"line from ({slv.GetParamByHandle(11):F3} {slv.GetParamByHandle(12):F3}) to ({slv.GetParamByHandle(13):F3} {slv.GetParamByHandle(14):F3})");
+ Console.WriteLine("arc center ({0:F3} {1:F3}) start ({2:F3} {3:F3}) " + "finish ({4:F3} {5:F3})", slv.GetParamByHandle(15), slv.GetParamByHandle(16), slv.GetParamByHandle(17), slv.GetParamByHandle(18), slv.GetParamByHandle(19), slv.GetParamByHandle(20));
+ Console.WriteLine($"circle center ({slv.GetParamByHandle(21):F3} {slv.GetParamByHandle(22):F3}) radius {slv.GetParamByHandle(23):F3}");
+
+ Console.WriteLine(slv.GetDof() + " DOF");
+ }
+ else
+ {
+ Console.Write("solve failed; problematic constraints are:");
+ foreach (var t in slv.GetFaileds())
+ {
+ Console.Write(" " + t);
+ }
+
+ Console.WriteLine("");
+ if ((slv.GetResult() == Solver.SLVS_RESULT_INCONSISTENT))
+ {
+ Console.WriteLine("system inconsistent");
+ }
+ else
+ {
+ Console.WriteLine("system nonconvergent");
+ }
+ }
+ }
+}
diff --git a/exposed/CsDemo/CsDemo.csproj b/exposed/CsDemo/CsDemo.csproj
new file mode 100644
index 000000000..74abf5c97
--- /dev/null
+++ b/exposed/CsDemo/CsDemo.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/exposed/CsDemo/Solver.cs b/exposed/CsDemo/Solver.cs
new file mode 100644
index 000000000..98c400257
--- /dev/null
+++ b/exposed/CsDemo/Solver.cs
@@ -0,0 +1,831 @@
+namespace SolveSpace;
+
+using System.Runtime.InteropServices;
+
+// This is a thin wrapper around those functions, which provides
+// convenience functions similar to those provided in slvs.h for the C API.
+public class Solver
+{
+ // The interface to the library, and the wrapper functions around
+ // that interface, follow.
+
+ // These are the core functions imported from the DLL
+ [DllImport("slvs.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Slvs_Solve(IntPtr sys, uint hg);
+
+ [DllImport("slvs.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Slvs_MakeQuaternion(double ux, double uy, double uz, double vx, double vy, double vz, ref double qw, ref double qx, ref double qy, ref double qz);
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Slvs_Param
+ {
+ public uint h;
+ public uint group;
+ public double val;
+ }
+
+ public const int SLVS_FREE_IN_3D = 0;
+
+ #region Entities
+
+ public const int SLVS_E_POINT_IN_3D = 50000;
+ public const int SLVS_E_POINT_IN_2D = 50001;
+ public const int SLVS_E_NORMAL_IN_3D = 60000;
+ public const int SLVS_E_NORMAL_IN_2D = 60001;
+ public const int SLVS_E_DISTANCE = 70000;
+ public const int SLVS_E_WORKPLANE = 80000;
+ public const int SLVS_E_LINE_SEGMENT = 80001;
+ public const int SLVS_E_CUBIC = 80002;
+ public const int SLVS_E_CIRCLE = 80003;
+ public const int SLVS_E_ARC_OF_CIRCLE = 80004;
+
+ #endregion
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Slvs_Entity
+ {
+ public uint h;
+ public uint group;
+
+ public int type;
+
+ public uint wrkpl;
+ public uint point0;
+ public uint point1;
+ public uint point2;
+ public uint point3;
+ public uint normal;
+ public uint distance;
+
+ public uint param0;
+ public uint param1;
+ public uint param2;
+ public uint param3;
+ }
+
+ #region Constraints
+
+ public const int SLVS_C_POINTS_COINCIDENT = 100000;
+ public const int SLVS_C_PT_PT_DISTANCE = 100001;
+ public const int SLVS_C_PT_PLANE_DISTANCE = 100002;
+ public const int SLVS_C_PT_LINE_DISTANCE = 100003;
+ public const int SLVS_C_PT_FACE_DISTANCE = 100004;
+ public const int SLVS_C_PT_IN_PLANE = 100005;
+ public const int SLVS_C_PT_ON_LINE = 100006;
+ public const int SLVS_C_PT_ON_FACE = 100007;
+ public const int SLVS_C_EQUAL_LENGTH_LINES = 100008;
+ public const int SLVS_C_LENGTH_RATIO = 100009;
+ public const int SLVS_C_EQ_LEN_PT_LINE_D = 100010;
+ public const int SLVS_C_EQ_PT_LN_DISTANCES = 100011;
+ public const int SLVS_C_EQUAL_ANGLE = 100012;
+ public const int SLVS_C_EQUAL_LINE_ARC_LEN = 100013;
+ public const int SLVS_C_SYMMETRIC = 100014;
+ public const int SLVS_C_SYMMETRIC_HORIZ = 100015;
+ public const int SLVS_C_SYMMETRIC_VERT = 100016;
+ public const int SLVS_C_SYMMETRIC_LINE = 100017;
+ public const int SLVS_C_AT_MIDPOINT = 100018;
+ public const int SLVS_C_HORIZONTAL = 100019;
+ public const int SLVS_C_VERTICAL = 100020;
+ public const int SLVS_C_DIAMETER = 100021;
+ public const int SLVS_C_PT_ON_CIRCLE = 100022;
+ public const int SLVS_C_SAME_ORIENTATION = 100023;
+ public const int SLVS_C_ANGLE = 100024;
+ public const int SLVS_C_PARALLEL = 100025;
+ public const int SLVS_C_PERPENDICULAR = 100026;
+ public const int SLVS_C_ARC_LINE_TANGENT = 100027;
+ public const int SLVS_C_CUBIC_LINE_TANGENT = 100028;
+ public const int SLVS_C_EQUAL_RADIUS = 100029;
+ public const int SLVS_C_PROJ_PT_DISTANCE = 100030;
+ public const int SLVS_C_WHERE_DRAGGED = 100031;
+ public const int SLVS_C_CURVE_CURVE_TANGENT = 100032;
+ public const int SLVS_C_LENGTH_DIFFERENCE = 100033;
+
+ #endregion
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Slvs_Constraint
+ {
+ public uint h;
+ public uint group;
+
+ public int type;
+
+ public uint wrkpl;
+
+ public double valA;
+ public uint ptA;
+ public uint ptB;
+ public uint entityA;
+ public uint entityB;
+ public uint entityC;
+ public uint entityD;
+
+ public int other;
+ public int other2;
+ }
+
+ #region Results
+
+ public const int SLVS_RESULT_OKAY = 0;
+ public const int SLVS_RESULT_INCONSISTENT = 1;
+ public const int SLVS_RESULT_DIDNT_CONVERGE = 2;
+ public const int SLVS_RESULT_TOO_MANY_UNKNOWNS = 3;
+
+ #endregion
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Slvs_System
+ {
+ public IntPtr param;
+ public int @params;
+ public IntPtr entity;
+ public int entities;
+ public IntPtr constraint;
+ public int constraints;
+
+ public uint dragged0;
+ public uint dragged1;
+ public uint dragged2;
+ public uint dragged3;
+
+ public int calculatedFaileds;
+
+ public IntPtr failed;
+ public int faileds;
+
+ public int dof;
+
+ public int result;
+ }
+
+ private readonly List Params = new();
+ private readonly List Entities = new();
+ private readonly List Constraints = new();
+
+ private readonly List Faileds = new();
+
+ private int Result;
+ private int Dof;
+
+ // Return the value of a parameter, by its handle. This function
+ // may be used, for example, to obtain the new values of the
+ // parameters after a call to Solve().
+ public double GetParamByHandle(uint h)
+ {
+ foreach (var t in Params)
+ {
+ if ((t.h == h))
+ {
+ return t.val;
+ }
+ }
+
+ throw new Exception("Invalid parameter handle.");
+ }
+
+ // Return the value of a parameter, by its index in the list (where
+ // that index is determined by the order in which the parameters
+ // were inserted with AddParam(), not by its handle).
+ public double GetParamByIndex(int i)
+ {
+ return Params[i].val;
+ }
+
+ // Get the result after a call to Solve(). This may be
+ // SLVS_RESULT_OKAY - it worked
+ // SLVS_RESULT_INCONSISTENT - failed, inconsistent
+ // SLVS_RESULT_DIDNT_CONVERGE - consistent, but still failed
+ // SLVS_RESULT_TOO_MANY_UNKNOWNS - too many parameters in one group
+ public int GetResult()
+ {
+ return Result;
+ }
+
+ // After a call to Solve(), this returns the number of unconstrained
+ // degrees of freedom for the sketch.
+ public int GetDof()
+ {
+ return Dof;
+ }
+
+ // After a failing call to Solve(), this returns the list of
+ // constraints, identified by their handle, that would fix the
+ // system if they were deleted. This list will be populated only
+ // if calculateFaileds is True in the Solve() call.
+ public List GetFaileds()
+ {
+ return Faileds;
+ }
+
+ // Clear our lists of entities, constraints, and parameters.
+ public void ResetAll()
+ {
+ Params.Clear();
+ Entities.Clear();
+ Constraints.Clear();
+ }
+
+
+ // These functions are broadly similar to the Slvs_Make...
+ // functions in slvs.h. See the file DOC.txt accompanying the
+ // library for details.
+
+ public void AddParam(uint h, uint group, double val)
+ {
+ Slvs_Param p = new()
+ {
+ h = h,
+ group = group,
+ val = val
+ };
+ Params.Add(p);
+ }
+
+ public void AddPoint2d(uint h, uint group, uint wrkpl, uint u, uint v)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_POINT_IN_2D,
+ wrkpl = wrkpl,
+ param0 = u,
+ param1 = v
+ };
+ Entities.Add(e);
+ }
+
+ public void AddPoint3d(uint h, uint group, uint x, uint y, uint z)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_POINT_IN_3D,
+ wrkpl = SLVS_FREE_IN_3D,
+ param0 = x,
+ param1 = y,
+ param2 = z
+ };
+ Entities.Add(e);
+ }
+
+ public void AddNormal3d(uint h, uint group, uint qw, uint qx, uint qy, uint qz)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_NORMAL_IN_3D,
+ wrkpl = SLVS_FREE_IN_3D,
+ param0 = qw,
+ param1 = qx,
+ param2 = qy,
+ param3 = qz
+ };
+ Entities.Add(e);
+ }
+
+ public void AddNormal2d(uint h, uint group, uint wrkpl)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_NORMAL_IN_2D,
+ wrkpl = wrkpl
+ };
+ Entities.Add(e);
+ }
+
+ public void AddDistance(uint h, uint group, uint wrkpl, uint d)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_DISTANCE,
+ wrkpl = wrkpl,
+ param0 = d
+ };
+ Entities.Add(e);
+ }
+
+ public void AddLineSegment(uint h, uint group, uint wrkpl, uint ptA, uint ptB)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_LINE_SEGMENT,
+ wrkpl = wrkpl,
+ point0 = ptA,
+ point1 = ptB
+ };
+ Entities.Add(e);
+ }
+
+ public void AddCubic(uint h, uint group, uint wrkpl, uint pt0, uint pt1, uint pt2, uint pt3)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_CUBIC,
+ wrkpl = wrkpl,
+ point0 = pt0,
+ point1 = pt1,
+ point2 = pt2,
+ point3 = pt3
+ };
+ Entities.Add(e);
+ }
+
+ public void AddArcOfCircle(uint h, uint group, uint wrkpl, uint normal, uint center, uint pstart, uint pend)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_ARC_OF_CIRCLE,
+ wrkpl = wrkpl,
+ normal = normal,
+ point0 = center,
+ point1 = pstart,
+ point2 = pend
+ };
+ Entities.Add(e);
+ }
+
+ public void AddCircle(uint h, uint group, uint wrkpl, uint center, uint normal, uint radius)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_CIRCLE,
+ wrkpl = wrkpl,
+ point0 = center,
+ normal = normal,
+ distance = radius
+ };
+ Entities.Add(e);
+ }
+
+ public void AddWorkplane(uint h, uint group, uint origin, uint normal)
+ {
+ Slvs_Entity e = new()
+ {
+ h = h,
+ group = group,
+ type = SLVS_E_WORKPLANE,
+ wrkpl = SLVS_FREE_IN_3D,
+ point0 = origin,
+ normal = normal
+ };
+ Entities.Add(e);
+ }
+
+ public void AddConstraint(uint h, uint group, int type, uint wrkpl, double valA, uint ptA, uint ptB, uint entityA, uint entityB)
+ {
+ Slvs_Constraint c = new()
+ {
+ h = h,
+ group = group,
+ type = type,
+ wrkpl = wrkpl,
+ valA = valA,
+ ptA = ptA,
+ ptB = ptB,
+ entityA = entityA,
+ entityB = entityB
+ };
+ Constraints.Add(c);
+ }
+
+ // Solve the system. The geometry of the system must already have
+ // been specified through the Add...() calls. The result of the
+ // solution process may be obtained by calling GetResult(),
+ // GetFaileds(), GetDof(), and GetParamByXXX().
+ //
+ // The parameters draggedx (indicated by their handles) will be held
+ // as close as possible to their original positions, even if this
+ // results in large moves for other parameters. This feature may be
+ // useful if, for example, the user is dragging the point whose
+ // location is defined by those parameters. Unused draggedx
+ // parameters may be specified as zero.
+ public void Solve(uint group, uint dragged0, uint dragged1, uint dragged2, uint dragged3, bool calculateFaileds)
+ {
+ var p = new Slvs_Param[Params.Count + 1];
+ var i = 0;
+ foreach (var pp in Params)
+ {
+ p[i] = pp;
+ i += 1;
+ }
+
+ var e = new Slvs_Entity[Entities.Count + 1];
+ i = 0;
+ foreach (var ee in Entities)
+ {
+ e[i] = ee;
+ i += 1;
+ }
+
+ var c = new Slvs_Constraint[Constraints.Count + 1];
+ i = 0;
+ foreach (var cc in Constraints)
+ {
+ c[i] = cc;
+ i += 1;
+ }
+
+ var f = new uint[Constraints.Count + 1];
+
+ Slvs_System sys = new();
+
+ var pgc = GCHandle.Alloc(p, GCHandleType.Pinned);
+ sys.param = pgc.AddrOfPinnedObject();
+ sys.@params = Params.Count;
+ var egc = GCHandle.Alloc(e, GCHandleType.Pinned);
+ sys.entity = egc.AddrOfPinnedObject();
+ sys.entities = Entities.Count;
+ var cgc = GCHandle.Alloc(c, GCHandleType.Pinned);
+ sys.constraint = cgc.AddrOfPinnedObject();
+ sys.constraints = Constraints.Count;
+
+ sys.dragged0 = dragged0;
+ sys.dragged1 = dragged1;
+ sys.dragged2 = dragged2;
+ sys.dragged3 = dragged3;
+
+ var fgc = GCHandle.Alloc(f, GCHandleType.Pinned);
+ sys.calculatedFaileds = calculateFaileds ? 1 : 0;
+ sys.faileds = Constraints.Count;
+ sys.failed = fgc.AddrOfPinnedObject();
+
+ var sysgc = GCHandle.Alloc(sys, GCHandleType.Pinned);
+
+ Slvs_Solve(sysgc.AddrOfPinnedObject(), group);
+
+ sys = (Slvs_System)sysgc.Target;
+
+ for (i = 0; i <= Params.Count - 1; i++)
+ {
+ Params[i] = p[i];
+ }
+
+ Faileds.Clear();
+ for (i = 0; i <= sys.faileds - 1; i++)
+ {
+ Faileds.Add(f[i]);
+ }
+
+ sysgc.Free();
+ fgc.Free();
+ pgc.Free();
+ egc.Free();
+ cgc.Free();
+
+ Result = sys.result;
+ Dof = sys.dof;
+ }
+
+ // A simpler version of the function, if the parameters being dragged
+ // correspond to a single point.
+ public void Solve(uint group, Point dragged, bool calculatedFaileds)
+ {
+ switch (dragged)
+ {
+ case Point2d point2d:
+ Solve(group, point2d.up.H, point2d.vp.H, 0, 0, calculatedFaileds);
+ break;
+ case Point3d point3d:
+ Solve(group, point3d.xp.H, point3d.yp.H, point3d.zp.H, 0, calculatedFaileds);
+ break;
+ default:
+ throw new Exception("Can't get dragged params for point.");
+ }
+ }
+
+ // or if it's a single distance (e.g., the radius of a circle)
+ public void Solve(uint group, Distance dragged, bool calculatedFaileds)
+ {
+ Solve(group, dragged.dp.H, 0, 0, 0, calculatedFaileds);
+ }
+
+ // or if it's nothing.
+ public void Solve(uint group, bool calculateFaileds)
+ {
+ Solve(group, 0, 0, 0, 0, calculateFaileds);
+ }
+
+ // Return the quaternion in (qw, qx, qy, qz) that represents a
+ // rotation from the base frame to a coordinate system with the
+ // specified basis vectors u and v. For example, u = (0, 1, 0)
+ // and v = (0, 0, 1) specifies the yz plane, such that a point with
+ // (u, v) = (7, 12) has (x, y, z) = (0, 7, 12).
+ public void MakeQuaternion(double ux, double uy, double uz, double vx, double vy, double vz, ref double qw, ref double qx, ref double qy, ref double qz)
+ {
+ Slvs_MakeQuaternion(ux, uy, uz, vx, vy, vz, ref qw, ref qx, ref qy, ref qz);
+ }
+
+ public Workplane FreeIn3d()
+ {
+ return new Workplane(this);
+ }
+
+ // Functions to create the object-oriented wrappers defined below.
+
+ public Param NewParam(uint group, double val)
+ {
+ return new Param(this, group, val);
+ }
+
+ public Point2d NewPoint2d(uint group, Workplane wrkpl, double u, double v)
+ {
+ return new Point2d(this, group, wrkpl, u, v);
+ }
+
+ public Point2d NewPoint2d(uint group, Workplane wrkpl, Param u, Param v)
+ {
+ return new Point2d(this, group, wrkpl, u, v);
+ }
+
+ public Point3d NewPoint3d(uint group, double x, double y, double z)
+ {
+ return new Point3d(this, group, x, y, z);
+ }
+
+ public Point3d NewPoint3d(uint group, Param x, Param y, Param z)
+ {
+ return new Point3d(this, group, x, y, z);
+ }
+
+ public Normal3d NewNormal3d(uint group, double ux, double uy, double uz, double vx, double vy, double vz)
+ {
+ return new Normal3d(this, group, ux, uy, uz, vx, vy, vz);
+ }
+
+ public Normal3d NewNormal3d(uint group, Param qw, Param qx, Param qy, Param qz)
+ {
+ return new Normal3d(this, group, qw, qx, qy, qz);
+ }
+
+ public Normal2d NewNormal2d(uint group, Workplane wrkpl)
+ {
+ return new Normal2d(this, group, wrkpl);
+ }
+
+ public Distance NewDistance(uint group, Workplane wrkpl, double d)
+ {
+ return new Distance(this, group, wrkpl, d);
+ }
+
+ public Distance NewDistance(uint group, Workplane wrkpl, Param d)
+ {
+ return new Distance(this, group, wrkpl, d);
+ }
+
+ public LineSegment NewLineSegment(uint group, Workplane wrkpl, Point ptA, Point ptB)
+ {
+ return new LineSegment(this, group, wrkpl, ptA, ptB);
+ }
+
+ public ArcOfCircle NewArcOfCircle(uint group, Workplane wrkpl, Normal normal, Point center, Point pstart, Point pend)
+ {
+ return new ArcOfCircle(this, group, wrkpl, normal, center, pstart, pend);
+ }
+
+ public Circle NewCircle(uint group, Workplane wrkpl, Point center, Normal normal, Distance radius)
+ {
+ return new Circle(this, group, wrkpl, center, normal, radius);
+ }
+
+ public Workplane NewWorkplane(uint group, Point origin, Normal normal)
+ {
+ return new Workplane(this, group, origin, normal);
+ }
+
+ public void AddConstraint(uint h, uint group, int type, Workplane wrkpl, double valA, Point ptA, Point ptB, Entity entityA, Entity entityB)
+ {
+ AddConstraint(h, group, type, wrkpl is null ? 0 : wrkpl.H, valA, ptA is null ? 0 : ptA.H, ptB is null ? 0 : ptB.H, entityA is null ? 0 : entityA.H, entityB is null ? 0 : entityB.H);
+ }
+
+
+ // The object-oriented wrapper classes themselves, to allow the
+ // representation of entities and constraints as .net objects, not
+ // integer handles. These don't do any work themselves, beyond
+ // allocating and storing a unique integer handle.
+ //
+ // These functions will assign parameters and entities with
+ // consecutive handles starting from 1. If they are intermixed
+ // with parameters and entities with user-specified handles, then
+ // those handles must be chosen not to conflict, e.g. starting
+ // from 100 000 or another large number.
+
+ public class Param
+ {
+ public Solver Solver;
+ public uint H;
+
+ public Param(Solver s, uint group, double val)
+ {
+ Solver = s;
+ H = (uint)(Solver.Params.Count + 1);
+ Solver.AddParam(H, group, val);
+ }
+ }
+
+ public abstract class Entity
+ {
+ public Solver Solver;
+ public uint H;
+
+ public Entity(Solver s)
+ {
+ Solver = s;
+ H = (uint)(Solver.Entities.Count + 1);
+ }
+ }
+
+ public abstract class Point : Entity
+ {
+ public Point(Solver s) : base(s)
+ {
+ }
+ }
+
+ public abstract class Normal : Entity
+ {
+ public Normal(Solver s) : base(s)
+ {
+ }
+ }
+
+ public class Point2d : Point
+ {
+ public Param up, vp;
+
+ public Point2d(Solver s, uint group, Workplane wrkpl, double u, double v) : base(s)
+ {
+ up = new Param(Solver, group, u);
+ vp = new Param(Solver, group, v);
+ Solver.AddPoint2d(H, group, wrkpl.H, up.H, vp.H);
+ }
+
+ public Point2d(Solver s, uint group, Workplane wrkpl, Param u, Param v) : base(s)
+ {
+ Solver.AddPoint2d(H, group, wrkpl.H, u.H, v.H);
+ up = u;
+ vp = v;
+ }
+
+ public double GetU()
+ {
+ return Solver.GetParamByHandle(up.H);
+ }
+
+ public double GetV()
+ {
+ return Solver.GetParamByHandle(vp.H);
+ }
+ }
+
+ public class Point3d : Point
+ {
+ public Param xp, yp, zp;
+
+ public Point3d(Solver s, uint group, double x, double y, double z) : base(s)
+ {
+ xp = new Param(Solver, group, x);
+ yp = new Param(Solver, group, y);
+ zp = new Param(Solver, group, z);
+ Solver.AddPoint3d(H, group, xp.H, yp.H, zp.H);
+ }
+
+ public Point3d(Solver s, uint group, Param x, Param y, Param z) : base(s)
+ {
+ Solver.AddPoint3d(H, group, x.H, y.H, z.H);
+ xp = x;
+ yp = y;
+ zp = z;
+ }
+
+ public double GetX()
+ {
+ return Solver.GetParamByHandle(xp.H);
+ }
+
+ public double GetY()
+ {
+ return Solver.GetParamByHandle(yp.H);
+ }
+
+ public double GetZ()
+ {
+ return Solver.GetParamByHandle(zp.H);
+ }
+ }
+
+ public class Normal3d : Normal
+ {
+ private Param qwp, qxp, qyp, qzp;
+
+ public Normal3d(Solver s, uint group, double ux, double uy, double uz, double vx, double vy, double vz) : base(s)
+ {
+ double qw = 0, qx = 0, qy = 0, qz = 0;
+ Solver.MakeQuaternion(ux, uy, uz, vx, vy, vz, ref qw, ref qx, ref qy, ref qz);
+ qwp = new Param(Solver, group, qw);
+ qxp = new Param(Solver, group, qx);
+ qyp = new Param(Solver, group, qy);
+ qzp = new Param(Solver, group, qz);
+ Solver.AddNormal3d(H, group, qwp.H, qxp.H, qyp.H, qzp.H);
+ }
+
+ public Normal3d(Solver s, uint group, Param qw, Param qx, Param qy, Param qz) : base(s)
+ {
+ Solver.AddNormal3d(H, group, qw.H, qx.H, qy.H, qz.H);
+ qwp = qw;
+ qxp = qx;
+ qyp = qy;
+ qzp = qz;
+ }
+ }
+
+ public class Normal2d : Normal
+ {
+ public Normal2d(Solver s, uint group, Workplane wrkpl) : base(s)
+ {
+ Solver.AddNormal2d(H, group, wrkpl.H);
+ }
+ }
+
+ public class Distance : Entity
+ {
+ public Param dp;
+
+ public Distance(Solver s, uint group, Workplane wrkpl, double d) : base(s)
+ {
+ dp = new Param(Solver, group, d);
+ Solver.AddDistance(H, group, wrkpl.H, dp.H);
+ }
+
+ public Distance(Solver s, uint group, Workplane wrkpl, Param d) : base(s)
+ {
+ Solver.AddDistance(H, group, wrkpl.H, d.H);
+ dp = d;
+ }
+
+ public double GetDistance()
+ {
+ return Solver.GetParamByHandle(dp.H);
+ }
+ }
+
+ public class LineSegment : Entity
+ {
+ public LineSegment(Solver s, uint group, Workplane wrkpl, Point ptA, Point ptB) : base(s)
+ {
+ Solver.AddLineSegment(H, group, wrkpl.H, ptA.H, ptB.H);
+ }
+ }
+
+ public class Cubic : Entity
+ {
+ public Cubic(Solver s, uint group, Workplane wrkpl, Point pt0, Point pt1, Point pt2, Point pt3) : base(s)
+ {
+ Solver.AddCubic(H, group, wrkpl.H, pt0.H, pt1.H, pt2.H, pt3.H);
+ }
+ }
+
+ public class ArcOfCircle : Entity
+ {
+ public ArcOfCircle(Solver s, uint group, Workplane wrkpl, Normal normal, Point center, Point pstart, Point pend) : base(s)
+ {
+ Solver.AddArcOfCircle(H, group, wrkpl.H, normal.H, center.H, pstart.H, pend.H);
+ }
+ }
+
+ public class Circle : Entity
+ {
+ public Circle(Solver s, uint group, Workplane wrkpl, Point center, Normal normal, Distance radius) : base(s)
+ {
+ Solver.AddCircle(H, group, wrkpl.H, center.H, normal.H, radius.H);
+ }
+ }
+
+ public class Workplane : Entity
+ {
+ public Workplane(Solver s) : base(s)
+ {
+ H = SLVS_FREE_IN_3D;
+ }
+
+ public Workplane(Solver s, uint group, Point origin, Normal normal) : base(s)
+ {
+ Solver.AddWorkplane(H, group, origin.H, normal.H);
+ }
+ }
+}