diff --git a/Components/ModuleConstruct.cs b/Components/ModuleConstruct.cs
index a293018..a463a9c 100644
--- a/Components/ModuleConstruct.cs
+++ b/Components/ModuleConstruct.cs
@@ -175,7 +175,7 @@ protected override void SolveInstance(IGH_DataAccess DA) {
return;
}
- if (name.Contains("\n") || name.Contains(":") || name.Contains("=")) {
+ if (Config.RESERVED_CHARS.Any(chars => name.Contains(chars))) {
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Monoceros Module name contains " +
"a forbidden content: :, ->, = or newline.");
return;
diff --git a/Components/RuleOuterContruct.cs b/Components/RuleAtBoundaryContruct.cs
similarity index 99%
rename from Components/RuleOuterContruct.cs
rename to Components/RuleAtBoundaryContruct.cs
index 83908f4..f7e02d2 100644
--- a/Components/RuleOuterContruct.cs
+++ b/Components/RuleAtBoundaryContruct.cs
@@ -132,7 +132,7 @@ private uint DirectionToSingleModuleConnectorIndex(Direction direction) {
/// Provides an Icon for every component that will be visible in the
/// User Interface. Icons need to be 24x24 pixels.
///
- protected override System.Drawing.Bitmap Icon => Properties.Resources.rule_out;
+ protected override System.Drawing.Bitmap Icon => Properties.Resources.rule_out_construct;
///
/// Each component must have a unique Guid to identify it. It is vital
diff --git a/Components/RuleOuterFromPoint.cs b/Components/RuleAtBoundaryFromPoint.cs
similarity index 100%
rename from Components/RuleOuterFromPoint.cs
rename to Components/RuleAtBoundaryFromPoint.cs
diff --git a/Components/RuleExplicitConstruct.cs b/Components/RuleExplicitConstruct.cs
index e971039..7b71a35 100644
--- a/Components/RuleExplicitConstruct.cs
+++ b/Components/RuleExplicitConstruct.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Grasshopper.Kernel;
namespace Monoceros {
@@ -83,12 +84,8 @@ protected override void SolveInstance(IGH_DataAccess DA) {
var sourceName = sourceNameRaw.Name;
var targetName = targetNameRaw.Name;
- if (sourceName.Contains("\n")
- || sourceName.Contains(":")
- || sourceName.Contains("=")
- || targetName.Contains("\n")
- || targetName.Contains(":")
- || targetName.Contains("=")) {
+ if (Config.RESERVED_CHARS.Any(chars => sourceName.Contains(chars))
+ || Config.RESERVED_CHARS.Any(chars => targetName.Contains(chars))) {
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input text contains " +
"a forbidden content: :, ->, = or newline.");
return;
diff --git a/Components/RuleFromSlots.cs b/Components/RuleFromSlots.cs
new file mode 100644
index 0000000..bd7fde9
--- /dev/null
+++ b/Components/RuleFromSlots.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using Grasshopper;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Data;
+using Rhino;
+using Rhino.DocObjects;
+using Rhino.Geometry;
+
+namespace Monoceros {
+ public class ComponentScanSlotsForRules : GH_Component {
+
+ public ComponentScanSlotsForRules( ) : base("Scan Slots For rules",
+ "RulesScan",
+ "Scan solved Slots and extract applied Rules.",
+ "Monoceros",
+ "Rule") {
+ }
+
+ ///
+ /// Registers all the input parameters for this component.
+ ///
+ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) {
+ pManager.AddParameter(new SlotParameter(),
+ "Slots",
+ "S",
+ "Monoceros Slots",
+ GH_ParamAccess.list);
+ pManager.AddParameter(new ModuleParameter(),
+ "Modules",
+ "M",
+ "All Monoceros Modules",
+ GH_ParamAccess.list);
+ }
+
+ ///
+ /// Registers all the output parameters for this component.
+ ///
+ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) {
+ pManager.AddParameter(new RuleParameter(),
+ "Rules Explicit",
+ "R",
+ "Explicit Monoceros Rules",
+ GH_ParamAccess.list);
+ }
+
+ ///
+ /// Wrap input geometry into module cages.
+ ///
+ /// The DA object can be used to retrieve data from
+ /// input parameters and to store data in output parameters.
+ protected override void SolveInstance(IGH_DataAccess DA) {
+ var modulesRaw = new List();
+ var slotsRaw = new List();
+
+ if (!DA.GetDataList(0, slotsRaw)) {
+ return;
+ }
+
+ if (!DA.GetDataList(1, modulesRaw)) {
+ return;
+ }
+
+ var slotsClean = new List();
+ foreach (var slot in slotsRaw) {
+ if (slot == null || !slot.IsValid) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slot is null or invalid and will be skipped.");
+ continue;
+ }
+ if (!slot.IsDeterministic) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Slot is not deterministic and will be skipped.");
+ continue;
+ }
+ slotsClean.Add(slot);
+ }
+
+ var modulesClean = new List();
+ foreach (var module in modulesRaw) {
+ if (module == null || !module.IsValid) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Module is null or invalid.");
+ continue;
+ }
+ if (module.Geometry.Count + module.ReferencedGeometry.Count == 0) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Module \"" + module.Name + "\" contains " +
+ "no geometry and therefore will be skipped.");
+ continue;
+ }
+
+ modulesClean.Add(module);
+ }
+
+ if (!modulesClean.Any()) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No valid Modules collected.");
+ return;
+ }
+
+ var slotsNonEmpty = slotsClean.Where(slot =>
+ modulesClean.Any(module => module.ContainsPart(slot.AllowedPartNames[0]))
+ );
+
+ if (!slotsNonEmpty.Any()) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No valid Slots collected.");
+ return;
+ }
+
+ var rulesOut = new List();
+ foreach (var slot in slotsNonEmpty) {
+ foreach (var slotOther in slotsNonEmpty) {
+ if (slot.RelativeCenter.IsNeighbor(slotOther.RelativeCenter)) {
+ var neighborVector = (slotOther.RelativeCenter - slot.RelativeCenter).ToVector3d();
+ if (Direction.FromVector(neighborVector, out var direction)
+ && direction.Orientation == Orientation.Positive) {
+ var currentPart = slot.AllowedPartNames[0];
+ var otherPart = slotOther.AllowedPartNames[0];
+ var ruleForSolver = new RuleForSolver(direction.Axis, currentPart, otherPart);
+ if (RuleExplicit.FromRuleForSolver(ruleForSolver, modulesClean, out var ruleExplicit)) {
+ var rule = new Rule(ruleExplicit);
+ if (!rulesOut.Contains(rule)) {
+ rulesOut.Add(rule);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ rulesOut.Sort();
+ DA.SetDataList(0, rulesOut);
+ }
+
+ ///
+ /// The Exposure property controls where in the panel a component icon
+ /// will appear. There are seven possible locations (primary to
+ /// septenary), each of which can be combined with the
+ /// GH_Exposure.obscure flag, which ensures the component will only be
+ /// visible on panel dropdowns.
+ ///
+ public override GH_Exposure Exposure => GH_Exposure.quinary;
+
+ ///
+ /// Provides an Icon for every component that will be visible in the
+ /// User Interface. Icons need to be 24x24 pixels.
+ ///
+ protected override Bitmap Icon => Properties.Resources.rules_from_slots;
+
+ ///
+ /// Each component must have a unique Guid to identify it. It is vital
+ /// this Guid doesn't change otherwise old ghx files that use the old ID
+ /// will partially fail during loading.
+ ///
+ public override Guid ComponentGuid => new Guid("14CD0308-26FC-4134-AB5A-C7B89B6405BF");
+ }
+}
diff --git a/Components/RuleIndifferentConstruct.cs b/Components/RuleIndifferentConstruct.cs
index 6bbb96c..0065429 100644
--- a/Components/RuleIndifferentConstruct.cs
+++ b/Components/RuleIndifferentConstruct.cs
@@ -78,7 +78,7 @@ protected override void SolveInstance(IGH_DataAccess DA) {
/// Provides an Icon for every component that will be visible in the
/// User Interface. Icons need to be 24x24 pixels.
///
- protected override System.Drawing.Bitmap Icon => Properties.Resources.rule_indifferent;
+ protected override System.Drawing.Bitmap Icon => Properties.Resources.rule_indifferent_construct;
///
/// Each component must have a unique Guid to identify it. It is vital
diff --git a/Components/RuleTypedConstruct.cs b/Components/RuleTypedConstruct.cs
index 61d7c5e..c15965a 100644
--- a/Components/RuleTypedConstruct.cs
+++ b/Components/RuleTypedConstruct.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Grasshopper.Kernel;
namespace Monoceros {
@@ -68,12 +69,8 @@ protected override void SolveInstance(IGH_DataAccess DA) {
var moduleName = moduleNameRaw.Name;
- if (moduleName.Contains("\n")
- || moduleName.Contains(":")
- || moduleName.Contains("=")
- || type.Contains("\n")
- || type.Contains(":")
- || type.Contains("=")) {
+ if (Config.RESERVED_CHARS.Any(chars => moduleName.Contains(chars))
+ || Config.RESERVED_CHARS.Any(chars => type.Contains(chars))) {
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input text contains " +
"a forbidden content: :, ->, = or newline.");
return;
diff --git a/Components/RuleTypedFromPoint.cs b/Components/RuleTypedFromPoint.cs
index f6bfaef..8e51787 100644
--- a/Components/RuleTypedFromPoint.cs
+++ b/Components/RuleTypedFromPoint.cs
@@ -68,9 +68,7 @@ protected override void SolveInstance(IGH_DataAccess DA) {
return;
}
- if (type.Contains("\n")
- || type.Contains(":")
- || type.Contains("=")) {
+ if (Config.RESERVED_CHARS.Any(chars => type.Contains(chars))) {
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input text contains " +
"a forbidden content: :, ->, = or newline.");
return;
diff --git a/Components/PopulateGeometryWithSlotCenters.cs b/Components/SliceGeometry.cs
similarity index 100%
rename from Components/PopulateGeometryWithSlotCenters.cs
rename to Components/SliceGeometry.cs
diff --git a/Components/SlotConstructWithModules.cs b/Components/SlotConstructWithModules.cs
index 91ccfc6..90260a4 100644
--- a/Components/SlotConstructWithModules.cs
+++ b/Components/SlotConstructWithModules.cs
@@ -91,11 +91,9 @@ protected override void SolveInstance(IGH_DataAccess DA) {
var allowedModules = allowedModulesRaw.Select(name => name.Name).Distinct().ToList();
- if (allowedModules.Any(name => name.Contains("\n")
- || name.Contains(":")
- || name.Contains("="))) {
+ if (Config.RESERVED_CHARS.Any(chars => allowedModules.Contains(chars))) {
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input text contains " +
- "a forbidden content: :, ->, = or newline.");
+ "a forbidden content: :, ->, = or newline.");
return;
}
diff --git a/Components/SlotDeconstruct.cs b/Components/SlotDeconstruct.cs
index 4b6bab0..36f2ef3 100644
--- a/Components/SlotDeconstruct.cs
+++ b/Components/SlotDeconstruct.cs
@@ -115,7 +115,7 @@ protected override void SolveInstance(IGH_DataAccess DA) {
} else {
DA.SetDataList(4, new List() { slot.AllowsAnyModule });
}
- DA.SetDataList(5, new List() { slot.AllowsNothing });
+ DA.SetDataList(5, new List() { slot.IsContradictory });
if (modulesProvided
&& moduleNames != null
&& (slot.AllowedModuleNames.Count > moduleNames.Count
diff --git a/Components/SlotsAddBoundary.cs b/Components/SlotsAddBoundary.cs
index 5e2c5c2..6cedb0a 100644
--- a/Components/SlotsAddBoundary.cs
+++ b/Components/SlotsAddBoundary.cs
@@ -150,7 +150,7 @@ protected override void SolveInstance(IGH_DataAccess DA) {
/// Provides an Icon for every component that will be visible in the
/// User Interface. Icons need to be 24x24 pixels.
///
- protected override Bitmap Icon => Properties.Resources.slot_add_boundary_2;
+ protected override Bitmap Icon => Properties.Resources.slot_add_boundary;
///
/// Each component must have a unique Guid to identify it. It is vital
diff --git a/Components/SlotsFromGeometry.cs b/Components/SlotsFromGeometry.cs
new file mode 100644
index 0000000..366a728
--- /dev/null
+++ b/Components/SlotsFromGeometry.cs
@@ -0,0 +1,261 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Types;
+using Rhino;
+using Rhino.Geometry;
+
+namespace Monoceros {
+ public class ComponentSlotsFromgeometry : GH_Component {
+ public ComponentSlotsFromgeometry( ) : base("Slots FromGeometry",
+ "SlotsFromGeometry",
+ "Identify Module geometry and construct Slots containing it. Ignores connection to boundary.",
+ "Monoceros", "Slot") {
+ }
+
+ ///
+ /// Registers all the input parameters for this component.
+ ///
+ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) {
+ pManager.AddGeometryParameter("Geometry",
+ "G",
+ "Geometry to scan and identify Modules",
+ GH_ParamAccess.list);
+ pManager.AddParameter(new ModuleParameter(),
+ "Module",
+ "M",
+ "Monoceros Module",
+ GH_ParamAccess.item);
+ pManager.AddPlaneParameter("Base Plane",
+ "B",
+ "Grid space base plane of the scanned geometry. Defines orientation of the grid.",
+ GH_ParamAccess.item,
+ Plane.WorldXY);
+ }
+
+ ///
+ /// Registers all the output parameters for this component.
+ ///
+ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) {
+ pManager.AddParameter(new SlotParameter(), "Slots", "S", "Monoceros Slots", GH_ParamAccess.list);
+ }
+
+ ///
+ /// Wrap input geometry into module cages.
+ ///
+ /// The DA object can be used to retrieve data from
+ /// input parameters and to store data in output parameters.
+ protected override void SolveInstance(IGH_DataAccess DA) {
+ var geometryRaw = new List();
+ var module = new Module();
+ var basePlane = new Plane();
+
+ if (!DA.GetDataList(0, geometryRaw)) {
+ return;
+ }
+
+ if (!DA.GetData(1, ref module)) {
+ return;
+ }
+
+ if (!DA.GetData(2, ref basePlane)) {
+ return;
+ }
+
+ if (module == null || !module.IsValid) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Module is null or invalid.");
+ return;
+ }
+
+ var geometryTransform = Transform.PlaneToPlane(basePlane, Plane.WorldXY);
+ var geometryClean = geometryRaw
+ .Select(goo => GH_Convert.ToGeometryBase(goo))
+ .Where(geo => geo != null)
+ .Select(geo => {
+ var transformedGeometry = geo.Duplicate();
+ transformedGeometry.Transform(geometryTransform);
+ return transformedGeometry;
+ }).ToList();
+ if (!geometryClean.Any()) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Warning,
+ "Failed to collect any valid geometry to scan.");
+ return;
+ }
+
+ var moduleTransform = Transform.PlaneToPlane(module.BasePlane, Plane.WorldXY);
+ var moduleGeometry = module.Geometry
+ .Concat(module.ReferencedGeometry)
+ .Select(geo => {
+ var transformedGeometry = geo.Duplicate();
+ transformedGeometry.Transform(moduleTransform);
+ return transformedGeometry;
+ });
+ var modulePartCenters = module.PartCenters
+ .Select(point => {
+ var transformedPoint = point.ToCartesian(module.BasePlane, module.PartDiagonal);
+ transformedPoint.Transform(moduleTransform);
+ return transformedPoint;
+ });
+ if (!moduleGeometry.Any()) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Warning,
+ "Module \"" + module.Name + "\" contains " +
+ "no geometry and therefore will be skipped.");
+ return;
+ }
+
+ var moduleBBoxes = moduleGeometry.Select(geo => geo.GetBoundingBox(false));
+ var modulePivot = module.Pivot;
+ modulePivot.Transform(moduleTransform);
+ var modulePivotOrigin = modulePivot.Origin;
+
+ var commonModuleBBox = BoundingBox.Empty;
+ commonModuleBBox.Union(modulePivotOrigin);
+
+ foreach (var moduleBBox in moduleBBoxes) {
+ commonModuleBBox.Union(moduleBBox);
+ }
+
+ var safetyBuffer = new Point3i(
+ (int)Math.Ceiling(commonModuleBBox.Diagonal.X / module.PartDiagonal.X) + 1,
+ (int)Math.Ceiling(commonModuleBBox.Diagonal.Y / module.PartDiagonal.Y) + 1,
+ (int)Math.Ceiling(commonModuleBBox.Diagonal.Z / module.PartDiagonal.Z) + 1);
+
+ var geometryBBoxes = geometryClean.Select(geo => geo.GetBoundingBox(false)).ToList();
+ var commonBBox = BoundingBox.Empty;
+
+ foreach (var bBox in geometryBBoxes) {
+ commonBBox.Union(bBox);
+ }
+
+ var slots = new List();
+
+ for (int z = (int)Math.Floor(commonBBox.Min.Z / module.PartDiagonal.Z) - safetyBuffer.Z;
+ z < Math.Ceiling(commonBBox.Max.Z / module.PartDiagonal.Z) + safetyBuffer.Z;
+ z++) {
+ for (int y = (int)Math.Floor(commonBBox.Min.Y / module.PartDiagonal.Y) - safetyBuffer.Y;
+ y < Math.Ceiling(commonBBox.Max.Y / module.PartDiagonal.Y) + safetyBuffer.Y;
+ y++) {
+ for (int x = (int)Math.Floor(commonBBox.Min.X / module.PartDiagonal.X) - safetyBuffer.X;
+ x < Math.Ceiling(commonBBox.Max.X / module.PartDiagonal.X) + safetyBuffer.X;
+ x++) {
+ var currentPivot = Plane.WorldXY;
+ var currentPivotOrigin = new Point3d(x * module.PartDiagonal.X,
+ y * module.PartDiagonal.Y,
+ z * module.PartDiagonal.Z);
+ currentPivot.Origin = currentPivotOrigin;
+ var currentTransform = Transform.PlaneToPlane(modulePivot, currentPivot);
+ var transformedModuleBBoxes = moduleBBoxes.Select(bBox => {
+ var transformedBBox = bBox;
+ transformedBBox.Transform(currentTransform);
+ return transformedBBox;
+ });
+
+ var indicesOfSimilarGeometries = new List();
+ var allModuleBoxesFoundSimilarGeometryBox = true;
+ foreach (var currentBBox in transformedModuleBBoxes) {
+ var foundBoxForCurrent = false;
+ var otherIndex = 0;
+ foreach (var otherBBox in geometryBBoxes) {
+ var currentPoints = currentBBox.GetCorners();
+ var otherPoints = otherBBox.GetCorners();
+ for (var i = 0; i < currentPoints.Length; i++) {
+ var currentPoint = currentPoints[i];
+ var otherPoint = otherPoints[i];
+ var equalPoints = currentPoint.EpsilonEquals(otherPoint, RhinoMath.SqrtEpsilon);
+ if (!equalPoints) {
+ break;
+ }
+ foundBoxForCurrent = true;
+ }
+ if (foundBoxForCurrent) {
+ indicesOfSimilarGeometries.Add(otherIndex);
+ break;
+ }
+ otherIndex++;
+ }
+ if (!foundBoxForCurrent) {
+ allModuleBoxesFoundSimilarGeometryBox = false;
+ break;
+ }
+ }
+
+ if (!allModuleBoxesFoundSimilarGeometryBox) {
+ continue;
+ }
+
+ // Heavy calculations
+
+ var transformedModuleGeometry = moduleGeometry.Select(geo => {
+ var transformedGeometry = geo.Duplicate();
+ transformedGeometry.Transform(currentTransform);
+ return transformedGeometry;
+ });
+
+ var geometriesToCheck = indicesOfSimilarGeometries.Select(index => geometryClean[index]);
+
+ var geometryEqualityPattern = transformedModuleGeometry
+ .Zip(geometriesToCheck, (current, other) => GeometryBase.GeometryEquals(current, other));
+
+ if (geometryEqualityPattern.All(equal => equal)) {
+
+ var transformedModulePartCenters = modulePartCenters.Select(centerPoint => {
+ var transformedPoint = centerPoint;
+ transformedPoint.Transform(currentTransform);
+ return transformedPoint;
+ });
+
+ var currentPivotCenter = new Point3d(currentPivotOrigin.X / module.PartDiagonal.X,
+ currentPivotOrigin.Y / module.PartDiagonal.Y,
+ currentPivotOrigin.Z / module.PartDiagonal.Z);
+ var currentPivotRelativeCenter = new Point3i(currentPivotCenter);
+ var firstModulePartRelativeCenter = module.PartCenters[0];
+ var modulePartsRelativeCentersRelativeToModulePivot = module.PartCenters.Select(center => center - firstModulePartRelativeCenter);
+
+ var currentModuleSlots = modulePartsRelativeCentersRelativeToModulePivot
+ .Zip(
+ module.PartNames,
+ (partCenter, partName) => new Slot(basePlane,
+ currentPivotRelativeCenter + partCenter,
+ module.PartDiagonal,
+ false,
+ new List() { module.Name },
+ new List() { partName },
+ 0));
+ slots.AddRange(currentModuleSlots);
+ }
+ }
+ }
+ }
+
+ if (!Slot.AreSlotLocationsUnique(slots)) {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Slot centers are not unique.");
+ }
+
+ DA.SetDataList(0, slots);
+ }
+
+
+ ///
+ /// The Exposure property controls where in the panel a component icon
+ /// will appear. There are seven possible locations (primary to
+ /// septenary), each of which can be combined with the
+ /// GH_Exposure.obscure flag, which ensures the component will only be
+ /// visible on panel dropdowns.
+ ///
+ public override GH_Exposure Exposure => GH_Exposure.tertiary;
+
+ ///
+ /// Provides an Icon for every component that will be visible in the
+ /// User Interface. Icons need to be 24x24 pixels.
+ ///
+ protected override System.Drawing.Bitmap Icon => Properties.Resources.slots_from_geometry;
+
+ ///
+ /// Each component must have a unique Guid to identify it. It is vital
+ /// this Guid doesn't change otherwise old ghx files that use the old ID
+ /// will partially fail during loading.
+ ///
+ public override Guid ComponentGuid => new Guid("725A08EC-60D6-4F72-9EF4-BA82864085D4");
+ }
+}
diff --git a/Components/Solver.cs b/Components/Solver.cs
index dec7dcf..0efdcf8 100644
--- a/Components/Solver.cs
+++ b/Components/Solver.cs
@@ -424,7 +424,7 @@ protected override void SolveInstance(IGH_DataAccess DA) {
return;
}
- if (slot.AllowsNothing) {
+ if (slot.IsContradictory) {
AddRuntimeMessage(GH_RuntimeMessageLevel.Error,
"Slot at " + slot.AbsoluteCenter + "does not allow any Module " +
"to be placed.");
diff --git a/Config.cs b/Config.cs
index a679a69..8ec9a02 100644
--- a/Config.cs
+++ b/Config.cs
@@ -1,4 +1,5 @@
-using System.Drawing;
+using System.Collections.Generic;
+using System.Drawing;
using System.Linq;
namespace Monoceros {
@@ -20,6 +21,10 @@ public class Config {
///
public const string INDIFFERENT_TAG = "indifferent";
+ public const string PART_SEPARATOR = "~";
+
+ public static List RESERVED_CHARS = new List() { "\n", ":", "=", PART_SEPARATOR };
+
///
/// Collected reserved module names.
///
diff --git a/Monoceros.csproj b/Monoceros.csproj
index 5e6bfb0..b39d838 100644
--- a/Monoceros.csproj
+++ b/Monoceros.csproj
@@ -56,10 +56,12 @@
+
+
-
+
@@ -67,7 +69,7 @@
-
+
@@ -79,7 +81,7 @@
-
+
@@ -126,6 +128,11 @@
+
+
+
+
+
@@ -154,7 +161,6 @@
-
@@ -180,7 +186,7 @@ Erase "$(TargetPath)"
en-US
- C:\Program Files\Rhino 6\System\Rhino.exe
+ C:\Program Files\Rhino 7\System\Rhino.exe
Program
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index a24417c..e4e67b1 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -35,5 +35,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.2.2.0")]
-[assembly: AssemblyFileVersion("1.2.2.0")]
+[assembly: AssemblyVersion("1.2.3.0")]
+[assembly: AssemblyFileVersion("1.2.3.0")]
diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
index 75ff884..7d391d6 100644
--- a/Properties/Resources.Designer.cs
+++ b/Properties/Resources.Designer.cs
@@ -230,6 +230,16 @@ internal static System.Drawing.Bitmap rule_indifferent {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap rule_indifferent_construct {
+ get {
+ object obj = ResourceManager.GetObject("rule_indifferent_construct", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
@@ -270,6 +280,16 @@ internal static System.Drawing.Bitmap rule_out {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap rule_out_construct {
+ get {
+ object obj = ResourceManager.GetObject("rule_out_construct", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
@@ -320,6 +340,16 @@ internal static System.Drawing.Bitmap rules_collect {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap rules_from_slots {
+ get {
+ object obj = ResourceManager.GetObject("rules_from_slots", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
@@ -333,9 +363,9 @@ internal static System.Drawing.Bitmap rules_unwrap {
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
- internal static System.Drawing.Bitmap slot_add_boundary_2 {
+ internal static System.Drawing.Bitmap slot_add_boundary {
get {
- object obj = ResourceManager.GetObject("slot_add_boundary_2", resourceCulture);
+ object obj = ResourceManager.GetObject("slot_add_boundary", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
@@ -400,6 +430,16 @@ internal static System.Drawing.Bitmap slot_transparent {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap slots_from_geometry {
+ get {
+ object obj = ResourceManager.GetObject("slots_from_geometry", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
diff --git a/Properties/Resources.resx b/Properties/Resources.resx
index 16b87f2..7f27e06 100644
--- a/Properties/Resources.resx
+++ b/Properties/Resources.resx
@@ -151,6 +151,9 @@
..\Resources\rules-collect.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\rules-from-slots.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
..\Resources\rules-unwrap.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
@@ -175,6 +178,9 @@
..\Resources\rule-indifferent.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\rule-indifferent-construct.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
..\Resources\rule-indifferent-unused.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
@@ -187,6 +193,9 @@
..\Resources\rule-out.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\rule-out-construct.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
..\Resources\rule-suggest.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
@@ -199,7 +208,10 @@
..\Resources\rule-typed-transparent.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
+
+ ..\Resources\slots-from-geometry.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
..\Resources\slot-add-boundary-2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/Resources/rule-indifferent-construct.png b/Resources/rule-indifferent-construct.png
new file mode 100644
index 0000000..f3c9713
Binary files /dev/null and b/Resources/rule-indifferent-construct.png differ
diff --git a/Resources/rule-out-construct.png b/Resources/rule-out-construct.png
new file mode 100644
index 0000000..1315680
Binary files /dev/null and b/Resources/rule-out-construct.png differ
diff --git a/Resources/rule-out.png b/Resources/rule-out.png
index cc8c5c7..893d1a6 100644
Binary files a/Resources/rule-out.png and b/Resources/rule-out.png differ
diff --git a/Resources/rule-suggest.png b/Resources/rule-suggest.png
index 3d7d5a6..d8c5146 100644
Binary files a/Resources/rule-suggest.png and b/Resources/rule-suggest.png differ
diff --git a/Resources/rules-from-slots.png b/Resources/rules-from-slots.png
new file mode 100644
index 0000000..a7d5e19
Binary files /dev/null and b/Resources/rules-from-slots.png differ
diff --git a/Resources/slots-from-geometry.png b/Resources/slots-from-geometry.png
new file mode 100644
index 0000000..1ee8283
Binary files /dev/null and b/Resources/slots-from-geometry.png differ
diff --git a/Types/Direction.cs b/Types/Direction.cs
index bd547fa..5e790ce 100644
--- a/Types/Direction.cs
+++ b/Types/Direction.cs
@@ -20,6 +20,37 @@ public Direction(Axis axis, Orientation orientation) {
this.orientation = orientation;
}
+ public static bool FromVector (Vector3d vector, out Direction direction) {
+ if (vector.Unitize()) {
+ if (vector.X == 1 && vector.Y == 0 && vector.Z == 0) {
+ direction = new Direction(Axis.X, Orientation.Positive);
+ return true;
+ }
+ if (vector.X == -1 && vector.Y == 0 && vector.Z == 0) {
+ direction = new Direction(Axis.X, Orientation.Negative);
+ return true;
+ }
+ if (vector.X == 0 && vector.Y == 1 && vector.Z == 0) {
+ direction = new Direction(Axis.Y, Orientation.Positive);
+ return true;
+ }
+ if (vector.X == 0 && vector.Y == -1 && vector.Z == 0) {
+ direction = new Direction(Axis.Y, Orientation.Negative);
+ return true;
+ }
+ if (vector.X == 0 && vector.Y == 0 && vector.Z == 1) {
+ direction = new Direction(Axis.Z, Orientation.Positive);
+ return true;
+ }
+ if (vector.X == 0 && vector.Y == 0 && vector.Z == -1) {
+ direction = new Direction(Axis.Z, Orientation.Negative);
+ return true;
+ }
+ }
+ direction = new Direction();
+ return false;
+ }
+
///
/// Determines whether the other is opposite to
/// the current. A is opposite when the
diff --git a/Types/Module.cs b/Types/Module.cs
index f996165..a8054ad 100644
--- a/Types/Module.cs
+++ b/Types/Module.cs
@@ -158,7 +158,7 @@ public Module(string name,
Vector3d partDiagonal) {
// Check if any part centers are defined
if (!partCenters.Any()) {
- throw new Exception("part centers list is empty");
+ throw new Exception("Part centers list is empty");
}
// Check if all the parts are unique
@@ -183,7 +183,7 @@ public Module(string name,
// Generate part names to be used as module names by the Monoceros solver
PartNames = new List();
for (var i = 0; i < partCenters.Count; i++) {
- PartNames.Add(name + i);
+ PartNames.Add(name + Config.PART_SEPARATOR + i);
}
// Place the pivot into the first part and orient is according to the base plane
@@ -192,7 +192,7 @@ public Module(string name,
pivot.Origin = PartCenters[0].ToCartesian(BasePlane, PartDiagonal);
Pivot = pivot;
// The name of the first part which should trigger the geometry placement
- PivotPartName = Name + 0;
+ PivotPartName = Name + Config.PART_SEPARATOR + 0;
// The connectors describe faces of parts and their relation to the entire module
Connectors = ComputeModuleConnectors(PartCenters,
@@ -470,6 +470,11 @@ private List ComputeInternalRules(List partCenters,
return rulesInternal;
}
+ ///
+ /// Checks if any Module Part name equals the test value.
+ ///
+ public bool ContainsPart(string partName) => PartNames.Any(name => name == partName);
+
///
/// Gets a value indicating whether the module is valid. Required by
/// Grasshopper.
diff --git a/Types/Rule.cs b/Types/Rule.cs
index c6589ea..7b3d318 100644
--- a/Types/Rule.cs
+++ b/Types/Rule.cs
@@ -3,6 +3,8 @@
using System.Linq;
using GH_IO.Serialization;
using Grasshopper.Kernel.Types;
+using Rhino;
+using Rhino.Geometry;
namespace Monoceros {
///
@@ -555,6 +557,84 @@ public RuleExplicit(string sourceModuleName,
TargetConnectorIndex = (int)targetConnectorIndex;
}
+ public static bool FromRuleForSolver(RuleForSolver ruleForSolver,
+ IEnumerable modules,
+ out RuleExplicit ruleExplicit) {
+ ruleExplicit = new RuleExplicit("wrong", 0, "error", 0);
+
+ var currentPartName = ruleForSolver.LowerPartName;
+ var otherPartName = ruleForSolver.HigherPartName;
+ var currentModule = modules.First(module => module.ContainsPart(currentPartName));
+ var otherModule = modules.First(module => module.ContainsPart(otherPartName));
+
+ if (currentModule == null
+ || otherModule == null
+ // Rule is internal
+ || (currentModule == otherModule && currentModule.InternalRules.Contains(ruleForSolver))) {
+ return false;
+ }
+ var currentPartIndex = currentModule.PartNames.FindIndex(name => name == currentPartName);
+ var currentPartCenter = currentModule
+ .PartCenters[currentPartIndex]
+ .ToCartesian(currentModule.BasePlane, currentModule.PartDiagonal);
+ var currentConnectorVector = new Vector3d();
+ if (ruleForSolver.Axis == Axis.X) {
+ currentConnectorVector = currentModule.BasePlane.XAxis;
+ currentConnectorVector.Unitize();
+ currentConnectorVector *= currentModule.PartDiagonal.X / 2;
+ }
+ if (ruleForSolver.Axis == Axis.Y) {
+ currentConnectorVector = currentModule.BasePlane.YAxis;
+ currentConnectorVector.Unitize();
+ currentConnectorVector *= currentModule.PartDiagonal.Y / 2;
+ }
+ if (ruleForSolver.Axis == Axis.Z) {
+ currentConnectorVector = currentModule.BasePlane.ZAxis;
+ currentConnectorVector.Unitize();
+ currentConnectorVector *= currentModule.PartDiagonal.Z / 2;
+ }
+ var currentPartConnectorOrigin = currentPartCenter + currentConnectorVector;
+ var currentConnectorIndex = currentModule
+ .Connectors
+ .FindIndex(connector => connector.AnchorPlane.Origin.EpsilonEquals(currentPartConnectorOrigin,
+ RhinoMath.SqrtEpsilon));
+ if (currentConnectorIndex == -1) {
+ return false;
+ }
+
+ var otherPartIndex = otherModule.PartNames.FindIndex(name => name == otherPartName);
+ var otherPartCenter = otherModule
+ .PartCenters[otherPartIndex]
+ .ToCartesian(otherModule.BasePlane, otherModule.PartDiagonal);
+ var otherConnectorVector = new Vector3d();
+ if (ruleForSolver.Axis == Axis.X) {
+ otherConnectorVector = otherModule.BasePlane.XAxis;
+ otherConnectorVector.Unitize();
+ otherConnectorVector *= -otherModule.PartDiagonal.X / 2;
+ }
+ if (ruleForSolver.Axis == Axis.Y) {
+ otherConnectorVector = otherModule.BasePlane.YAxis;
+ otherConnectorVector.Unitize();
+ otherConnectorVector *= -otherModule.PartDiagonal.Y / 2;
+ }
+ if (ruleForSolver.Axis == Axis.Z) {
+ otherConnectorVector = otherModule.BasePlane.ZAxis;
+ otherConnectorVector.Unitize();
+ otherConnectorVector *= -otherModule.PartDiagonal.Z / 2;
+ }
+ var otherPartConnectorOrigin = otherPartCenter + otherConnectorVector;
+ var otherConnectorIndex = otherModule
+ .Connectors
+ .FindIndex(connector => connector.AnchorPlane.Origin.EpsilonEquals(otherPartConnectorOrigin,
+ RhinoMath.SqrtEpsilon));
+ if (otherConnectorIndex == -1) {
+ return false;
+ }
+
+ ruleExplicit = new RuleExplicit(currentModule.Name, (uint)currentConnectorIndex, otherModule.Name, (uint)otherConnectorIndex);
+ return true;
+ }
+
///
/// Checks if the provided object is equal to the current
/// . The check is bi-directional, so an
diff --git a/Types/Slot.cs b/Types/Slot.cs
index 9c67c4f..f874abd 100644
--- a/Types/Slot.cs
+++ b/Types/Slot.cs
@@ -431,7 +431,7 @@ public void DrawViewportWires(GH_PreviewWireArgs args) {
color = Config.CAGE_EVERYTHING_COLOR;
}
- if (AllowsNothing) {
+ if (IsContradictory) {
color = Config.CAGE_NONE_COLOR;
}
@@ -463,7 +463,14 @@ public void DrawViewportWires(GH_PreviewWireArgs args) {
/// not allow placement of any part and therefore also any
/// .
///
- public bool AllowsNothing => !(AllowsAnyModule || AllowedModuleNames.Any());
+ public bool IsContradictory => !(AllowsAnyModule || AllowedModuleNames.Any());
+
+ ///
+ /// True if the is deterministic and therefore does
+ /// allow placement of exactly one Part. Such Slot
+ /// is considered to be solved and can be materialized.
+ ///
+ public bool IsDeterministic => AllowedPartNames.Count == 1;
///
/// Checks the validity of a . Required by
@@ -533,10 +540,10 @@ public override string ToString( ) {
if (AllowsAnyModule) {
containment = "all Modules";
}
- if (AllowsNothing) {
+ if (IsContradictory) {
containment = "no Modules";
}
- if (!AllowsNothing && !AllowsAnyModule) {
+ if (!IsContradictory && !AllowsAnyModule) {
var count = AllowedModuleNames.Count;
if (count == 1) {
containment = "Module '" + AllowedModuleNames[0] + "'";
@@ -586,7 +593,7 @@ public void BakeGeometry(RhinoDoc doc, ObjectAttributes att, List obj_ids)
color = Config.CAGE_EVERYTHING_COLOR;
}
- if (AllowsNothing || !IsValid) {
+ if (IsContradictory || !IsValid) {
color = Config.CAGE_NONE_COLOR;
}
diff --git a/bin/Monoceros.pdb b/bin/Monoceros.pdb
index ca7cbe7..623dc44 100644
Binary files a/bin/Monoceros.pdb and b/bin/Monoceros.pdb differ