/// <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> /// 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 slots = new List <Slot>(); var layers = 1; if (!DA.GetDataList(0, slots)) { return; } if (!DA.GetData(1, ref layers)) { return; } var slotsClean = new List <Slot>(); foreach (var slot in slots) { if (slot == null || !slot.IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slot is null or invalid."); } else { slotsClean.Add(slot); } } if (!slotsClean.Any()) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No valid Slots collected."); return; } if (!Slot.AreSlotDiagonalsCompatible(slotsClean)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slots are not defined with the same diagonal."); return; } var diagonal = slotsClean.First().Diagonal; if (!Slot.AreSlotBasePlanesCompatible(slotsClean)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slots are not defined with the same base plane."); return; } var basePlane = slotsClean.First().BasePlane; if (!Slot.AreSlotLocationsUnique(slotsClean)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slot centers are not unique."); return; } var potentialRelativeNeighborCenters = new List <Point3i> { new Point3i(-1, 0, 0), new Point3i(0, -1, 0), new Point3i(0, 0, -1), new Point3i(1, 0, 0), new Point3i(0, 1, 0), new Point3i(0, 0, 1) }; var allSlotCenters = slotsClean.Select(slot => slot.RelativeCenter).ToList(); var newNeighborCenters = new List <Point3i>(); for (int l = 0; l < layers; l++) { var currentNewNeighborCenters = new List <Point3i>(); for (var i = 0; i < allSlotCenters.Count; i++) { var slotCenter = allSlotCenters[i]; var currentNeighborCenters = new List <Point3i>(); foreach (var other in allSlotCenters) { if (slotCenter.IsNeighbor(other)) { currentNeighborCenters.Add(other); if (currentNeighborCenters.Count == 6) { break; } } } if (currentNeighborCenters.Count < 6) { var potentialNeighborCenters = potentialRelativeNeighborCenters .Select(relativeNeighborCenter => slotCenter + relativeNeighborCenter); var currentNewlNeighborCenters = potentialNeighborCenters.Except(currentNeighborCenters); currentNewNeighborCenters.AddRange(currentNewlNeighborCenters); } } newNeighborCenters = newNeighborCenters .Concat(currentNewNeighborCenters) .Distinct() .ToList(); allSlotCenters.AddRange(currentNewNeighborCenters); } var newNeighborCentersP3d = newNeighborCenters .Select(p3i => p3i.ToCartesian(basePlane, diagonal)); DA.SetDataList(0, newNeighborCentersP3d); }
/// <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 slots = new List <Slot>(); var layers = 1; if (!DA.GetDataList(0, slots)) { return; } if (!DA.GetData(1, ref layers)) { return; } var slotsClean = new List <Slot>(); foreach (var slot in slots) { if (slot == null || !slot.IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slot is null or invalid."); } else { slotsClean.Add(slot); } } if (!slotsClean.Any()) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No valid Slots collected."); return; } var diagonal = slotsClean.First().Diagonal; if (slotsClean.Any(slot => slot.Diagonal != diagonal)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slots are not defined with the same diagonal."); return; } if (!Slot.AreSlotLocationsUnique(slotsClean)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Slot locations are not unique."); return; } var boundaryPattern = Enumerable.Repeat(false, slotsClean.Count).ToList(); for (int l = 0; l < layers; l++) { var currentBoundaryPattern = Enumerable.Repeat(true, slotsClean.Count).ToList(); for (var i = 0; i < slotsClean.Count; i++) { if (!boundaryPattern[i]) { var slot = slotsClean[i]; var neighborsCount = 0; for (var j = 0; j < slotsClean.Count; j++) { if (!boundaryPattern[j]) { var other = slotsClean[j]; if (slot.RelativeCenter.IsNeighbor(other.RelativeCenter) && ++neighborsCount == 6) { currentBoundaryPattern[i] = false; break; } } } } } for (var i = 0; i < currentBoundaryPattern.Count; i++) { boundaryPattern[i] |= currentBoundaryPattern[i]; } } DA.SetDataList(0, boundaryPattern); }