/// <summary> /// De-serialization. Required by Grasshopper for data internalization. /// </summary> /// <param name="reader">The reader.</param> /// <returns>True when successful.</returns> public bool Read(GH_IReader reader) { var formatter = new BinaryFormatter(); if (reader.ItemExists("SlotData")) { var slotBytes = reader.GetByteArray("SlotData"); using (var slotDataStream = new System.IO.MemoryStream(slotBytes)) { try { var slot = (Slot)formatter.Deserialize(slotDataStream); BasePlane = slot.BasePlane; RelativeCenter = slot.RelativeCenter; Diagonal = slot.Diagonal; AllowsAnyModule = slot.AllowsAnyModule; AllowedModuleNames = slot.AllowedModuleNames; AllowedPartNames = slot.AllowedPartNames; AllPartsCount = slot.AllPartsCount; return(true); } catch (SerializationException e) { throw e; } } } return(false); }
/// <summary> /// Initializes a new instance of the <see cref="Slot"/> class. /// </summary> /// <param name="basePlane">The base plane - has to match with the other /// <see cref="Slot"/>s of the World.</param> /// <param name="relativeCenter">The relative center.</param> /// <param name="diagonal">The <see cref="Slot"/> diagonal - has to /// match with the other <see cref="Slot"/>s of the World and with /// all <see cref="Module"/>s</param> /// <param name="allowAnyModule">If true, any part can be placed into /// the <see cref="Slot"/>.</param> /// <param name="allowedModules">The allowed modules.</param> /// <param name="allowedParts">The allowed parts.</param> /// <param name="allPartsCount">The all parts count.</param> public Slot(Plane basePlane, Point3i relativeCenter, Vector3d diagonal, bool allowAnyModule, List <string> allowedModules, List <string> allowedParts, int allPartsCount) { if (diagonal.X <= 0 || diagonal.Y <= 0 || diagonal.Z <= 0) { throw new Exception("One or more slot dimensions are not larger than 0."); } if (allPartsCount < 0) { throw new Exception("All parts count is lower than 0."); } Diagonal = diagonal; BasePlane = basePlane; RelativeCenter = relativeCenter; AllowsAnyModule = allowAnyModule; AllowedModuleNames = allowedModules; AllowedPartNames = allowedParts; AllPartsCount = allPartsCount; }
/// <summary> /// Wrap input geometry into module cages. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from /// input parameters and to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { var point = new Point3d(); var basePlane = new Plane(); var diagonal = new Vector3d(); if (!DA.GetData(0, ref point)) { return; } if (!DA.GetData(1, ref basePlane)) { return; } if (!DA.GetData(2, ref diagonal)) { return; } if (diagonal.X <= 0 || diagonal.Y <= 0 || diagonal.Z <= 0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "One or more slot dimensions are not larger than 0."); return; } // Scale down to unit size var normalizationTransform = Transform.Scale(basePlane, 1 / diagonal.X, 1 / diagonal.Y, 1 / diagonal.Z); // Orient to the world coordinate system var worldAlignmentTransform = Transform.PlaneToPlane(basePlane, Plane.WorldXY); point.Transform(normalizationTransform); point.Transform(worldAlignmentTransform); // Round point location // Slot dimension is for the sake of this calculation 1,1,1 var slotCenterPoint = new Point3i(point); var slot = new Slot(basePlane, slotCenterPoint, diagonal, true, new List <string>(), new List <string>(), 0); if (!slot.IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, slot.IsValidWhyNot); return; } DA.SetData(0, slot); }
/// <summary> /// Wrap input geometry into module cages. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from /// input parameters and to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { var modules = new List <Module>(); var rule = new Rule(); var basePlane = new Plane(); if (!DA.GetDataList(0, modules)) { return; } if (!DA.GetData(1, ref rule)) { return; } if (!DA.GetData(2, ref basePlane)) { return; } var transforms = new DataTree <Transform>(); var geometry = new DataTree <GeometryBase>(); if (rule == null || !rule.IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The rule is null or invalid."); return; } if (!rule.IsExplicit) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Rule Assemble works only with Monoceros Explicit Rules to " + "make sure a single Rule generate a single assembly. " + "Unwrap Monoceros Rules first."); return; } //if (rule.Explicit.SourceModuleName == Config.OUTER_MODULE_NAME // || rule.Explicit.TargetModuleName == Config.OUTER_MODULE_NAME // || rule.Explicit.SourceModuleName == Config.EMPTY_MODULE_NAME // || rule.Explicit.TargetModuleName == Config.EMPTY_MODULE_NAME) { // AddRuntimeMessage(GH_RuntimeMessageLevel.Error, // "Catalog cannot display a Monoceros Rule describing a connection " + // "to an outer or empty Monoceros Module."); // return; //} var invalidModuleCount = modules.RemoveAll(module => module == null || !module.IsValid); if (invalidModuleCount > 0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, invalidModuleCount + " Modules are null or invalid and were removed."); } if (!modules.Any()) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No valid Modules collected."); return; } if (!rule.IsValidWithModules(modules)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The Monoceros Rule is not valid with the given Monoceros Modules."); return; } var sourceModule = modules.Find(module => module.Name == rule.Explicit.SourceModuleName); var sourceConnector = sourceModule.Connectors[rule.Explicit.SourceConnectorIndex]; var targetModule = modules.Find(module => module.Name == rule.Explicit.TargetModuleName); var targetConnector = targetModule.Connectors[rule.Explicit.TargetConnectorIndex]; if (sourceModule.BasePlane != targetModule.BasePlane) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The source and target Modules are not defined with the same Base Plane. " + "The resulting Slots would be incompatible."); return; } if (sourceModule.PartDiagonal != targetModule.PartDiagonal) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The source and target Module Part Diagonals are not identical. " + "The Modules are incompatible."); return; } var allPartsCount = sourceModule.PartCenters.Count + targetModule.PartCenters.Count; var sourcePivotOffset = Point3i.FromCartesian(sourceModule.Pivot.Origin, sourceModule.BasePlane, sourceModule.PartDiagonal); var sourceSlots = new List <Slot>(); for (var i = 0; i < sourceModule.PartNames.Count; i++) { var sourceModulePartCenter = sourceModule.PartCenters[i] - sourcePivotOffset; var sourceModulePartName = sourceModule.PartNames[i]; var slot = new Slot(basePlane, sourceModulePartCenter, sourceModule.PartDiagonal, false, new List <string> { sourceModule.Name }, new List <string> { sourceModulePartName }, allPartsCount); sourceSlots.Add(slot); } var targetToSourceTransfrom = Transform.PlaneToPlane(targetConnector.AnchorPlane, sourceConnector.AnchorPlane); var targetPartCenters = targetModule.PartCenters.Select(center => center.ToCartesian(targetModule.BasePlane, targetModule.PartDiagonal)); var targetSlots = new List <Slot>(); for (var i = 0; i < targetModule.PartNames.Count; i++) { var targetModulePartCenterCartesian = targetModule .PartCenters[i] .ToCartesian(targetModule.BasePlane, targetModule.PartDiagonal); targetModulePartCenterCartesian.Transform(targetToSourceTransfrom); var targetModulePartCenter = Point3i.FromCartesian(targetModulePartCenterCartesian, sourceModule.BasePlane, sourceModule.PartDiagonal) - sourcePivotOffset; var targetModulePartName = targetModule.PartNames[i]; var slot = new Slot(basePlane, targetModulePartCenter, targetModule.PartDiagonal, false, new List <string> { targetModule.Name }, new List <string> { targetModulePartName }, allPartsCount); targetSlots.Add(slot); } DA.SetDataList(0, sourceSlots); DA.SetDataList(1, targetSlots); }
/// <summary> /// Wrap input geometry into module cages. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from /// input parameters and to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { var geometryRaw = new List <IGH_GeometricGoo>(); 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; } // Transform the geometry to be oriented to world XYZ fore easier scanning 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 moduleGeometry = module.Geometry .Concat(module.ReferencedGeometry); if (!moduleGeometry.Any()) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Module \"" + module.Name + "\" contains " + "no geometry and therefore will be skipped."); return; } var moduleGeometryBBoxes = moduleGeometry.Select(geo => geo.GetBoundingBox(true)); var bBoxUnionModule = BoundingBox.Empty; bBoxUnionModule.Union(module.Pivot.Origin); foreach (var moduleBBox in moduleGeometryBBoxes) { bBoxUnionModule.Union(moduleBBox); } var moduleDimensionSafetyBuffer = new Point3i( (int)Math.Ceiling(bBoxUnionModule.Diagonal.X / module.PartDiagonal.X) + 1, (int)Math.Ceiling(bBoxUnionModule.Diagonal.Y / module.PartDiagonal.Y) + 1, (int)Math.Ceiling(bBoxUnionModule.Diagonal.Z / module.PartDiagonal.Z) + 1); var geometryBBoxes = geometryClean.Select(geo => geo.GetBoundingBox(true)).ToList(); var bBoxUnionGeometry = BoundingBox.Empty; foreach (var bBox in geometryBBoxes) { bBoxUnionGeometry.Union(bBox); } var slots = new List <Slot>(); for (int z = (int)Math.Floor(bBoxUnionGeometry.Min.Z / module.PartDiagonal.Z) - moduleDimensionSafetyBuffer.Z; z < Math.Ceiling(bBoxUnionGeometry.Max.Z / module.PartDiagonal.Z) + moduleDimensionSafetyBuffer.Z; z++) { for (int y = (int)Math.Floor(bBoxUnionGeometry.Min.Y / module.PartDiagonal.Y) - moduleDimensionSafetyBuffer.Y; y < Math.Ceiling(bBoxUnionGeometry.Max.Y / module.PartDiagonal.Y) + moduleDimensionSafetyBuffer.Y; y++) { for (int x = (int)Math.Floor(bBoxUnionGeometry.Min.X / module.PartDiagonal.X) - moduleDimensionSafetyBuffer.X; x < Math.Ceiling(bBoxUnionGeometry.Max.X / module.PartDiagonal.X) + moduleDimensionSafetyBuffer.X; x++) { var currentRelativePosition = new Point3i(x, y, z); var currentPivot = Plane.WorldXY; currentPivot.Origin = currentRelativePosition.ToCartesian(Plane.WorldXY, module.PartDiagonal); var transformModuleToCurrentPivot = Transform.PlaneToPlane(module.Pivot, currentPivot); var moduleGeometryBBoxesAtCurrentPivot = moduleGeometryBBoxes.Select(bBox => { var transformedBBox = bBox; transformedBBox.Transform(transformModuleToCurrentPivot); return(transformedBBox); }); var indicesOfSimilarBBoxes = moduleGeometryBBoxesAtCurrentPivot.Select(moduleGeometryBBox => geometryBBoxes.Select((geometryBBox, index) => { var moduleCorners = moduleGeometryBBox.GetCorners().ToList(); var geometryCorners = geometryBBox.GetCorners().ToList(); moduleCorners.Sort(); geometryCorners.Sort(); if (Enumerable.SequenceEqual(moduleCorners, geometryCorners)) { return(index); } else { return(-1); } }).Where(index => index != -1) ); // If any module geometry doesn't have a bbox similar to any geometry, then early continue if (!indicesOfSimilarBBoxes.All(similarBBoxes => similarBBoxes.Any())) { continue; } // Heavy calculations var transformedModuleGeometry = moduleGeometry.Select(geo => { var transformedGeometry = geo.Duplicate(); transformedGeometry.Transform(transformModuleToCurrentPivot); return(transformedGeometry); }); var geometriesToCheck = indicesOfSimilarBBoxes.Select(indices => indices.Select(index => geometryClean[index])); var geometryEqualityPattern = transformedModuleGeometry .Zip(geometriesToCheck, (current, others) => others.Any(other => // TODO: when the original geometry is moved, the meshes become unequal // TODO: replace with visual similarity check (pull random points to geometry) // TODO: check if the two are of the same type first GeometryBase.GeometryEquals(current, other) )); if (geometryEqualityPattern.All(equal => equal)) { var firstModulePartRelativeCenter = module.PartCenters[0]; var modulePartsRelativeCentersRelativeToModulePivot = module.PartCenters.Select(center => center - firstModulePartRelativeCenter); var currentModuleSlots = modulePartsRelativeCentersRelativeToModulePivot .Zip( module.PartNames, (partCenter, partName) => new Slot(basePlane, currentRelativePosition + partCenter, module.PartDiagonal, false, new List <string>() { module.Name }, new List <string>() { partName }, 0)); slots.AddRange(currentModuleSlots); } } } } if (!Slot.AreSlotLocationsUnique(slots)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Slot centers are not unique."); } DA.SetDataList(0, slots); }
/// <summary> /// Checks if two discrete points are neighbors in one of the 6 /// directions in the discrete grid world. /// </summary> /// <param name="other">The other point.</param> /// <returns>True if neighbors</returns> public bool IsNeighbor(Point3i other) { return((Math.Abs(X - other.X) == 1 && Y == other.Y && Z == other.Z) || (X == other.X && Math.Abs(Y - other.Y) == 1 && Z == other.Z) || (X == other.X && Y == other.Y && (Math.Abs(Z - other.Z) == 1))); }
/// <summary> /// Wrap input geometry into module cages. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from /// input parameters and to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { var point = new Point3d(); var basePlane = new Plane(); var diagonal = new Vector3d(); var allowedModulesRaw = new List <ModuleName>(); var allModules = new List <Module>(); var modulesProvided = false; if (!DA.GetData(0, ref point)) { return; } if (!DA.GetData(1, ref basePlane)) { return; } if (!DA.GetData(2, ref diagonal)) { return; } if (!DA.GetDataList(3, allowedModulesRaw)) { return; } if (DA.GetDataList(4, allModules)) { modulesProvided = true; } var allowedModules = allowedModulesRaw .Where(name => name != null) .Select(name => name.Name).Distinct().ToList(); if (Config.RESERVED_CHARS.Any(chars => allowedModules.Contains(chars))) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input text contains " + "a forbidden content: :, ->, = or newline."); return; } if (diagonal.X <= 0 || diagonal.Y <= 0 || diagonal.Z <= 0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "One or more slot dimensions are not larger than 0."); return; } // Scale down to unit size var normalizationTransform = Transform.Scale(basePlane, 1 / diagonal.X, 1 / diagonal.Y, 1 / diagonal.Z); // Orient to the world coordinate system var worldAlignmentTransform = Transform.PlaneToPlane(basePlane, Plane.WorldXY); point.Transform(normalizationTransform); point.Transform(worldAlignmentTransform); // Round point location // Slot dimension is for the sake of this calculation 1,1,1 var slotCenterPoint = new Point3i(point); var allModulesCount = modulesProvided ? allModules.Aggregate(0, (count, module) => count + module.PartCenters.Count) : 0; var allowedParts = modulesProvided ? allModules .Where(module => allowedModules.Contains(module.Name)) .SelectMany(module => module.PartNames) .ToList() : new List <string>(); var slot = new Slot(basePlane, slotCenterPoint, diagonal, false, allowedModules, allowedParts, allModulesCount); if (!slot.IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, slot.IsValidWhyNot); return; } DA.SetData(0, slot); }