Große Herausforderung. Hier ist meine Lösung. Ich habe versucht, dem ursprünglichen Comic so nahe wie möglich zu kommen, ich habe sogar die xkcd-Schriftart verwendet .
Es ist eine WPF-Anwendung, aber ich System.Drawing
habe die Zeichnungsteile gemacht, weil ich faul bin.
Grundkonzept: In WPF sind Fenster Visuals
, dh sie können gerendert werden. Ich rendere die gesamte Window-Instanz auf eine Bitmap, zähle das Schwarz und das gesamte Schwarz oder Weiß auf (ohne die Graustufen in der Schriftglättung und so weiter) und zähle diese auch für jedes Drittel des Bildes auf (für jedes Panel). Dann mache ich es nochmal auf einem Timer. Es erreicht das Gleichgewicht innerhalb von ein oder zwei Sekunden.
Herunterladen:
MEGA
Überprüfen Sie heruntergeladene Dateien immer auf Viren usw.
Sie müssen die oben genannte Schriftart auf Ihrem System installieren, wenn Sie sie sehen möchten. Andernfalls ist sie die Standard-WPF-Schriftart.
XAML:
<Window
x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="xkcd: 688" Height="300" Width="1000" WindowStyle="ToolWindow">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"/>
<ColumnDefinition Width="0.3*"/>
<ColumnDefinition Width="0.3*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" x:Name="bFirstPanel" BorderThickness="3" Padding="10px" Margin="0 0 10px 0">
<Grid>
<Label FontSize="18" FontFamily="xkcd" VerticalAlignment="Top">Fraction of this window that is white</Label>
<Label FontSize="18" FontFamily="xkcd" VerticalAlignment="Bottom">Fraction of this window that is black</Label>
<Image x:Name="imgFirstPanel"></Image>
</Grid>
</Border>
<Border Grid.Column="1" x:Name="bSecondPanel" BorderBrush="Black" BorderThickness="3" Padding="10px" Margin="10px 0">
<Grid>
<TextBlock FontSize="18" FontFamily="xkcd" VerticalAlignment="Top" HorizontalAlignment="Left">Amount of <LineBreak></LineBreak>black ink <LineBreak></LineBreak>by panel:</TextBlock>
<Image x:Name="imgSecondPanel"></Image>
</Grid>
</Border>
<Border Grid.Column="2" x:Name="bThirdPanel" BorderBrush="Black" BorderThickness="3" Padding="10px" Margin="10px 0 0 0">
<Grid>
<TextBlock FontSize="18" FontFamily="xkcd" VerticalAlignment="Top" HorizontalAlignment="Left">Location of <LineBreak></LineBreak>black ink <LineBreak></LineBreak>in this window:</TextBlock>
<Image x:Name="imgThirdPanel"></Image>
</Grid>
</Border>
</Grid>
</Window>
Code:
using System;
using System.Drawing;
using System.Timers;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Brushes = System.Drawing.Brushes;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
private Timer mainTimer = new Timer();
public MainWindow()
{
InitializeComponent();
Loaded += (o1,e1) =>
{
mainTimer = new Timer(1000/10);
mainTimer.Elapsed += (o, e) => {
try
{
Dispatcher.Invoke(Refresh);
} catch(Exception ex)
{
// Nope
}
};
mainTimer.Start();
};
}
private void Refresh()
{
var actualh = this.RenderSize.Height;
var actualw = this.RenderSize.Width;
var renderTarget = new RenderTargetBitmap((int) actualw, (int) actualh, 96, 96, PixelFormats.Pbgra32);
var sourceBrush = new VisualBrush(this);
var visual = new DrawingVisual();
var context = visual.RenderOpen();
// Render the window onto the target bitmap
using (context)
{
context.DrawRectangle(sourceBrush, null, new Rect(0,0, actualw, actualh));
}
renderTarget.Render(visual);
// Create an array with all of the pixel data
var stride = (int) actualw*4;
var data = new byte[stride * (int)actualh];
renderTarget.CopyPixels(data, stride, 0);
var blackness = 0f;
var total = 0f;
var blacknessFirstPanel = 0f;
var blacknessSecondPanel = 0f;
var blacknessThirdPanel = 0f;
var totalFirstPanel = 0f;
var totalSecondPanel = 0f;
var totalThirdPanel = 0f;
// Count all of the things
for (var i = 0; i < data.Length; i += 4)
{
var b = data[i];
var g = data[i + 1];
var r = data[i + 2];
if (r == 0 && r == g && g == b)
{
blackness += 1;
total += 1;
var x = i%(actualw*4) / 4;
if(x < actualw / 3f)
{
blacknessFirstPanel += 1;
totalFirstPanel += 1;
} else if (x < actualw * (2f / 3f))
{
blacknessSecondPanel += 1;
totalSecondPanel += 1;
}
else if (x < actualw)
{
blacknessThirdPanel += 1;
totalThirdPanel += 1;
}
} else if (r == 255 && r == g && g == b)
{
total += 1;
var x = i % (actualw * 4) / 4;
if (x < actualw / 3f)
{
totalFirstPanel += 1;
}
else if (x < actualw * (2f / 3f))
{
totalSecondPanel += 1;
}
else if (x < actualw)
{
totalThirdPanel += 1;
}
}
}
var black = blackness/total;
Redraw(black, blacknessFirstPanel, blacknessSecondPanel, blacknessThirdPanel, blackness, renderTarget);
}
private void Redraw(double black, double firstpanel, double secondpanel, double thirdpanel, double totalpanels, ImageSource window)
{
DrawPieChart(black);
DrawBarChart(firstpanel, secondpanel, thirdpanel, totalpanels);
DrawImage(window);
}
void DrawPieChart(double black)
{
var w = (float)bFirstPanel.ActualWidth;
var h = (float)bFirstPanel.ActualHeight;
var padding = 0.1f;
var b = new Bitmap((int)w, (int)h);
var g = Graphics.FromImage(b);
var px = padding*w;
var py = padding*h;
var pw = w - (2*px);
var ph = h - (2*py);
g.DrawEllipse(Pens.Black, px,py,pw,ph);
g.FillPie(Brushes.Black, px, py, pw, ph, 120, (float)black * 360);
g.DrawLine(Pens.Black, 30f, h * 0.1f, w / 2 + w * 0.1f, h / 2 - h * 0.1f);
g.DrawLine(Pens.Black, 30f, h - h * 0.1f, w / 2 - w * 0.2f, h / 2 + h * 0.2f);
imgFirstPanel.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(b.Width, b.Height));
}
void DrawBarChart(double b1, double b2, double b3, double btotal)
{
var w = (float)bFirstPanel.ActualWidth;
var h = (float)bFirstPanel.ActualHeight;
var padding = 0.1f;
var b = new Bitmap((int)w, (int)h);
var g = Graphics.FromImage(b);
var px = padding * w;
var py = padding * h;
var pw = w - (2 * px);
var ph = h - (2 * py);
g.DrawLine(Pens.Black, px, py, px, ph+py);
g.DrawLine(Pens.Black, px, py + ph, px+pw, py+ph);
var fdrawbar = new Action<int, double>((number, value) =>
{
var height = ph*(float) value/(float) btotal;
var width = pw/3f - 4f;
var x = px + (pw/3f)*(number-1);
var y = py + (ph - height);
g.FillRectangle(Brushes.Black, x, y, width, height);
});
fdrawbar(1, b1);
fdrawbar(2, b2);
fdrawbar(3, b3);
imgSecondPanel.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(b.Width, b.Height));
}
void DrawImage(ImageSource window)
{
imgThirdPanel.Source = window;
}
}
}
Der Code ist nicht aufgeräumt, sollte aber etwas lesbar sein, sorry.