private void btnGarbage2_Click(object sender, RoutedEventArgs e) { try { if (_prevSketches.Count == 0) { MessageBox.Show("Need stored images first", this.Title, MessageBoxButton.OK, MessageBoxImage.Warning); return; } // Pick a random sketch var sketch = StaticRandom.NextItem(_prevSketches); Color[] colors = sketch.BitmapColors.GetColors(0, 0, sketch.Bitmap.PixelWidth, sketch.Bitmap.PixelHeight); Color[] inverseColors = colors. Select(o => { bool isBlack = (o.A > 200 && Math1D.Avg(o.R, o.G, o.B) < 20); // Invert the color return(isBlack ? Colors.Transparent : Colors.Black); }). ToArray(); } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
private void AddToTab(PartDesignBase part) { var key = new Tuple <PartToolItemBase, PartDesignBase>(part.GetToolItem(), part); _tabParts.Add(key); if (_tabName == null) { _tabName = key.Item1.TabName; } else if (_tabName != key.Item1.TabName) { throw new ArgumentException(string.Format("parts span tabNames, not sure how to handle that: \"{0}\", \"{1}\"", _tabName, key.Item1.TabName)); } int tabIndex = FindOrCreateTab(key); // The standard 2D element is pretty basic, add a tooltip FrameworkElement asFramework = key.Item1.Visual2D as FrameworkElement; if (asFramework != null && asFramework.ToolTip == null) { asFramework.ToolTip = new TextBlock() { Text = string.Format("volume {0}", Math1D.Avg(part.Scale.X, part.Scale.Y, part.Scale.Z).ToStringSignificantDigits(3)), }; } _tabStats[tabIndex].Item2.Children.Add(key.Item1.Visual2D); }
/// <summary> /// The hopfield can only store booleans. So low and high tell how to map from external to local values /// </summary> /// <param name="midValue"> /// If left null, this will just be the value between low and high. /// But if you want to exaggerate lows, make this larger than that average (because all values less than this go low). Or the /// other way to exaggerate highs. /// </param> public Hopfield(int nodeCount, double lowValue, double highValue, double?midValue = null) { _nodeCount = nodeCount; _lowValue = lowValue; _highValue = highValue; _midValue = midValue ?? Math1D.Avg(lowValue, highValue); }
public static decimal GetCredits_ShipPart(ShipPartDNA dna) { decimal baseAmt = GetCredits_ShipPart_Base(dna.PartType ?? ""); decimal scale = Convert.ToDecimal(Math1D.Avg(dna.Scale.X, dna.Scale.Y, dna.Scale.Z)); return(baseAmt * scale); }
public EncogOCR_StrokeDefinition(Stroke stroke) { this.Points = (stroke.DrawingAttributes.FitToCurve ? stroke.GetBezierStylusPoints() : stroke.StylusPoints). Select(o => new Point(o.X, o.Y)). ToArray(); this.Color = stroke.DrawingAttributes.Color.ToHex(); this.Thickness = Math1D.Avg(stroke.DrawingAttributes.Width, stroke.DrawingAttributes.Height); }
public void GenerateBitmap(int imageSize) { this.Bitmap = DrawStrokes(this.Strokes, imageSize, imageSize, this.InkCanvasSize.Width, this.InkCanvasSize.Height); this.BitmapColors = UtilityWPF.ConvertToColorArray(this.Bitmap, true, Colors.Transparent); this.NNInput = this.BitmapColors.GetColors(0, 0, imageSize, imageSize). Select(o => (o.A / 255d) * (255d - Math1D.Avg(o.R, o.G, o.B)) / 255d). // 0 should be 1d, 255 should be 0d ToArray(); }
private void btnGarbage_Click(object sender, RoutedEventArgs e) { try { if (_prevSketches.Count == 0) { MessageBox.Show("Need stored images first", this.Title, MessageBoxButton.OK, MessageBoxImage.Warning); return; } #region Drawing attrib // This is a percent of the canvas's size (each sketch could come from a different size canvas) double avgStroke = _prevSketches. Select(o => new { CanvasSize = Math1D.Avg(o.InkCanvasSize.Width, o.InkCanvasSize.Height), Sizes = o.Strokes.Select(p => p.Thickness) }). Select(o => o.Sizes.Select(p => p / o.CanvasSize)). SelectMany(o => o). Average(); // Now figure out the stroke size for this current canvas double strokeSize = Math1D.Avg(canvasInk.ActualWidth, canvasInk.ActualHeight); strokeSize = avgStroke * strokeSize; DrawingAttributes attrib = new DrawingAttributes() { Width = strokeSize, Height = strokeSize, Color = Colors.Black, FitToCurve = true, IsHighlighter = false, StylusTip = StylusTip.Ellipse, StylusTipTransform = Transform.Identity.Value, }; #endregion StrokeCollection strokes = new StrokeCollection(); Point[] points = new[] { new Point(0, 0), new Point(canvasInk.ActualWidth, canvasInk.ActualHeight), }; strokes.Add(new Stroke(new StylusPointCollection(points), attrib)); // Show the drawing canvasInk.Strokes.Clear(); canvasInk.Strokes.Add(strokes); } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
public Inventory(ShipPartDNA part) { this.Ship = null; this.Part = part; this.Mineral = null; this.Count = 1; this.Volume = Math1D.Avg(part.Scale.X, part.Scale.Y, part.Scale.Z); this.Mass = 1; this.Token = TokenGenerator.NextToken(); }
private static BitmapSource DrawStrokes(IEnumerable <EncogOCR_StrokeDefinition> strokes, int width, int height, double origWidth, double origHeight) { //NOTE: InkCanvas draws lines smoother than this method. This is a crude way to approximate the thickness/darkeness of the lines double scale = Math1D.Avg(width, height) / Math1D.Avg(origWidth, origHeight); double reduce = 1; if (scale < .2) { reduce = UtilityCore.GetScaledValue(.8, 1, .05, .2, scale); } RenderTargetBitmap retVal = new RenderTargetBitmap(width, height, UtilityWPF.DPI, UtilityWPF.DPI, PixelFormats.Pbgra32); DrawingVisual dv = new DrawingVisual(); using (DrawingContext ctx = dv.RenderOpen()) { foreach (EncogOCR_StrokeDefinition stroke in strokes) { SolidColorBrush brush = new SolidColorBrush(UtilityWPF.ColorFromHex(stroke.Color)); if (stroke.Points.Length == 1) { // Single Point double radius = stroke.Thickness / 2d; radius *= reduce; ctx.DrawEllipse(brush, null, stroke.Points[0], radius, radius); } else { // Multiple Points Pen pen = new Pen(brush, stroke.Thickness * reduce) { StartLineCap = PenLineCap.Round, EndLineCap = PenLineCap.Round, }; for (int cntr = 0; cntr < stroke.Points.Length - 1; cntr++) { ctx.DrawLine(pen, stroke.Points[cntr], stroke.Points[cntr + 1]); } } } } dv.Transform = new ScaleTransform(width / origWidth, height / origHeight); retVal.Render(dv); return(retVal); }
/// <summary> /// This gets the raw values that will be used for RGB (could return negative) /// </summary> private static Tuple <double, double, double> GetNodeColor_RGB(VectorND nodeWeights) { int size = nodeWeights.Size; if (size == 0) { return(Tuple.Create(0d, 0d, 0d)); } else if (size == 3) { return(Tuple.Create(nodeWeights[0], nodeWeights[1], nodeWeights[2])); } else if (size < 3) { double left = nodeWeights[0]; double right = nodeWeights[size - 1]; return(Tuple.Create(left, Math1D.Avg(left, right), right)); } int div = size / 3; int rem = size % 3; // Start them all off with the same amount int[] widths = Enumerable.Range(0, 3). Select(o => div). ToArray(); // Randomly distribute the remainder foreach (int index in UtilityCore.RandomRange(0, 3, rem)) { widths[index]++; } double r = nodeWeights. Take(widths[0]). Average(); double g = nodeWeights. Skip(widths[0]). Take(widths[1]). Average(); double b = nodeWeights. Skip(widths[0] + widths[1]). Average(); return(Tuple.Create(r, g, b)); }
public FitnessInfo?EvaluateTick(double elapedTime) { //System.Diagnostics.Debug.WriteLine(Guid.NewGuid().ToString()); if (!_isEvaluating) { // This is called after the score has been given, but before a new phenome is assigned throw new InvalidOperationException("EvaluateTick was called at an invalid time. Either StartNewEvaluation was never called, or the current phenome has returned a final score and this class is waiting for a new call to StartNewEvaluation"); } _room.Bot.RefillContainers(); //TODO: May want to give a 1 second warmup before evaluating var distance = GetDistanceError(_room.Bot, _room.Center, _minDistance, _maxDistance, _maxDistance2); var speed = GetSpeedError(_room.Bot, _minSpeed, _maxSpeed, _maxSpeed2); // Combine errors //TODO: Have multipliers so that distance could be weighted differently than speed - be sure the final value is normalized to 0 to 1 double error = Math1D.Avg(distance.error, speed.error); double actualElapsed = EvaluatorUtil.GetActualElapsedTime(ref _runningTime, elapedTime, _maxEvalTime); // Add the error to the total (cap it if time is exceeded) error *= actualElapsed; _error += error; _snapshots.Add(new BotTrackingEntry_Eval3(_runningTime, _room.Bot)); if (distance.distance > _maxDistance2) { // They went outside the allowed area. Quit early and give maximum error for the remainder of the time _error += (_maxEvalTime - _runningTime); _runningTime = _maxEvalTime; return(EvaluatorUtil.FinishEvaluation(ref _isEvaluating, _runningTime, _error, _maxEvalTime, _log, _room, _snapshots.ToArray())); } else if (_runningTime >= _maxEvalTime) { // If the counter is a certain amount, return the final score // It's been tested long enough. Return the score (score needs to grow, so an error of zero will give max score) return(EvaluatorUtil.FinishEvaluation(ref _isEvaluating, _runningTime, _error, _maxEvalTime, _log, _room, _snapshots.ToArray())); } else { // Still evaluating return(null); } }
internal static CollisionHull CreateSensorCollisionHull(WorldBase world, Vector3D scale, Quaternion orientation, Point3D position) { Transform3DGroup transform = new Transform3DGroup(); //transform.Children.Add(new ScaleTransform3D(scale)); // it ignores scale transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 90))); // the physics hull is along x, but dna is along z transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(orientation))); transform.Children.Add(new TranslateTransform3D(position.ToVector())); // Scale X and Y should be identical, but average them to be safe double radius = SIZEPERCENTOFSCALE_XY * Math1D.Avg(scale.X, scale.Y) * .5; // multiplying by .5 to turn diameter into radius double height = SIZEPERCENTOFSCALE_Z * scale.Z; return(CollisionHull.CreateCylinder(world, 0, radius, height, transform.Value)); }
public Cargo_ShipPart(ShipPartDNA dna, ItemOptions options, EditorOptions editorOptions) : base(CargoType.ShipPart) { PartDesignBase part = BotConstructor.GetPartDesign(dna, editorOptions, true); //TODO: This is really ineficient, let design calculate it for real //TODO: Volume and Mass should be calculated by the design class (add to PartBase interface) var aabb = Math3D.GetAABB(UtilityWPF.GetPointsFromMesh(part.Model)); this.Volume = (aabb.Item2.X - aabb.Item1.X) * (aabb.Item2.Y - aabb.Item1.Y) * (aabb.Item2.Y - aabb.Item1.Y); //TODO: Let the design class return this (expose a property called DryDensity) this.Density = Math1D.Avg(options.Thruster_Density, options.Sensor_Density); this.PartDNA = dna; }
private static Neuron_SensorPosition[] CreateNeurons(ShipPartDNA dna, ItemOptions itemOptions, double neuronDensity, bool ignoreSetValue) { #region calculate counts // Figure out how many to make //NOTE: This radius isn't taking SCALE into account. The other neural parts do this as well, so the neural density properties can be more consistent double radius = Math1D.Avg(dna.Scale.X, dna.Scale.Y, dna.Scale.Z) / 2d; // dividing by 2, because radius is wanted, not diameter double area = Math.Pow(radius, itemOptions.Sensor_NeuronGrowthExponent); int neuronCount = (neuronDensity * area).ToInt_Ceiling(); neuronCount = Math.Max(neuronCount, 10); #endregion // Place them evenly on the surface of a sphere Vector3D[] positions = NeuralUtility.GetNeuronPositions_SphericalShell_Even(dna.Neurons, neuronCount, radius); return(positions. Select(o => new Neuron_SensorPosition(o.ToPoint(), true, ignoreSetValue)). ToArray()); }
public SwarmBay(EditorOptions options, ItemOptions itemOptions, ShipPartDNA dna, Map map, World world, int material_SwarmBot, IContainer plasma, SwarmObjectiveStrokes strokes) : base(options, dna, itemOptions.SwarmBay_Damage.HitpointMin, itemOptions.SwarmBay_Damage.HitpointSlope, itemOptions.SwarmBay_Damage.Damage) { _itemOptions = itemOptions; _map = map; _world = world; _material_SwarmBot = material_SwarmBot; _plasma = plasma; _strokes = strokes; this.Design = new SwarmBayDesign(options, true); this.Design.SetDNA(dna); double volume, radius; GetMass(out _mass, out volume, out radius, out _scaleActual, dna, itemOptions); //this.Radius = radius; _timeBetweenBots = StaticRandom.NextPercent(itemOptions.SwarmBay_BirthRate, .1); int maxCount = (itemOptions.SwarmBay_MaxCount * Math1D.Avg(dna.Scale.X, dna.Scale.Y, dna.Scale.Z)).ToInt_Round(); if (maxCount < 0) { maxCount = 1; } _maxBots = maxCount; _plasmaTankThreshold = itemOptions.SwarmBay_Birth_TankThresholdPercent; _birthCost = itemOptions.SwarmBay_BirthCost; _birthRadius = itemOptions.SwarmBay_BirthSize / 2d; if (_map != null) { _map.ItemRemoved += Map_ItemRemoved; } this.Destroyed += SwarmBay_Destroyed; }
private Visual3D GetAsteroidBlip(Asteroid asteroid) { if (asteroid.Radius < 2) { // No need to flood the map with tiny asteroids return(null); } // Material MaterialGroup materials = new MaterialGroup(); materials.Children.Add(new DiffuseMaterial(Brushes.DimGray)); //materials.Children.Add(new SpecularMaterial(Brushes.White, 20d)); // Geometry Model GeometryModel3D geometry = new GeometryModel3D(); geometry.Material = materials; geometry.BackMaterial = materials; geometry.Geometry = _blipGeometry_Circle.Value; double[] astRad = new[] { asteroid.RadiusVect.X, asteroid.RadiusVect.Y, asteroid.RadiusVect.Z }.OrderByDescending(o => o).ToArray(); double avgRad = Math1D.Avg(asteroid.RadiusVect.X, asteroid.RadiusVect.Y, asteroid.RadiusVect.Z); Transform3DGroup transform = new Transform3DGroup(); transform.Children.Add(new ScaleTransform3D(astRad[0] * 2.2, astRad[2] * 2.2, astRad[1] * .5)); transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), StaticRandom.NextDouble(360)))); geometry.Transform = transform; // Model Visual ModelVisual3D retVal = new ModelVisual3D(); retVal.Content = geometry; // Exit Function return(retVal); }
public Tuple <decimal, decimal> GetPrice_Base(Inventory inventory) { if (inventory.Ship != null) { Tuple <decimal, decimal>[] prices = inventory.Ship.PartsByLayer. SelectMany(o => o.Value). Select(o => { decimal standPrice = ItemOptionsAstMin2D.GetCredits_ShipPart(o); decimal mult1 = FindPriceMult(o.PartType); return(Tuple.Create(standPrice, mult1)); }). ToArray(); // Get the weighted percent (taking this % times the sum will be the same as taking the sum of each individual price*%) double mult2 = Math1D.Avg(prices.Select(o => Tuple.Create(Convert.ToDouble(o.Item2), Convert.ToDouble(o.Item1))).ToArray()); // Make a completed ship more expensive than just parts return(Tuple.Create(prices.Sum(o => o.Item1) * 1.5m, Convert.ToDecimal(mult2))); } else if (inventory.Part != null) { decimal standPrice = ItemOptionsAstMin2D.GetCredits_ShipPart(inventory.Part); decimal mult = FindPriceMult(inventory.Part.PartType); return(Tuple.Create(standPrice, mult)); } else if (inventory.Mineral != null) { //return ItemOptionsAstMin2D.GetCredits_Mineral(inventory.Mineral.MineralType, inventory.Mineral.Volume); decimal mult = FindPriceMult(inventory.Mineral.MineralType); return(Tuple.Create(inventory.Mineral.Credits, mult)); } else { throw new ApplicationException("Unknown type of inventory"); } }
private static int GetResize(int[] itemDimensions) { // Ideal sizes: // 2D=7x7 int retVal = -1; switch (itemDimensions.Length) { case 1: #region 1D //TODO: This is just a rough guess at an ideal range. When there are real scenarios, adjust as necessary // I don't think the s curve makes sense here. It's too fuzzy, and has the potential to enlarge when that's not wanted //double scaled = Math1D.PositiveSCurve(itemDimensions[0], 50); //retVal = UtilityCore.GetScaledValue(5, 50, 0, 50, scaled).ToInt_Round(); if (itemDimensions[0] < 5) { retVal = 5; } else if (itemDimensions[0] <= 50) { retVal = itemDimensions[0]; } else { retVal = 50; } #endregion break; case 2: #region 2D double avg = Math1D.Avg(itemDimensions[0], itemDimensions[1]); if (avg < 3) { retVal = 3; } else if (avg <= 7) { retVal = avg.ToInt_Round(); } else { retVal = 7; } #endregion break; default: // The convolutions class will need to be expanded first throw new ApplicationException("TODO: Handle more than 2 dimensions"); } return(retVal); }
private void btnGarbage3_Click(object sender, RoutedEventArgs e) { try { if (_prevSketches.Count == 0) { MessageBox.Show("Need stored images first", this.Title, MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (_prevSketches.Select(o => o.Bitmap.PixelWidth).Distinct().Count() != 1 || _prevSketches.Select(o => o.Bitmap.PixelHeight).Distinct().Count() != 1) { MessageBox.Show("Stored images are different sizes", this.Title, MessageBoxButton.OK, MessageBoxImage.Warning); return; } Color[][] colors = _prevSketches. Select(o => o.BitmapColors.GetColors(0, 0, o.Bitmap.PixelWidth, o.Bitmap.PixelHeight)). ToArray(); bool[] isBlack = Enumerable.Range(0, colors[0].Length). AsParallel(). Select(o => new { Index = o, IsBlack = colors.Any(p => p[o].A > 200 && Math1D.Avg(p[o].R, p[o].G, p[o].B) < 20) }). OrderBy(o => o.Index). Select(o => o.IsBlack). ToArray(); bool[] inverted = isBlack. Select(o => !o). ToArray(); //canvasPixels.Source = GetBitmap(isBlack); canvasPixels.Source = GetBitmap(inverted); } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
//TODO: random chance of randomly placing metal fittings on the handle private static void GetModel_Rod_Wood(Model3DGroup geometries, WeaponHandleDNA dna, WeaponHandleDNA finalDNA, WeaponMaterialCache materials) { const double PERCENT = 1; Random rand = StaticRandom.GetRandomForThread(); var from = dna.KeyValues; var to = finalDNA.KeyValues; GeometryModel3D geometry = new GeometryModel3D(); #region material switch (dna.HandleMaterial) { case WeaponHandleMaterial.Soft_Wood: geometry.Material = materials.Handle_SoftWood; break; case WeaponHandleMaterial.Hard_Wood: geometry.Material = materials.Handle_HardWood; break; default: throw new ApplicationException("Unexpected WeaponHandleMaterial: " + dna.HandleMaterial.ToString()); } geometry.BackMaterial = geometry.Material; #endregion #region tube double maxX1 = WeaponDNA.GetKeyValue("maxX1", from, to, rand.NextDouble(.45, .7)); double maxX2 = WeaponDNA.GetKeyValue("maxX2", from, to, rand.NextDouble(.45, .7)); double maxY1 = WeaponDNA.GetKeyValue("maxY1", from, to, rand.NextDouble(.85, 1.05)); double maxY2 = WeaponDNA.GetKeyValue("maxY2", from, to, rand.NextDouble(.85, 1.05)); List <TubeRingBase> rings = new List <TubeRingBase>(); rings.Add(new TubeRingRegularPolygon(0, false, maxX1 * .45, maxY1 * .75, true)); rings.Add(new TubeRingRegularPolygon(WeaponDNA.GetKeyValue("ring1", from, to, rand.NextPercent(.5, PERCENT)), false, maxX1 * .5, maxY1 * 1, false)); rings.Add(new TubeRingRegularPolygon(WeaponDNA.GetKeyValue("ring2", from, to, rand.NextPercent(2, PERCENT)), false, maxX1 * .4, maxY1 * .8, false)); rings.Add(new TubeRingRegularPolygon(WeaponDNA.GetKeyValue("ring3", from, to, rand.NextPercent(5, PERCENT)), false, Math1D.Avg(maxX1, maxX2) * .35, Math1D.Avg(maxY1, maxY2) * .75, false)); rings.Add(new TubeRingRegularPolygon(WeaponDNA.GetKeyValue("ring4", from, to, rand.NextPercent(5, PERCENT)), false, maxX2 * .4, maxY2 * .8, false)); rings.Add(new TubeRingRegularPolygon(WeaponDNA.GetKeyValue("ring5", from, to, rand.NextPercent(2, PERCENT)), false, maxX2 * .5, maxY2 * 1, false)); rings.Add(new TubeRingRegularPolygon(WeaponDNA.GetKeyValue("ring6", from, to, rand.NextPercent(.5, PERCENT)), false, maxX2 * .45, maxY2 * .75, true)); rings = TubeRingBase.FitNewSize(rings, dna.Radius * Math.Max(maxX1, maxX2), dna.Radius * Math.Max(maxY1, maxY2), dna.Length); // multiplying x by maxX, because the rings were defined with x maxing at maxX, and y maxing at 1 geometry.Geometry = UtilityWPF.GetMultiRingedTube(10, rings, true, true, new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 90))); // the tube builds along z, but this class wants along x #endregion geometries.Children.Add(geometry); }
private static void GenerateGarbageData(EncogOCR_SketchData[] sketches, int outputSize, List <double[]> inputs, List <double[]> outputs) { #region validate #if DEBUG if (sketches.Select(o => o.BitmapColors.Width).Distinct().Count() != 1 || sketches.Select(o => o.BitmapColors.Height).Distinct().Count() != 1) { throw new ApplicationException("Stored images are different sizes"); } #endif #endregion double[] zeroOutput = Enumerable.Range(0, outputSize).Select(o => 0d).ToArray(); #region efficient if only a final is needed //Color[][] colors = sketches. // Select(o => o.BitmapColors.GetColors(0, 0, o.Bitmap.PixelWidth, o.Bitmap.PixelHeight)). // ToArray(); //bool[] isBlack = Enumerable.Range(0, colors[0].Length). // AsParallel(). // Select(o => new { Index = o, IsBlack = colors.Any(p => p[o].A > 200 && Math3D.Avg(p[o].R, p[o].G, p[o].B) < 20) }). // OrderBy(o => o.Index). // Select(o => o.IsBlack). // ToArray(); #endregion // Figure out which pixels of each sketch are black bool[][] isBlack = sketches. Select(o => o.BitmapColors.GetColors(0, 0, o.BitmapColors.Width, o.BitmapColors.Height)). Select(o => o.Select(p => p.A > 200 && Math1D.Avg(p.R, p.G, p.B) < 20).ToArray()). ToArray(); // Or the images together bool[] composite = Enumerable.Range(0, isBlack[0].Length). AsParallel(). Select(o => new { Index = o, IsBlack = isBlack.Any(p => p[o]) }). OrderBy(o => o.Index). Select(o => o.IsBlack). ToArray(); #region Blank sketch double[] percentBlack = isBlack. Select(o => o.Sum(p => p ? 1d : 0d) / o.Length). ToArray(); if (percentBlack.All(o => o > .01)) { // None of the inputs are blank, so make blank a garbage state inputs.Add(Enumerable.Range(0, composite.Length).Select(o => 0d).ToArray()); outputs.Add(zeroOutput); } #endregion #region Inverted sketch // Making this, because the assumption is that if they draw in a region outside any of the inputs, then it should be a zero output inputs.Add(composite.Select(o => o ? 0d : 1d).ToArray()); outputs.Add(zeroOutput); #endregion //TODO: Do a few subsets of inverted }