private bool ComputeRowNumberAndLength(Layer2DCylImp layer , out int firstRowLength, out int secondRowLength, out int rowNumber , out double actualLength, out double actualWidth) { double palletLength = GetPalletLength(layer); double palletWidth = GetPalletWidth(layer); double radius = layer.Radius; double diameter = 2.0 * layer.Radius; // initialize out parameters firstRowLength = 0; secondRowLength = 0; rowNumber = 0; actualLength = 0.0; actualWidth = 0.0; // sanity check if (diameter > palletLength || diameter > palletWidth) { return(false); } // first row number firstRowLength = (int)Math.Floor(palletLength / diameter); // second row if ((firstRowLength + 0.5) * diameter < palletLength) { secondRowLength = firstRowLength; actualLength = (firstRowLength + 0.5) * diameter; } else { secondRowLength = firstRowLength - 1; actualLength = firstRowLength * diameter; } // numbers of rows rowNumber = (int)Math.Floor(1 + (palletWidth / radius - 2.0) / Math.Sqrt(3.0)); actualWidth = (2.0 + (rowNumber - 1) * Math.Sqrt(3.0)) * radius; return(true); }
public ILayer2D BuildLayer(Packable packable, Vector2D dimContainer, LayerDesc layerDesc, Vector2D actualDimensions, double minSpace) { ILayer2D layer = null; LayerPattern pattern = null; if (packable.IsBrick) { LayerDescBox layerDescBox = layerDesc as LayerDescBox; // instantiate layer layer = new Layer2DBrickImp(packable.OuterDimensions, dimContainer, layerDescBox.PatternName, layerDescBox.AxisOrtho, layerDesc.Swapped) { ForcedSpace = minSpace }; // get layer pattern pattern = LayerPatternBox.GetByName(layerDesc.PatternName); } else if (packable.IsCylinder) { var cylProperties = packable as RevSolidProperties; layer = new Layer2DCylImp(cylProperties.RadiusOuter, cylProperties.Height, dimContainer, layerDesc.Swapped); // get layer pattern pattern = LayerPatternCyl.GetByName(layerDesc.PatternName); } else { throw new EngineException(string.Format("Unexpected packable {0} (Type = {1})", packable.Name, packable.GetType().ToString())); } pattern.GenerateLayer( layer , layer.Swapped ? actualDimensions.Y : actualDimensions.X , layer.Swapped ? actualDimensions.X : actualDimensions.Y ); return(layer); }
public override void GenerateLayer(ILayer2D layer, double actualLength, double actualWidth) { layer.Clear(); double palletLength = GetPalletLength(layer); double palletWidth = GetPalletWidth(layer); double radius = GetRadius(layer); Layer2DCylImp layerCyl = layer as Layer2DCylImp; if (!ComputeRowNumberAndLength(layerCyl , out int firstRowLength, out int secondRowLength, out int rowNumber , out actualLength, out actualWidth)) { return; } double offsetX = 0.5 * (palletLength - actualLength); double offsetY = 0.5 * (palletWidth - actualWidth); for (int i = 0; i < rowNumber; ++i) { double y = (offsetY + radius) + i * radius * Math.Sqrt(3.0); for (int j = 0; j < (i % 2 == 0 ? firstRowLength : secondRowLength); ++j) { AddPosition(layer, new Vector2D(offsetX + ((i % 2 == 0) ? 0.0 : radius) + j * 2.0 * radius + radius, y)); } } }
public override bool GetLayerDimensions(ILayer2D layer, out double actualLength, out double actualWidth) { Layer2DCylImp layerCyl = layer as Layer2DCylImp; ComputeRowNumberAndLength(layerCyl , out int firstRowLength, out int secondRowLength, out int rowNumber , out actualLength, out actualWidth); return((firstRowLength > 0) && (secondRowLength > 0) && (rowNumber > 0)); }
public void Draw(Graphics2D graphics, Packable packable, double height, bool selected, bool annotate) { graphics.NumberOfViews = 1; graphics.Clear(selected ? Color.LightBlue : Color.White); graphics.SetViewport(0.0f, 0.0f, (float)_layer.Length, (float)_layer.Width); graphics.SetCurrentView(0); graphics.DrawRectangle(Vector2D.Zero, new Vector2D(_layer.Length, _layer.Width), Color.Black); // draw layer (brick) if (_layer is Layer2DBrick layer2D) { uint pickId = 0; foreach (var bPosition in layer2D.Positions) { Box b = null; if (packable is PackProperties) { b = new Pack(pickId++, packable as PackProperties, bPosition); } else if (packable is PackableBrick) { b = new Box(pickId++, packable as PackableBrick, bPosition); } if (null != b) { b.Draw(graphics); } } } // draw layer (cylinder) else if (_layer is Layer2DCylImp) { Layer2DCylImp layer2DCyl = _layer as Layer2DCylImp; uint pickId = 0; foreach (Vector2D pos in layer2DCyl) { Cylinder c = new Cylinder(pickId++, packable as CylinderProperties, new CylPosition(new Vector3D(pos.X, pos.Y, 0.0), HalfAxis.HAxis.AXIS_Z_P)); graphics.DrawCylinder(c); } } // draw axes bool showAxis = false; if (showAxis) { // draw axis X graphics.DrawLine(Vector2D.Zero, new Vector2D(_layer.Length, 0.0), Color.Red); // draw axis Y graphics.DrawLine(Vector2D.Zero, new Vector2D(0.0, _layer.Width), Color.Green); } // annotate thumbnail if (annotate) { Annotate(graphics.Graphics, graphics.Size, height); } }
private bool ComputeRowNumberAndLength(Layer2DCylImp layer , out int alignedRowLength, out int rowNumber1 , out int stagRowLength, out int rowNumber2 , out double actualLength, out double actualWidth) { double palletLength = GetPalletLength(layer); double palletWidth = GetPalletWidth(layer); double radius = layer.Radius; double diameter = 2.0 * layer.Radius; // initialize out parameters alignedRowLength = 0; rowNumber1 = 0; stagRowLength = 0; rowNumber2 = 0; actualLength = 0; actualWidth = 0; // sanity check if (diameter > palletLength || diameter > palletWidth) { return(false); } // first row number alignedRowLength = (int)Math.Floor(palletLength / diameter); // second row if ((alignedRowLength + 0.5) * diameter < palletLength) { stagRowLength = alignedRowLength; actualLength = (stagRowLength + 0.5) * diameter; } else { stagRowLength = alignedRowLength - 1; actualLength = stagRowLength * diameter; } actualLength = Math.Max(actualLength, alignedRowLength * diameter); // numbers of rows int rowNumber1Max = (int)Math.Floor(palletWidth / diameter); // optimization int maxCount = 0; for (int i = 1; i < rowNumber1Max - 1; ++i) { int rowNumber1Temp = i; int rowNumber2Temp = (int)Math.Floor(((palletWidth / radius) - 2.0 * rowNumber1Temp) / Math.Sqrt(3.0)); int count = alignedRowLength * rowNumber1Temp + (rowNumber2Temp / 2 + (rowNumber2Temp % 2 == 0 ? 0 : 1)) * stagRowLength + (rowNumber2Temp / 2) * alignedRowLength; if (count >= maxCount) { rowNumber1 = rowNumber1Temp; rowNumber2 = rowNumber2Temp; } } actualWidth = diameter + (rowNumber1 - 1) * diameter + radius * Math.Sqrt(3.0) * rowNumber2; return(true); }
public List <Layer2DCylImp> BuildLayers( double radius, double height , Vector2D dimContainer , double offsetZ /* e.g. pallet height */ , ConstraintSetAbstract constraintSet , bool keepOnlyBest) { var listLayers0 = new List <Layer2DCylImp>(); foreach (LayerPatternCyl pattern in LayerPatternCyl.All) { // not swapped vs swapped pattern for (int iSwapped = 0; iSwapped < 2; ++iSwapped) { // does swapping makes sense for this layer pattern ? if (!pattern.CanBeSwapped && (iSwapped == 1)) { continue; } // instantiate layer var layer = new Layer2DCylImp(radius, height, dimContainer, iSwapped == 1); layer.PatternName = pattern.Name; double actualLength = 0.0, actualWidth = 0.0; if (!pattern.GetLayerDimensions(layer, out actualLength, out actualWidth)) { continue; } pattern.GenerateLayer(layer, actualLength, actualWidth); listLayers0.Add(layer); } // keep only best layers if (keepOnlyBest) { // 1. get best count int bestCount = 0; foreach (Layer2DCylImp layer in listLayers0) { bestCount = Math.Max(layer.CountInHeight(constraintSet.OptMaxHeight.Value - offsetZ), bestCount); } // 2. remove any layer that does not match the best count given its orientation listLayers0.RemoveAll(layer => !(layer.CountInHeight(constraintSet.OptMaxHeight.Value - offsetZ) >= bestCount)); } listLayers0.Sort(new LayerCylComparerCount(constraintSet.OptMaxHeight.Value - offsetZ)); } return(listLayers0); }
/// <summary> /// Used to compute load dimension /// </summary> public bool GetDimensions(List <LayerDesc> layers, Packable packable, Vector2D dimContainer, double minSpace, ref Vector2D actualDimensions) { foreach (LayerDesc layerDesc in layers) { // dimensions double actualLength = 0.0, actualWidth = 0.0; if (packable.IsBrick) { LayerDescBox layerDescBox = layerDesc as LayerDescBox; // instantiate layer var layer = new Layer2DBrickImp(packable.OuterDimensions, dimContainer, layerDescBox.PatternName, layerDescBox.AxisOrtho, layerDesc.Swapped) { ForcedSpace = minSpace }; // get layer pattern LayerPatternBox pattern = LayerPatternBox.GetByName(layerDesc.PatternName); // dimensions if (!pattern.GetLayerDimensionsChecked(layer, out actualLength, out actualWidth)) { _log.Error(string.Format("Failed to get layer dimension : {0}", pattern.Name)); break; } } else if (packable.IsCylinder) { var cylProp = packable as RevSolidProperties; // instantiate layer var layer = new Layer2DCylImp(cylProp.RadiusOuter, cylProp.Height, dimContainer, layerDesc.Swapped); // get layer pattern LayerPatternCyl pattern = LayerPatternCyl.GetByName(layerDesc.PatternName); // dimensions if (!pattern.GetLayerDimensions(layer, out actualLength, out actualWidth)) { _log.Error(string.Format("Failed to get layer dimension : {0}", pattern.Name)); break; } } else { throw new EngineException(string.Format("Unexpected packable {0} (Type = {1})", packable.Name, packable.GetType().ToString())); } actualDimensions.X = Math.Max(actualDimensions.X, layerDesc.Swapped ? actualWidth : actualLength); actualDimensions.Y = Math.Max(actualDimensions.Y, layerDesc.Swapped ? actualLength : actualWidth); } return(true); }
public ILayer2D BuildLayer(Packable packable, Vector2D dimContainer, LayerDesc layerDesc, double minSpace) { ILayer2D layer = null; if (packable.IsBrick) { if (layerDesc is LayerDescBox layerDescBox) { // layer instantiation layer = new Layer2DBrickImp(packable.OuterDimensions, dimContainer, layerDesc.PatternName, layerDescBox.AxisOrtho, layerDesc.Swapped) { ForcedSpace = minSpace }; // get layer pattern LayerPatternBox pattern = LayerPatternBox.GetByName(layerDesc.PatternName); // dimensions if (!pattern.GetLayerDimensionsChecked(layer as Layer2DBrickImp, out double actualLength, out double actualWidth)) { return(null); } pattern.GenerateLayer( layer as Layer2DBrickImp , actualLength , actualWidth); } return(layer); } else if (packable.IsCylinder) { // casts var cylProperties = packable as RevSolidProperties; // layer instantiation layer = new Layer2DCylImp(cylProperties.RadiusOuter, cylProperties.Height, dimContainer, layerDesc.Swapped); // get layer pattern LayerPatternCyl pattern = LayerPatternCyl.GetByName(layerDesc.PatternName); if (!pattern.GetLayerDimensions(layer as Layer2DCylImp, out double actualLength, out double actualWidth)) { return(null); } pattern.GenerateLayer(layer as Layer2DCylImp, actualLength, actualWidth); } else { throw new EngineException(string.Format("Unexpected packable {0} (Type = {1})", packable.Name, packable.GetType().ToString())); } return(layer); }
public void Draw(Graphics3D graphics, Packable packable, double height, bool selected, bool annotate) { graphics.BackgroundColor = selected ? Color.LightBlue : Color.White; graphics.CameraPosition = Graphics3D.Corner_0; // draw layer (brick) if (_layer is Layer2DBrick layer2D) { uint pickId = 0; foreach (var bPosition in layer2D.Positions) { if (packable is PackProperties) { graphics.AddBox(new Pack(pickId++, packable as PackProperties, bPosition)); } else if (packable is PackableBrick) { graphics.AddBox(new Box(pickId++, packable as PackableBrick, bPosition)); } } } // draw layer (cylinder) else if (_layer is Layer2DCylImp) { Layer2DCylImp layer2DCyl = _layer as Layer2DCylImp; uint pickId = 0; foreach (Vector2D pos in layer2DCyl) { Cyl cyl = null; if (packable is CylinderProperties cylProperties) { cyl = new Cylinder(pickId++, cylProperties, new CylPosition(new Vector3D(pos.X, pos.Y, 0.0), HalfAxis.HAxis.AXIS_Z_P)); } else if (packable is BottleProperties bottleProperties) { cyl = new Bottle(pickId++, bottleProperties, new CylPosition(new Vector3D(pos.X, pos.Y, 0.0), HalfAxis.HAxis.AXIS_Z_P)); } graphics.AddCylinder(cyl); } } graphics.Flush(); // annotate thumbnail if (annotate) { Annotate(graphics.Graphics, graphics.Size, height); } }
public override bool GetLayerDimensions(ILayer2D layer, out double actualLength, out double actualWidth) { double palletLength = layer.Length; double palletWidth = layer.Width; Layer2DCylImp layerCyl = layer as Layer2DCylImp; double radius = layerCyl.Radius; int alignedRowLength = 0, stagRowLength = 0; int rowNumber1 = 0, rowNumber2 = 0; ComputeRowNumberAndLength(layerCyl , out alignedRowLength, out rowNumber1 , out stagRowLength, out rowNumber2 , out actualLength, out actualWidth); return(rowNumber1 > 0 && rowNumber2 > 0); }
public override void GenerateLayer(ILayer2D layer, double actualLength, double actualWidth) { layer.Clear(); double palletLength = GetPalletLength(layer); double palletWidth = GetPalletWidth(layer); double radius = GetRadius(layer); double diameter = 2.0 * radius; int alignedRowLength = 0, stagRowLength = 0; int rowNumber1 = 0, rowNumber2 = 0; Layer2DCylImp layerCyl = layer as Layer2DCylImp; ComputeRowNumberAndLength(layerCyl , out alignedRowLength, out rowNumber1 , out stagRowLength, out rowNumber2 , out actualLength, out actualWidth); double offsetX = 0.5 * (palletLength - actualLength); double offsetY = 0.5 * (palletWidth - actualWidth); for (int j = 0; j < rowNumber1; ++j) { for (int i = 0; i < alignedRowLength; ++i) { AddPosition(layerCyl, new Vector2D( radius + offsetX + i * diameter , radius + offsetY + j * diameter )); } } for (int i = 0; i < rowNumber2; ++i) { double y = radius + offsetY + (rowNumber1 - 1.0) * diameter + (i + 1) * radius * Math.Sqrt(3.0); for (int j = 0; j < (i % 2 == 0 ? stagRowLength : alignedRowLength); ++j) { AddPosition(layer, new Vector2D(offsetX + ((i % 2 != 0) ? 0.0 : radius) + j * 2.0 * radius + radius, y)); } } }
public List <ILayer2D> BuildLayers( Packable packable, Vector2D dimContainer, double offsetZ, /* e.g. pallet height */ ConstraintSetAbstract constraintSet, bool keepOnlyBest) { var listLayers0 = new List <ILayer2D>(); if (packable is PackableBrick packableBrick) { // loop through all patterns foreach (LayerPatternBox pattern in LayerPatternBox.All) { // loop through all orientation HalfAxis.HAxis[] patternAxes = pattern.IsSymetric ? HalfAxis.Positives : HalfAxis.All; foreach (HalfAxis.HAxis axisOrtho in patternAxes) { // is orientation allowed if (!constraintSet.AllowOrientation(Layer2DBrickImp.VerticalAxis(axisOrtho))) { continue; } // not swapped vs swapped pattern for (int iSwapped = 0; iSwapped < 2; ++iSwapped) { try { // does swapping makes sense for this layer pattern ? if (!pattern.CanBeSwapped && (iSwapped == 1)) { continue; } // instantiate layer var layer = new Layer2DBrickImp(packableBrick.OuterDimensions, dimContainer, pattern.Name, axisOrtho, iSwapped == 1) { ForcedSpace = constraintSet.MinimumSpace.Value }; if (layer.NoLayers(constraintSet.OptMaxHeight.Value) < 1) { continue; } if (!pattern.GetLayerDimensionsChecked(layer, out double actualLength, out double actualWidth)) { continue; } pattern.GenerateLayer(layer, actualLength, actualWidth); if (0 == layer.Count) { continue; } listLayers0.Add(layer); } catch (Exception ex) { _log.ErrorFormat("Pattern: {0} Orient: {1} Swapped: {2} Message: {3}" , pattern.Name , axisOrtho.ToString() , iSwapped == 1 ? "True" : "False" , ex.Message); } } } } } else if (packable is CylinderProperties cylinder) { // loop through all patterns foreach (LayerPatternCyl pattern in LayerPatternCyl.All) { // not swapped vs swapped pattern for (int iSwapped = 0; iSwapped < 2; ++iSwapped) { try { var layer = new Layer2DCylImp(cylinder.RadiusOuter, cylinder.Height, dimContainer, iSwapped == 1) { PatternName = pattern.Name }; if (!pattern.GetLayerDimensions(layer as Layer2DCylImp, out double actualLength, out double actualWidth)) { continue; } pattern.GenerateLayer(layer as Layer2DCylImp, actualLength, actualWidth); if (0 == layer.Count) { continue; } listLayers0.Add(layer); } catch (Exception ex) { _log.ErrorFormat("Pattern: {0} Swapped: {1} Message: {2}" , pattern.Name , iSwapped == 1 ? "True" : "False" , ex.Message); } } } } // keep only best layers if (keepOnlyBest) { // 1. get best count int bestCount = 0; foreach (ILayer2D layer in listLayers0) { bestCount = Math.Max(layer.CountInHeight(constraintSet.OptMaxHeight.Value - offsetZ), bestCount); } // 2. remove any layer that does not match the best count given its orientation var listLayers1 = new List <ILayer2D>(); foreach (ILayer2D layer in listLayers0) { if (layer.CountInHeight(constraintSet.OptMaxHeight.Value - offsetZ) >= bestCount) { listLayers1.Add(layer); } } // 3. copy back in original list listLayers0.Clear(); listLayers0.AddRange(listLayers1); } if (constraintSet.OptMaxHeight.Activated) { listLayers0.Sort(new LayerComparerCount(constraintSet, offsetZ)); } return(listLayers0); }
public override void GenerateLayer(ILayer2D layer, double actualLength, double actualWidth) { layer.Clear(); double length = GetPalletLength(layer); double width = GetPalletWidth(layer); double diameter = GetDiameter(layer); double radius = 0.5 * diameter; Layer2DCylImp layerCyl = layer as Layer2DCylImp; if (!ComputeRowNumberAndLength(layerCyl , out int number , out int firstRowLength, out int secondRowLength, out int rowNumber , out actualLength, out actualWidth)) { return; } double offsetX = 0.0; //0.5 * (length - actualLength); double offsetY = 0.5 * (width - actualWidth); if (length < diameter || width < diameter) { } else if (width < 2 * diameter) { double Y = width - diameter; double X = diameter * Math.Cos(Math.Asin(Y / diameter)); double stepX = X; if (X <= 0.5 * diameter) { stepX = diameter; } else { stepX = 2 * X; } double xpos = 0.5 * diameter, ypos = width - 0.5 * diameter; Vector2D pLast = Vector2D.Zero; while (offsetX + xpos < length - 0.5 * diameter) { Vector2D p0 = new Vector2D(offsetX + xpos, offsetY + ypos); Vector2D p1 = new Vector2D(offsetX + xpos + X, offsetY + 0.5 * diameter); AddPosition(layer, p0); if (p1.X < length - 0.5 * diameter) { AddPosition(layer, p1); } pLast = 0.5 * (p0 + p1) + 0.5 * Math.Sqrt(3) * new Vector2D(Y, X); xpos += stepX; } if (pLast.X <= length - 0.5 * diameter && pLast.Y < width - 0.5 * diameter) { AddPosition(layer, pLast); } } else if (width < 3 * diameter) { double Y = width - 2 * diameter; double X = 0.0; double ydiff = 0.0; if (Y >= diameter / 2) { Y = (width - diameter) / 3; X = diameter * Math.Cos(Math.Asin(Y / diameter)); ydiff = -2.0 * Y; } else { Y = width - 2 * diameter; X = diameter * Math.Cos(Math.Asin(Y / diameter)); ydiff = -diameter; } // initialize positions double xpos = 0.5 * diameter, ypos = 0.0; int index = 0; Vector2D pLast = Vector2D.Zero; // add positions until end is reached while (xpos < length - 0.5 * diameter) { ypos = width - 0.5 * diameter - ((index % 2 == 1) ? Y : 0.0); AddPosition(layer, new Vector2D(offsetX + xpos, offsetY + ypos)); AddPosition(layer, new Vector2D(offsetX + xpos, offsetY + ypos + ydiff)); pLast = new Vector2D(offsetX + xpos + 0.5 * Math.Sqrt(3.0) * diameter, offsetY + ypos + 0.5 * ydiff); xpos += X; ++index; } if (pLast.X <= length - 0.5 * diameter) { AddPosition(layer, pLast); } } else { for (int i = 0; i < rowNumber; ++i) { double y = (offsetY + radius) + i * radius * Math.Sqrt(3.0); for (int j = 0; j < (i % 2 == 0 ? firstRowLength : secondRowLength); ++j) { AddPosition(layer, new Vector2D(offsetX + ((i % 2 == 0) ? 0.0 : radius) + j * 2.0 * radius + radius, y)); } } } }
private bool ComputeRowNumberAndLength(Layer2DCylImp layer , out int number , out int firstRowLength, out int secondRowLength, out int rowNumber , out double actualLength, out double actualWidth) { double length = GetPalletLength(layer); double width = GetPalletWidth(layer); double diameter = GetDiameter(layer); number = 0; rowNumber = 0; firstRowLength = 0; secondRowLength = 0; actualLength = 0.0; actualWidth = 0.0; // sanity check if (length < diameter || width < diameter) { number = 0; rowNumber = 0; firstRowLength = 0; secondRowLength = 0; actualLength = 0.0; actualWidth = 0.0; return(false); } else if (width < 2 * diameter) { double Y = width - diameter; double X = diameter * Math.Cos(Math.Asin(Y / diameter)); if (X <= diameter / 2.0) { int noCols = (int)(Math.Floor((length - diameter - X)) / diameter) + 1; number = 2 * (int)(Math.Floor((length - diameter - X)) / diameter) + 2; bool lastRoll = length - 0.5 * diameter * number - diameter * Math.Cos((Math.PI / 6.0) - Math.Asin(width - diameter / diameter)) > 0.0; number += lastRoll ? 1 : 0; actualLength = noCols * diameter + X + (lastRoll ? diameter * Math.Cos((Math.PI / 6.0) + Math.Atan(X / Y)) : 0.0); firstRowLength = secondRowLength = noCols; } else { number = (int)Math.Floor((length - diameter) / X) + 1; actualLength = (number - 1) * X + diameter; firstRowLength = number / 2; secondRowLength = number / 2 + number % 2; } actualWidth = width; rowNumber = 2; } else if (width < 3.0 * diameter) { double Y = width - 2.0 * diameter; if (Y >= diameter / 2.0) { Y = (width - diameter) / 3.0; double X = diameter * Math.Cos(Math.Asin(Y / diameter)); number = 2 * (int)Math.Floor(length / X); actualLength = ((number / 2) - 1) * X + diameter; firstRowLength = number / 4; secondRowLength = number / 4 + (number / 2) % 2; } else { Y = width - 2.0 * diameter; double X = diameter * Math.Cos(Math.Asin(Y / diameter)); int noCols = (int)Math.Floor(length / X); number = 2 * (int)Math.Floor(length / X); bool lastRoll = length - (X * number / 2.0) - diameter * Math.Cos(Math.PI / 6.0) > 0; number += lastRoll ? 1 : 0; actualLength = (noCols - 1) * X + diameter * (1.0 + Math.Asin(Math.PI / 6.0)); firstRowLength = noCols; secondRowLength = noCols + number % 2; } rowNumber = 4; actualWidth = width; } else { // first row number firstRowLength = (int)Math.Floor(length / diameter); // second row if ((firstRowLength + 0.5) * diameter < length) { secondRowLength = firstRowLength; actualLength = (firstRowLength + 0.5) * diameter; } else { secondRowLength = firstRowLength - 1; actualLength = firstRowLength * diameter; } // numbers of rows rowNumber = (int)Math.Floor(1 + (2.0 * width / diameter - 2.0) / Math.Sqrt(3.0)); actualWidth = (2.0 + (rowNumber - 1) * Math.Sqrt(3.0)) * 0.5 * diameter; } return(true); }