diff --git a/Rhino.Templates/content/CSGrasshopper2/.template.config/dotnetcli.host.json b/Rhino.Templates/content/CSGrasshopper2/.template.config/dotnetcli.host.json
new file mode 100755
index 0000000..7301b0a
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/.template.config/dotnetcli.host.json
@@ -0,0 +1,52 @@
+{
+ "$schema": "http://json.schemastore.org/dotnetcli.host",
+ "symbolInfo": {
+ "IncludeSample": {
+ "longName": "include-sample",
+ "shortName": "sample"
+ },
+ "RhinoVersion": {
+ "longName": "version",
+ "shortName": "v"
+ },
+ "PlugInDisplayName": {
+ "longName": "plugin-display-name",
+ "shortName": "plugin-name"
+ },
+ "ComponentClassName": {
+ "longName": "component-class",
+ "shortName": "component"
+ },
+ "ComponentName": {
+ "longName": "component-name",
+ "shortName": "cname"
+ },
+ "ComponentInfo": {
+ "shortName": "info"
+ },
+ "ComponentChapter": {
+ "longName": "chapter",
+ "shortName": "ch"
+ },
+ "ComponentSection": {
+ "longName": "section",
+ "shortName": "sec"
+ },
+ "UseWpf": {
+ "longName": "include-wpf",
+ "shortName": "wpf"
+ },
+ "UseWinForms": {
+ "longName": "include-winforms",
+ "shortName": "wf"
+ },
+ "BuildYak": {
+ "longName": "build-yak",
+ "shortName": "yak"
+ },
+ "IncludeVSCode": {
+ "longName": "include-vscode-launch",
+ "shortName": "vscode"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/.template.config/ide.host.json b/Rhino.Templates/content/CSGrasshopper2/.template.config/ide.host.json
new file mode 100755
index 0000000..fb277c7
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/.template.config/ide.host.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "http://json.schemastore.org/vs-2017.3.host",
+ "unsupportedHosts": [
+ {
+ "id": "vs"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/.template.config/template.json b/Rhino.Templates/content/CSGrasshopper2/.template.config/template.json
new file mode 100755
index 0000000..9fa9008
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/.template.config/template.json
@@ -0,0 +1,159 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "McNeel",
+ "classifications": [
+ "Rhino",
+ "Grasshopper2",
+ "GH2"
+ ],
+ "name": "Grasshopper2 PlugIn (WIP)",
+ "description": "Build Grasshopper2 components for Rhino 3D in C#",
+ "identity": "Grasshopper2.Component.CS",
+ "groupIdentity": "Grasshopper2.PlugIn",
+ "precedence": 100,
+ "defaultName": "MyGrasshopper2Components",
+ "shortName": "gh2",
+ "tags": {
+ "language": "C#",
+ "type": "project"
+ },
+ "sourceName": "MyGrasshopper.1",
+ "preferNameDirectory": true,
+ "symbols": {
+ "IncludeSample": {
+ "type": "parameter",
+ "description": "Include code sample.",
+ "dataType": "bool",
+ "defaultValue": "false"
+ },
+ "RhinoVersion": {
+ "type": "parameter",
+ "description": "Version of Rhino",
+ "datatype": "choice",
+ "defaultValue": "8",
+ "choices": [
+ {
+ "choice": "8",
+ "description": "Version 8"
+ }
+ ]
+ },
+ "BuildYak": {
+ "type": "parameter",
+ "description": "Include target to build yak package(s) for your plugin",
+ "datatype": "bool",
+ "defaultValue": "false"
+ },
+ "IncludeVSCode": {
+ "type": "parameter",
+ "description": "Include tasks.json and launch.json to build/debug in VS Code",
+ "datatype": "bool",
+ "defaultValue": "true"
+ },
+ "PlugInDisplayName": {
+ "type": "parameter",
+ "description": "Display name of your Grasshopper2 plug-in",
+ "datatype": "text",
+ "replaces": "MyGrasshopper.1 Info"
+ },
+ "ComponentClassName": {
+ "type": "parameter",
+ "description": "Name of the component class",
+ "datatype": "text",
+ "replaces": "MyGrasshopper__1Component",
+ "fileRename": "MyGrasshopper__1Component"
+ },
+ "ComponentName": {
+ "type": "parameter",
+ "description": "Display name of the component",
+ "datatype": "text",
+ "replaces": "MyGrasshopper.1 Component"
+ },
+ "ComponentInfo": {
+ "type": "parameter",
+ "description": "Description of the component",
+ "replaces": "ComponentInfo",
+ "defaultValue": "Description of component"
+ },
+ "ComponentChapter": {
+ "type": "parameter",
+ "description": "Chapter of the component",
+ "replaces": "ComponentChapter",
+ "defaultValue": "Chapter"
+ },
+ "ComponentSection": {
+ "type": "parameter",
+ "description": "Section of the component",
+ "replaces": "ComponentSection",
+ "defaultValue": "Section"
+ },
+
+ "UseWpf": {
+ "type": "parameter",
+ "description": "Enable to use WPF (Windows only)",
+ "datatype": "bool",
+ "defaultValue": "false"
+ },
+ "UseWinForms": {
+ "type": "parameter",
+ "description": "Enable the use of Windows Forms",
+ "datatype": "bool",
+ "defaultValue": "false"
+ },
+ "UseWindowsDesktop": {
+ "type": "computed",
+ "value": "UseWpf || UseWinForms"
+ },
+
+ "HostIdentifier": {
+ "type": "bind",
+ "binding": "HostIdentifier"
+ }
+ },
+ "forms": {
+ "JsonStringEncode": {
+ "identifier": "replace",
+ "pattern": "\\\\",
+ "replacement": "\\\\"
+ }
+ },
+ "sources": [
+ {
+ "modifiers": [
+ {
+ "condition": "!IncludeVSCode",
+ "exclude": [ ".vscode/*.*" ]
+ },
+ {
+ "condition": "ComponentClassName == 'MyGrasshopper__1Component'",
+ "rename": {
+ "MyGrasshopper__1Component.cs": "MyGrasshopper.1Component.cs",
+ "MyGrasshopper__1Component.3dm": "MyGrasshopper.1Component.3dm",
+ "MyGrasshopper__1Component.ghicon": "MyGrasshopper.1Component.ghicon"
+ }
+ }
+ ]
+ }
+ ],
+ "primaryOutputs": [
+ { "path": "MyGrasshopper.1.csproj" },
+ { "path": "MyGrasshopper__1Component.cs" }
+ ],
+ "guids": [
+ "ee4e2b39-d96b-4e4c-8f9d-9b6561e61b64",
+ "e79cd2b5-cb9c-4d08-93ec-446cc1f6d923",
+ "cd826b9b-8dbe-4c31-aac1-6fc7ea2bcfb7"
+ ],
+ "postActions": [
+ {
+ "condition": "(HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")",
+ "description": "Opens the command in the editor",
+ "manualInstructions": [],
+ "actionId": "84C0DA21-51C8-4541-9940-6CA19AF04EE6",
+ "args": {
+ "files": "1"
+ },
+ "continueOnError": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/.vscode/launch.json b/Rhino.Templates/content/CSGrasshopper2/.vscode/launch.json
new file mode 100644
index 0000000..6d3f097
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/.vscode/launch.json
@@ -0,0 +1,47 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Rhino 8 - netcore",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "",
+ "osx": {
+ "program": "/Applications/Rhino 8.app/Contents/MacOS/Rhinoceros",
+ "args": [
+ "-runscript=_G2"
+ ]
+ },
+ "windows": {
+ "program": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
+ "targetArchitecture": "x86_64",
+ "args": "/netcore /runscript=\"_G2\""
+ },
+ "env": {
+ "RHINO_PACKAGE_DIRS": "${workspaceFolder}/bin/Debug"
+ },
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Rhino 8 Windows - netfx",
+ "type": "clr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "windows": {
+ "program": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
+ "targetArchitecture": "x86_64",
+ "args": "/netfx /runscript=\"_G2\""
+ },
+ "env": {
+ "RHINO_PACKAGE_DIRS": "${workspaceFolder}/bin/Debug"
+ },
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ ],
+ "compounds": []
+}
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/.vscode/tasks.json b/Rhino.Templates/content/CSGrasshopper2/.vscode/tasks.json
new file mode 100644
index 0000000..20c5e62
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/.vscode/tasks.json
@@ -0,0 +1,21 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "shell",
+ "args": [
+ "build",
+ "-clp:NoSummary",
+ "${workspaceFolder}/MyGrasshopper.1.csproj"
+ ],
+ "problemMatcher": "$msCompile",
+ "presentation": {
+ "reveal": "always",
+ "clear": true
+ },
+ "group": "build"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper.1Plugin.3dm b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper.1Plugin.3dm
new file mode 100644
index 0000000..114cfcf
Binary files /dev/null and b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper.1Plugin.3dm differ
diff --git a/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper.1Plugin.ghicon b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper.1Plugin.ghicon
new file mode 100644
index 0000000..26fcfab
Binary files /dev/null and b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper.1Plugin.ghicon differ
diff --git a/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper__1Component.3dm b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper__1Component.3dm
new file mode 100644
index 0000000..cf0f299
Binary files /dev/null and b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper__1Component.3dm differ
diff --git a/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper__1Component.ghicon b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper__1Component.ghicon
new file mode 100644
index 0000000..e3f0e3c
Binary files /dev/null and b/Rhino.Templates/content/CSGrasshopper2/Icons/MyGrasshopper__1Component.ghicon differ
diff --git a/Rhino.Templates/content/CSGrasshopper2/Icons/plugin.ico b/Rhino.Templates/content/CSGrasshopper2/Icons/plugin.ico
new file mode 100644
index 0000000..022d1f7
Binary files /dev/null and b/Rhino.Templates/content/CSGrasshopper2/Icons/plugin.ico differ
diff --git a/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1.csproj b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1.csproj
new file mode 100755
index 0000000..f0bfde0
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1.csproj
@@ -0,0 +1,89 @@
+
+
+
+
+
+ net7.0-windows;net48
+
+ net7.0-windows;net7.0;net48
+
+ net7.0;net48
+
+ true
+ .rhp
+
+ NU1701;NETSDK1086
+
+ NU1701
+
+ true
+
+
+
+
+ 1.0
+ MyGrasshopper.1
+ MyGrasshopper.1 Authors
+ Description of MyGrasshopper.1
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Program Files\Rhino 8\System\Yak.exe
+ /Applications/Rhino 8.app/Contents/Resources/bin/yak
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1Plugin.cs b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1Plugin.cs
new file mode 100644
index 0000000..f54028a
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1Plugin.cs
@@ -0,0 +1,28 @@
+using System;
+using Rhino;
+
+namespace MyGrasshopper._1
+{
+ ///
+ /// Every RhinoCommon .rhp assembly must have one and only one PlugIn-derived
+ /// class. DO NOT create instances of this class yourself. It is the
+ /// responsibility of Rhino to create an instance of this class.
+ /// To complete plug-in information, please also see all PlugInDescription
+ /// attributes in AssemblyInfo.cs (you might need to click "Project" ->
+ /// "Show All Files" to see it in the "Solution Explorer" window).
+ ///
+ public class MyGrasshopper__1Plugin : Rhino.PlugIns.PlugIn
+ {
+ public MyGrasshopper__1Plugin()
+ {
+ Instance = this;
+ }
+
+ ///Gets the only instance of the MyRhino__1Plugin plug-in.
+ public static MyGrasshopper__1Plugin Instance { get; private set; }
+
+ // You can override methods here to change the plug-in behavior on
+ // loading and shut down, add options pages to the Rhino _Option command
+ // and maintain plug-in wide options in a document.
+ }
+}
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1PluginInfo.cs b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1PluginInfo.cs
new file mode 100644
index 0000000..0d53186
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper.1PluginInfo.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Reflection;
+using Grasshopper.UI;
+using Grasshopper.UI.Icon;
+
+namespace MyGrasshopper._1
+{
+ public sealed class MyGrasshopper__1PluginInfo : Grasshopper.Framework.Plugin
+ {
+ static T GetAttribute() where T : Attribute => typeof(MyGrasshopper__1PluginInfo).Assembly.GetCustomAttribute();
+
+ public MyGrasshopper__1PluginInfo()
+ : base(new Guid("cd826b9b-8dbe-4c31-aac1-6fc7ea2bcfb7"),
+ new Nomen(
+ GetAttribute()?.Title,
+ GetAttribute()?.Description),
+ typeof(MyGrasshopper__1PluginInfo).Assembly.GetName().Version)
+ {
+ Icon = AbstractIcon.FromResource("MyGrasshopper.1Plugin", typeof(MyGrasshopper__1PluginInfo));
+ }
+
+ public override string Author => GetAttribute()?.Company;
+
+ public override sealed IIcon Icon { get; }
+
+ public override sealed string Copyright => GetAttribute()?.Copyright ?? base.Copyright;
+
+ // public override sealed string Website => "https://mywebsite.example.com";
+
+ // public override sealed string Contact => "myemail@example.com";
+
+ // public override sealed string LicenceAgreement => "license or URL";
+
+ }
+}
\ No newline at end of file
diff --git a/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper__1Component.cs b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper__1Component.cs
new file mode 100644
index 0000000..b98d9b9
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/MyGrasshopper__1Component.cs
@@ -0,0 +1,134 @@
+using System;
+using Rhino.Geometry;
+using GrasshopperIO;
+using Grasshopper.UI;
+using Grasshopper.Components;
+
+namespace MyGrasshopper._1
+{
+ [IoId("e79cd2b5-cb9c-4d08-93ec-446cc1f6d923")]
+ public sealed class MyGrasshopper__1Component : Component
+ {
+ public MyGrasshopper__1Component() : base(new Nomen(
+ "MyGrasshopper.1 Component",
+ "ComponentInfo",
+ "ComponentChapter",
+ "ComponentSection"))
+ {
+
+ }
+
+ public MyGrasshopper__1Component(IReader reader) : base(reader) { }
+
+#if IncludeSample
+ ///
+ /// Registers all the input parameters for this component.
+ ///
+ protected override void AddInputs(InputAdder inputs)
+ {
+ inputs.AddPlane("Plane", "P", "Base plane for spiral").Set(Plane.WorldXY);
+ inputs.AddNumber("Inner Radius", "R0", "Inner radius for spiral").Set(1.0);
+ inputs.AddNumber("Outer Radius", "R1", "Outer radius for spiral").Set(10.0);
+ inputs.AddInteger("Turns", "T", "Number of turns between radii").Set(10);
+ }
+
+ ///
+ /// Registers all the output parameters for this component.
+ ///
+ protected override void AddOutputs(OutputAdder outputs)
+ {
+ outputs.AddCurve("Spiral", "S", "Spiral curve");
+ }
+
+ ///
+ /// This is the method that actually does the work.
+ ///
+ /// The IDataAccess object can be used to retrieve data from input parameters and
+ /// to store data in output parameters.
+ protected override void Process(IDataAccess access)
+ {
+ // First, we need to retrieve all data from the input parameters.
+ // When data cannot be extracted from a parameter, we should abort this method.
+ if (!access.GetItem(0, out Plane plane)) return;
+ if (!access.GetItem(1, out double radius0)) return;
+ if (!access.GetItem(2, out double radius1)) return;
+ if (!access.GetItem(3, out int turns)) return;
+
+ // We should now validate the data and warn the user if invalid data is supplied.
+ if (radius0 < 0.0)
+ {
+ access.AddError("Inner radius must be bigger than or equal to zero", "");
+ return;
+ }
+ if (radius1 <= radius0)
+ {
+ access.AddError("Outer radius must be bigger than the inner radius", "");
+ return;
+ }
+ if (turns <= 0)
+ {
+ access.AddError("Spiral turn count must be bigger than or equal to one", "");
+ return;
+ }
+
+ // We're set to create the spiral now. To keep the size of the SolveInstance() method small,
+ // The actual functionality will be in a different method:
+ Curve spiral = CreateSpiral(plane, radius0, radius1, turns);
+
+ // Finally assign the spiral to the output parameter.
+ access.SetItem(0, spiral);
+ }
+
+ Curve CreateSpiral(Plane plane, double r0, double r1, Int32 turns)
+ {
+ Line l0 = new Line(plane.Origin + r0 * plane.XAxis, plane.Origin + r1 * plane.XAxis);
+ Line l1 = new Line(plane.Origin - r0 * plane.XAxis, plane.Origin - r1 * plane.XAxis);
+
+ Point3d[] p0;
+ Point3d[] p1;
+
+ l0.ToNurbsCurve().DivideByCount(turns, true, out p0);
+ l1.ToNurbsCurve().DivideByCount(turns, true, out p1);
+
+ PolyCurve spiral = new PolyCurve();
+
+ for (int i = 0; i < p0.Length - 1; i++)
+ {
+ Arc arc0 = new Arc(p0[i], plane.YAxis, p1[i + 1]);
+ Arc arc1 = new Arc(p1[i + 1], -plane.YAxis, p0[i + 1]);
+
+ spiral.Append(arc0);
+ spiral.Append(arc1);
+ }
+
+ return spiral;
+ }
+#else
+ ///
+ /// Registers all the input parameters for this component.
+ ///
+ protected override void AddInputs(InputAdder inputs)
+ {
+
+ }
+
+ ///
+ /// Registers all the output parameters for this component.
+ ///
+ protected override void AddOutputs(OutputAdder outputs)
+ {
+
+ }
+
+ ///
+ /// This is the method that actually does the work.
+ ///
+ /// The IDataAccess object can be used to retrieve data from input parameters and
+ /// to store data in output parameters.
+ protected override void Process(IDataAccess access)
+ {
+
+ }
+#endif
+ }
+}
diff --git a/Rhino.Templates/content/CSGrasshopper2/Properties/AssemblyInfo.cs b/Rhino.Templates/content/CSGrasshopper2/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..e76b049
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Rhino.PlugIns;
+
+// Plug-in Description Attributes - all of these are optional.
+// These will show in Rhino's option dialog, in the tab Plug-ins.
+[assembly: PlugInDescription(DescriptionType.Address, "")]
+[assembly: PlugInDescription(DescriptionType.Country, "")]
+[assembly: PlugInDescription(DescriptionType.Email, "")]
+[assembly: PlugInDescription(DescriptionType.Phone, "")]
+[assembly: PlugInDescription(DescriptionType.Fax, "")]
+[assembly: PlugInDescription(DescriptionType.Organization, "")]
+[assembly: PlugInDescription(DescriptionType.UpdateUrl, "")]
+[assembly: PlugInDescription(DescriptionType.WebSite, "")]
+
+// Icons should be Windows .ico files and contain 32-bit images in the following sizes: 16, 24, 32, 48, and 256.
+[assembly: PlugInDescription(DescriptionType.Icon, "MyGrasshopper._1.Icons.plugin.ico")]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+// This will also be the Guid of the Rhino plug-in
+[assembly: Guid("ee4e2b39-d96b-4e4c-8f9d-9b6561e61b64")]
diff --git a/Rhino.Templates/content/CSGrasshopper2/Properties/launchSettings.json b/Rhino.Templates/content/CSGrasshopper2/Properties/launchSettings.json
new file mode 100644
index 0000000..e30f217
--- /dev/null
+++ b/Rhino.Templates/content/CSGrasshopper2/Properties/launchSettings.json
@@ -0,0 +1,20 @@
+{
+ "profiles": {
+ "Rhino 8 - netcore": {
+ "commandName": "Executable",
+ "executablePath": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
+ "commandLineArgs": "/netcore /runscript=\"_G2\"",
+ "environmentVariables": {
+ "RHINO_PACKAGE_DIRS": "$(ProjectDir)$(OutputPath)\\"
+ }
+ },
+ "Rhino 8 - netfx": {
+ "commandName": "Executable",
+ "executablePath": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
+ "commandLineArgs": "/netfx /runscript=\"_G2\"",
+ "environmentVariables": {
+ "RHINO_PACKAGE_DIRS": "$(ProjectDir)$(OutputPath)\\"
+ }
+ },
+ }
+}
\ No newline at end of file
diff --git a/Rhino.VisualStudio.Windows/Rhino.VisualStudio.Windows.csproj b/Rhino.VisualStudio.Windows/Rhino.VisualStudio.Windows.csproj
index 4f0465f..5fb0500 100755
--- a/Rhino.VisualStudio.Windows/Rhino.VisualStudio.Windows.csproj
+++ b/Rhino.VisualStudio.Windows/Rhino.VisualStudio.Windows.csproj
@@ -94,6 +94,8 @@
+
+
@@ -214,6 +216,7 @@
+
diff --git a/Rhino.VisualStudio.Windows/Templates/CSGrasshopper2/grasshopper2.vstemplate b/Rhino.VisualStudio.Windows/Templates/CSGrasshopper2/grasshopper2.vstemplate
new file mode 100755
index 0000000..f9d7d5f
--- /dev/null
+++ b/Rhino.VisualStudio.Windows/Templates/CSGrasshopper2/grasshopper2.vstemplate
@@ -0,0 +1,43 @@
+
+
+
+ Grasshopper2 Plug-In for Rhino 3D (C#)
+ Build Grasshopper2 components for Rhino 3D in C#
+ 1
+
+ 20
+
+ Grasshopper2.Component.CS
+ CSharp
+ Windows
+ macOS
+ Desktop
+ Rhino
+ CSharp
+ icon.ico
+ MyGrasshopper2PlugIn
+
+ true
+ true
+ Enabled
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ Rhino.VisualStudio.Windows, Version=8.15.0.0, Culture=neutral, PublicKeyToken=null
+ Rhino.VisualStudio.Windows.Wizard.Grasshopper2Wizard
+
+
+ Microsoft.VisualStudio.TemplateEngine.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+ Microsoft.VisualStudio.TemplateEngine.Wizard.TemplateEngineWizard
+
+
\ No newline at end of file
diff --git a/Rhino.VisualStudio.Windows/Templates/CSGrasshopper2/icon.ico b/Rhino.VisualStudio.Windows/Templates/CSGrasshopper2/icon.ico
new file mode 100755
index 0000000..dc15579
Binary files /dev/null and b/Rhino.VisualStudio.Windows/Templates/CSGrasshopper2/icon.ico differ
diff --git a/Rhino.VisualStudio.Windows/Wizard/Grasshopper2Wizard.cs b/Rhino.VisualStudio.Windows/Wizard/Grasshopper2Wizard.cs
new file mode 100755
index 0000000..b4b9046
--- /dev/null
+++ b/Rhino.VisualStudio.Windows/Wizard/Grasshopper2Wizard.cs
@@ -0,0 +1,11 @@
+using Eto.Forms;
+
+namespace Rhino.VisualStudio.Windows.Wizard
+{
+ public class Grasshopper2Wizard : EtoWizard
+ {
+ protected override Control CreatePanel() => new Grasshopper2OptionsPanel(false);
+
+ protected override BaseWizardViewModel CreateViewModel() => new Grasshopper2OptionsViewModel();
+ }
+}
\ No newline at end of file
diff --git a/Rhino.VisualStudio/Grasshopper2OptionsPanel.cs b/Rhino.VisualStudio/Grasshopper2OptionsPanel.cs
new file mode 100755
index 0000000..bdb2bdb
--- /dev/null
+++ b/Rhino.VisualStudio/Grasshopper2OptionsPanel.cs
@@ -0,0 +1,110 @@
+using Eto.Forms;
+using Eto.Drawing;
+using System;
+using Rhino.VisualStudio.Controls;
+
+namespace Rhino.VisualStudio
+{
+ public class Grasshopper2OptionsPanel : BaseRhinoPageView
+ {
+ public Grasshopper2OptionsPanel(bool showProjectName)
+ {
+ var spacing = new Size(6, 6);
+ var padding = new Padding(8);
+
+ var projectNameTextBox = new TextBox();
+ projectNameTextBox.TextBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.ProjectName);
+
+ var nameValid = new Label { TextColor = Global.Theme.ErrorForeground };
+ nameValid.BindDataContext(c => c.Visible, (Grasshopper2OptionsViewModel m) => m.IsProjectNameInvalid);
+ nameValid.BindDataContext(c => c.Text, (Grasshopper2OptionsViewModel m) => m.ProjectNameValidationText);
+
+ var pluginDisplayNameTextBox = new TextBox();
+ pluginDisplayNameTextBox.TextBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.PlugInDisplayName);
+
+ var componentClassNameTextBox = new TextBox();
+ componentClassNameTextBox.TextBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.ComponentClassName);
+
+ var provideCommandSampleCheckBox = new CheckBox { Text = "Provide sample code", ToolTip = "Check to provide a sample implementation for the component" };
+ provideCommandSampleCheckBox.CheckedBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.IncludeSample);
+
+
+ var componentNameTextBox = new TextBox();
+ componentNameTextBox.TextBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.ComponentName);
+
+ var componentChapterTextBox = new TextBox();
+ componentChapterTextBox.TextBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.ComponentChapter);
+
+ var componentSectionTextBox = new TextBox();
+ componentSectionTextBox.TextBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.ComponentSection);
+
+ var componentInfoTextBox = new TextBox();
+ componentInfoTextBox.TextBinding.BindDataContext((Grasshopper2OptionsViewModel m) => m.ComponentInfo);
+
+ // rhino location
+ var rhinoLocation = new FilePicker();
+ rhinoLocation.Filters.Add(new FileFilter("Rhino.exe", "Rhino.exe"));
+ rhinoLocation.BindDataContext(c => c.FilePath, (BaseLocationWizardViewModel m) => m.ExecutableLocation);
+
+ var rhinoLocationInvalid = new Label { Text = "Could not find Rhino.exe at the specified location.", TextColor = Global.Theme.ErrorForeground };
+ rhinoLocationInvalid.BindDataContext(c => c.Visible, (BaseLocationWizardViewModel m) => m.IsLocationInvalid);
+
+
+ // styles
+ Styles.Add(null, g => g.Padding = padding);
+
+ // layout
+ var layout = new DynamicLayout { DefaultSpacing = spacing, Padding = padding };
+
+ layout.AddCentered("Adds the first component that has an icon to the toolbar.");
+ // top
+ layout.BeginVertical();
+ if (showProjectName)
+ {
+ layout.AddRow("Project name", new TableLayout(projectNameTextBox, nameValid));
+ }
+ layout.AddRow("Plug-In display name", pluginDisplayNameTextBox);
+ layout.AddRow("Component class name", componentClassNameTextBox);
+ layout.EndVertical();
+
+ // type group
+ layout.BeginVertical();
+ layout.Add(new PanelSeparator("First component"));;
+ layout.AddRow("Name", componentNameTextBox);
+ layout.BeginHorizontal();
+ layout.Add("Chapter");
+ layout.BeginVertical();
+ layout.BeginHorizontal();
+ layout.Add(componentChapterTextBox, xscale: true);
+ layout.Add("Section");
+ layout.Add(componentSectionTextBox, xscale: true);
+ layout.EndHorizontal();
+ layout.EndVertical();
+ layout.AddRow("Description", componentInfoTextBox);
+
+ layout.EndVertical();
+
+ layout.BeginVertical();
+ layout.Add(new PanelSeparator("Options"));
+ AddRhinoVersion(layout);
+ AddBuildYakPackage(layout);
+ AddIncludeVSCode(layout);
+ layout.Add(provideCommandSampleCheckBox);
+ layout.EndVertical();
+
+ AddWindowsUI(layout);
+
+ // AddRhinoLocation(layout);
+
+ Content = layout;
+
+ var information = new DynamicLayout();
+ information.AddSpace();
+ AddRhinoDownloadInfo(information);
+ information.AddSpace();
+
+ Information = information;
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rhino.VisualStudio/Grasshopper2OptionsViewModel.cs b/Rhino.VisualStudio/Grasshopper2OptionsViewModel.cs
new file mode 100755
index 0000000..9fb503a
--- /dev/null
+++ b/Rhino.VisualStudio/Grasshopper2OptionsViewModel.cs
@@ -0,0 +1,136 @@
+using Eto.Forms;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Rhino.VisualStudio
+{
+ public class Grasshopper2OptionsViewModel : BaseDesktopWizardViewModel
+ {
+ string _componentClassName;
+
+ public override string ProjectTitle => "New Grasshopper2 Plug-In";
+
+ public string ComponentClassName
+ {
+ get => _componentClassName ?? Utility.GetSafeName(ProjectName, "Component", "Component", "Components", "Plugin", "Addin");
+ set
+ {
+ if (Set(ref _componentClassName, value))
+ {
+ OnPropertyChanged(nameof(IsValid));
+ }
+ }
+ }
+
+ string _componentName;
+ public string ComponentName
+ {
+ get => _componentName ?? ComponentClassName;
+ set => Set(ref _componentName, value);
+ }
+
+ string _componentChapter;
+ public string ComponentChapter
+ {
+ get => _componentChapter;
+ set => Set(ref _componentChapter, value);
+ }
+
+ string _componentSection;
+ public string ComponentSection
+ {
+ get => _componentSection;
+ set => Set(ref _componentSection, value);
+ }
+
+ string _componentInfo;
+ public string ComponentInfo
+ {
+ get => _componentInfo;
+ set => Set(ref _componentInfo, value);
+ }
+
+ string _projectName;
+ public override string ProjectName
+ {
+ get => _projectName ?? string.Empty;
+ set
+ {
+ if (Set(ref _projectName, value))
+ {
+ OnPropertyChanged(nameof(PlugInDisplayName));
+ OnPropertyChanged(nameof(ComponentClassName));
+ OnPropertyChanged(nameof(ComponentName));
+ OnPropertyChanged(nameof(IsValid));
+ OnPropertyChanged(nameof(IsProjectNameInvalid));
+ }
+ }
+ }
+
+ string _plugInDisplayName;
+ public string PlugInDisplayName
+ {
+ get => _plugInDisplayName ?? ProjectName;
+ set
+ {
+ if (Set(ref _plugInDisplayName, value))
+ {
+ OnPropertyChanged(nameof(IsValid));
+ }
+ }
+ }
+
+ bool _includeSample;
+ public bool IncludeSample
+ {
+ get => _includeSample;
+ set
+ {
+ if (Set(ref _includeSample, value))
+ {
+ if (value)
+ {
+ ComponentChapter = "Curve";
+ ComponentSection = "Primitive";
+ ComponentInfo = "Construct an Archimedean, or arithmetic, spiral given its radii and number of turns.";
+ }
+ }
+ }
+ }
+
+ protected override string FindLocation(int version) => Global.Helpers.FindRhino(version);
+
+ public Grasshopper2OptionsViewModel()
+ {
+ SetDefaults();
+ }
+
+ public override bool IsValid =>
+ !IsProjectNameInvalid
+ && Utility.IsValidIdentifier(ComponentClassName);
+
+ void SetDefaults()
+ {
+ ComponentChapter = "Chapter";
+ ComponentSection = "Section";
+
+ ComponentInfo = "Description";
+ }
+
+ public override void Finish()
+ {
+ base.Finish();
+ if (Host == null)
+ return;
+
+ Host.SetParameter("ComponentClassName", ComponentClassName);
+ Host.SetParameter("AddonDisplayName", PlugInDisplayName);
+ Host.SetParameter("IncludeSample", IncludeSample.ToString());
+ Host.SetParameter("ComponentName", ComponentName);
+ Host.SetParameter("ComponentChapter", ComponentChapter);
+ Host.SetParameter("ComponentSsection", ComponentSection);
+ Host.SetParameter("ComponentInfo", ComponentInfo);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/build/GenerateTemplates.proj b/build/GenerateTemplates.proj
index ac66868..ba208bb 100644
--- a/build/GenerateTemplates.proj
+++ b/build/GenerateTemplates.proj
@@ -69,7 +69,7 @@
Condition="!Exists($(MyTextFile))" />
-
+
@@ -200,6 +200,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+