xkcd challenge: "Prozentsatz des Bildschirms, der [x] Farbe hat"

57

Ich denke, wir haben wahrscheinlich alle diesen xkcd-Comic gesehen :

http://imgs.xkcd.com/comics/self_description.png:

Das könnte entweder zu allgemein oder zu schwierig sein, da bin ich mir nicht sicher. Die Herausforderung besteht jedoch darin, ein Programm in einer beliebigen Sprache zu erstellen, das ein Fenster mit mindestens zwei Farben erstellt und in englischen Wörtern anzeigt, wie viel Prozent des Bildschirms jeweils farbig sind.

Ex. Die einfachste Lösung wäre ein weißer Hintergrund mit schwarzen Buchstaben mit der Aufschrift "Prozentsatz dieses Bildes, das schwarz ist: [x]%. Prozentsatz dieses Bildes, das weiß ist: [y]%".

Sie können so verrückt oder so einfach werden, wie Sie möchten. Klartext ist eine gültige Lösung, aber wenn Sie interessante Bilder wie im xkcd-Comic erstellen, ist das sogar noch besser! Der Gewinner ist die unterhaltsamste und kreativste Lösung, die die meisten Stimmen erhält. Also mach etwas, das Spaß macht und der xkcd würdig ist! :)

Also was denkst du? Klingt nach einer lustigen Herausforderung? :)

Bitte fügen Sie Ihrer Antwort einen Screenshot Ihres laufenden Programms bei :)

WendiKidd
quelle
6
Ein "dieses Programm hat 64 A's, 4 B's, ... und 34 doppelte Anführungszeichen im Quellcode" -Programm wäre interessanter :-)
John Dvorak
2
OK ... was sind die objektiven Gewinnkriterien? Wie ermitteln Sie, ob eine bestimmte Ausgabe gültig ist? Ist es ausreichend, dass es wahr ist und eine Eigenschaft von sich selbst numerisch beschreibt?
John Dvorak
@ JanDvorak Oh, das ist gut! Das Alphabet-Programm ist eigentlich das, woran ich ursprünglich gedacht habe, aber ich habe nicht darüber nachgedacht, das Quellcode-Element hinzuzufügen! Das solltest du als Frage posten :) Ja, es reicht aus, dass es wahr ist und sich selbst beschreibt. Hmm, Sie haben Recht, ich habe nicht darüber nachgedacht, wie ich beweisen würde, dass die Endergebnisse korrekt sind. Ich brauche einen Weg, um alle Pixel jeder Farbe in einem Ergebnisbild zu zählen, nehme ich an. Ich werde das jetzt untersuchen. (Sorry, meine erste Frage hatte Probleme ... Ich habe es versucht, aber ich bin neu in diesem! Vielen Dank :))
WendiKidd
Wenn Wahrhaftigkeit und Selbstreferenz das ausreichende Kriterium sind, ist hier mein Golfscript-Kandidat: "/.*/"(Lesen: [der Quellcode] enthält keine neue Zeile)
John Dvorak
@JanDvorak Hmm, ich habe deinen Code hier ausprobiert und die Ausgabe war die gleiche wie der Code, außer ohne Anführungszeichen. Vielleicht erkläre ich das nicht richtig, sorry. Es müssen mindestens 2 Farben generiert werden, und in einer Form eines englischen Satzes muss die Ausgabe echte Wörter generieren, die beschreiben, wie viel Prozent des Bildschirms jede der Farben belegt. Vielleicht war das eine dumme Idee. Ich dachte, es würde Spaß machen, aber es könnte in der Praxis nicht funktionieren :)
WendiKidd

Antworten:

35

Ulme

Ich habe noch niemanden gesehen, der diese Lücke benutzt hat: Demo

import Color exposing (hsl)
import Graphics.Element exposing (..)
import Mouse
import Text
import Window

msg a = centered <| Text.color a (Text.fromString "half the screen is this color")

type Pos = Upper | Lower

screen (w,h) (x,y) = 
  let (dx,dy) = (toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y)
      ang = hsl (atan2 dy dx) 0.7 0.5
      ang' = hsl (atan2 dx dy) 0.7 0.5
      box c = case c of
        Upper -> container w (h // 2) middle (msg ang) |> color ang'
        Lower -> container w (h // 2) middle (msg ang') |> color ang
  in  flow down [box Upper, box Lower]

main = Signal.map2 screen Window.dimensions Mouse.position

Bildbeschreibung hier eingeben

hoosierEE
quelle
3
Große Lücke!
Timtech
Ich liebe es!!! Zumindest für den Moment erhalten Sie das Häkchen für schiere clevere Punkte. Liebe es!
WendiKidd
13
Das Beste ist, ich bin mir immer noch nicht sicher, welcher Satz über welche Farbe spricht.
Brilliand
3
Die view sourcewird dort von share-elm.com gestellt und ist nicht Teil des kompilierten JS / HTML.
hoosierEE
1
@ML Das hängt vom Umfang des Wortes "this" ab. JavaScript-Programmierer verstehen ...
hoosierEE
37

JavaScript mit HTML

Ich habe versucht, den Original-Comic genauer zu reproduzieren. Ein Screenshot wird mit der html2canvas-Bibliothek erstellt. Die Zahlen werden wiederholt berechnet, sodass Sie die Größe des Fensters ändern oder der Seite in Echtzeit etwas hinzufügen können.

Versuchen Sie es online: http://copy.sh/xkcd-688.html

Hier ist ein Screenshot:

Bildbeschreibung hier eingeben

<html contenteditable>
<script src=http://html2canvas.hertzen.com/build/html2canvas.js></script>
<script>
onload = function() {
    setInterval(k, 750);
    k();
}
function k() {
    html2canvas(document.body, { onrendered: t });
}
function t(c) {
    z.getContext("2d").drawImage(c, 0, 0, 300, 150);
    c = c.getContext("2d").getImageData(0, 0, c.width, c.height).data;

    for(i = y = 0; i < c.length;) 
        y += c[i++];

    y /= c.length * 255;

    x.textContent = (y * 100).toFixed(6) + "% of this website is white";

    q = g.getContext("2d");

    q.fillStyle = "#eee";
    q.beginPath();
    q.moveTo(75, 75);
    q.arc(75,75,75,0,7,false);
    q.lineTo(75,75);
    q.fill();

    q.fillStyle = "#000";
    q.beginPath();
    q.moveTo(75, 75);
    q.arc(75,75,75,0,6.28319*(1-y),false);
    q.lineTo(75,75);
    q.fill();
}
</script>
<center>
<h2 id=x></h2>
<hr>
<table><tr>
<td>Fraction of<br>this website<br>which is white _/
<td><canvas width=150 id=g></canvas>
<td>&nbsp; Fraction of<br>- this website<br>&nbsp; which is black
</table>
<hr>
0
<canvas style="border-width: 0 0 1px 1px; border-style: solid" id=z></canvas>
<h4>Location of coloured pixels in this website</h4>
Kopieren
quelle
Nett!! Lieben Sie die Ähnlichkeiten zum xkcd Comic und die Tatsache, dass ich den Text ändern kann. Ordentlich! : D
WendiKidd
1
beeindruckende Arbeit oO
izabera
Nifty ... aber ich denke, es muss sich stabilisieren, um eine "Lösung" zu sein. Ich habe es nicht vollständig durchdacht - aber da es nicht unbedingt eine Lösung für willkürliche Präzision gibt, wenn Sie aus einem begrenzten Satz von Ziffern-Glyphen zeichnen, müssen Sie die Präzision aufheben, wenn sie nicht mit der höheren Präzision gelöst werden kann Du versuchst es. Ich stelle mir vor, dass die Verwendung einer Monospace-Schriftart, mit der Sie die Schwarz / Weiß-Pixel vorberechnen, ebenfalls erforderlich sein wird.
Dr. Rebmu
Sie verwenden 3 Farben. Wo sind also die Prozentsätze für Grau? ;)
ML
26

Verarbeitung, 222 Zeichen

http://i.imgur.com/eQzMGzk.png

Ich wollte schon immer meine eigene Version dieses Comics machen! Der einfachste (einzige?) Weg, wie ich mir das vorstellen konnte, war Versuch und Irrtum - etwas zeichnen, zählen, erneut zeichnen ...

Dieses Programm stellt sich nach einigen Sekunden auf einen genauen Prozentsatz ein. Es ist nicht sehr hübsch, aber interaktiv . Sie können die Größe des Fensters ändern und es wird neu berechnet.

Einige neue Zeilen zur besseren Lesbarkeit hinzugefügt:

float s,S,n;
int i;
void draw(){
frame.setResizable(true);
background(255);
fill(s=i=0);
text(String.format("%.2f%% of this is white",S/++n*100),10,10);
loadPixels();
while(i<width*height)if(pixels[i++]==-1)s++;
S+=s/height/width;
}

Es wird nur der Prozentsatz der weißen Pixel angezeigt. Aufgrund des Antialiasing des Texts sind nicht weiße Pixel nicht unbedingt schwarz. Je länger es ausgeführt wird, desto mehr Zeit benötigt es, um sich bei einer Größenänderung selbst zu aktualisieren.

Bearbeiten:

Es ist also eine Code-Herausforderung. Ich habe sowieso irgendwie Golf gespielt. Vielleicht könnte ich später eine Art Grafik hinzufügen, aber das allgemeine Prinzip würde das gleiche bleiben. Die Interaktivität ist der nette Teil, den ich denke.

daniero
quelle
Sehr schön!! Ich denke, Sie erhalten zusätzliche Anerkennung für die Interaktivität; Ich hatte Spaß daran, die Fenstergröße zu ändern! Sehr cool :) Und du bist meine erste Antwort! Ich wusste nicht, ob jemand spielen möchte, also danke. Du hast meinen Tag gerettet. : D +1! (Ich bin jedoch neugierig, warum verlangsamt es sich mit der Zeit und nähert sich dem richtigen Prozentsatz. Ich bin nur neugierig, was passiert. Ich habe diese Sprache noch nie gesehen. Ich sehe eine Viele neue Sachen stöbern auf dieser Seite!)
WendiKidd
Headdesk Außer ich habe versehentlich vergessen, auf +1 zu klicken. Jetzt +1 ... haha. Es tut uns leid!
WendiKidd
1
Sie können eine weitere Funktion hinzufügen, mit der Benutzer mit der Maus darauf zeichnen können, um die Interaktivität zu verbessern.
AJMansfield
7
Holy Box Shadow, Batman
Bojangles
Wenn Sie Golf spielen möchten, können Sie background(-1)anstelle vonbackground(255)
Kritixi Lithos
20

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.Drawinghabe 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.

Kiran Price
quelle
2
Ein später Einstieg, aber einer der besten.
Primo
14

C (mit SDL und SDL_ttf): Graustufenlösung

Hier ist eine Lösung, die das Tortendiagramm nutzt, um das gesamte Spektrum der Graustufenpixelfarben zu erfassen, die sich auf knapp 100 Zeilen belaufen.

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "SDL.h"
#include "SDL_ttf.h"

int main(void)
{
    SDL_Surface *screen, *buffer, *caption;
    SDL_Color pal[256];
    SDL_Rect rect;
    SDL_Event event;
    TTF_Font *font;
    int levels[256], plev[256];
    Uint8 *p;
    float g;
    int cr, redraw, hoffset, h, n, v, w, x, y;

    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    screen = SDL_SetVideoMode(640, 480, 0, SDL_ANYFORMAT | SDL_RESIZABLE);
    font = TTF_OpenFont(FONTPATH, 24);
    buffer = 0;
    for (;;) {
        if (!buffer) {
            buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h,
                                          8, 0, 0, 0, 0);
            for (n = 0 ; n < 256 ; ++n)
                pal[n].r = pal[n].g = pal[n].b = n;
            SDL_SetColors(buffer, pal, 0, 256);
        }
        memcpy(plev, levels, sizeof levels);
        memset(levels, 0, sizeof levels);
        SDL_LockSurface(buffer);
        p = buffer->pixels;
        for (h = 0 ; h < buffer->h ; ++h) {
            for (w = 0 ; w < buffer->w ; ++w)
                ++levels[p[w]];
            p += buffer->pitch;
        }
        for (n = 1 ; n < 256 ; ++n)
            levels[n] += levels[n - 1];
        redraw = memcmp(levels, plev, sizeof levels);
        if (redraw) {
            SDL_UnlockSurface(buffer);
            SDL_FillRect(buffer, NULL, 255);
            caption = TTF_RenderText_Shaded(font,
                        "Distribution of pixel color in this image",
                        pal[0], pal[255]);
            rect.x = (buffer->w - caption->w) / 2;
            rect.y = 4;
            hoffset = caption->h + 4;
            SDL_BlitSurface(caption, NULL, buffer, &rect);
            SDL_FreeSurface(caption);
            SDL_LockSurface(buffer);
            cr = buffer->h - hoffset;
            cr = (cr < buffer->w ? cr : buffer->w) / 2 - 4;
            p = buffer->pixels;
            for (h = 0 ; h < buffer->h ; ++h) {
                y = h - (screen->h + hoffset) / 2;
                for (w = 0 ; w < buffer->w ; ++w) {
                    x = w - buffer->w / 2;
                    g = sqrtf(x * x + y * y);
                    if (g < cr - 1) {
                        g = atanf((float)y / (x + g));
                        v = levels[255] * (g / M_PI + 0.5);
                        for (n = 0 ; n < 255 && levels[n] < v ; ++n) ;
                        p[w] = n;
                    } else if (g < cr + 1) {
                        p[w] = (int)(128.0 * fabs(g - cr));
                    }
                }
                p += buffer->pitch;
            }
        }
        SDL_UnlockSurface(buffer);
        SDL_BlitSurface(buffer, NULL, screen, NULL);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
        if (redraw ? SDL_PollEvent(&event) : SDL_WaitEvent(&event)) {
            if (event.type == SDL_QUIT)
                break;
            if (event.type == SDL_VIDEORESIZE) {
                SDL_SetVideoMode(event.resize.w, event.resize.h, 0,
                                 SDL_ANYFORMAT | SDL_RESIZABLE);
                SDL_FreeSurface(buffer);
                buffer = 0;
            }
        }
    }
    SDL_Quit();
    TTF_Quit();
    return 0;
}

Wie bei meiner vorherigen Lösung muss der Pfad zur Schriftartdatei entweder in der Quelle fest codiert oder dem Build-Befehl hinzugefügt werden, z.

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH=`fc-match --format='"%{file}"' :bold`
    xkcdgolf.c -lSDL_ttf `sdl-config --libs` -lm

Die Ausgabe des Programms sieht folgendermaßen aus:

Kreisdiagramm mit der vollständigen Verteilung der Graustufenpixelfarben

Es macht Spaß, diese zu sehen, denn die ganze Mathematik verlangsamt das Neuzeichnen, bis Sie sehen, dass das Programm in der stabilen Lösung Null ist. Die erste Schätzung ist verrückt (da die Oberfläche komplett schwarz ist) und schrumpft nach etwa einem Dutzend Iterationen auf die endgültige Größe.

Der Code berechnet die Grundgesamtheit für jede Pixelfarbe im aktuellen Bild. Wenn diese Bevölkerungszahl nicht mit der letzten übereinstimmt, wird das Bild neu gezeichnet. Der Code durchläuft jedes Pixel, wandelt jedoch die x- und y-Koordinaten in Polarkoordinaten um und berechnet zunächst den Radius (unter Verwendung der Bildmitte als Ursprung). Wenn der Radius innerhalb des Kreisdiagrammbereichs liegt, wird das Theta berechnet. Das Theta lässt sich leicht auf die Anzahl der Populationen skalieren, die die Pixelfarbe bestimmt. Befindet sich der Radius hingegen direkt am Rand des Kreisdiagramms, wird ein Anti-Alias-Wert berechnet, um den Kreis um die Außenseite des Diagramms zu zeichnen. Polarkoordinaten machen alles einfach!

Brot-Box
quelle
Sie verwenden meistens die floatVersionen der mathematischen Bibliotheksfunktionen, sollten es aber nicht fabssein fabsf?
Luser Droog
Technisch vielleicht aber fabs()portabler.
Brotkasten
Es stimmt, ich hatte Probleme damit, dass einer nicht in den Kopfzeilen definiert ist, selbst wenn er in der Bibliothek vorhanden ist. Außerdem kann weniger Leistung erzielt werden als mit den Transzendentalen. :)
Luser Droog
10

C (mit SDL und SDL_ttf)

Hier ist eine sehr einfache Implementierung in etwa 60 Zeilen C-Code:

#include <stdio.h>
#include "SDL.h"
#include "SDL_ttf.h"

int main(void)
{
    char buf[64];
    SDL_Surface *screen, *text;
    SDL_Rect rect;
    SDL_Color black;
    SDL_Event event;
    TTF_Font *font;
    Uint32 blackval, *p;
    int size, b, prevb, h, i;

    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    screen = SDL_SetVideoMode(640, 480, 32, SDL_ANYFORMAT | SDL_RESIZABLE);
    font = TTF_OpenFont(FONTPATH, 32);
    black.r = black.g = black.b = 0;
    blackval = SDL_MapRGB(screen->format, 0, 0, 0);

    b = -1;
    for (;;) {
        prevb = b;
        b = 0;
        SDL_LockSurface(screen);
        p = screen->pixels;
        for (h = screen->h ; h ; --h) {
            for (i = 0 ; i < screen->w ; ++i)
                b += p[i] == blackval;
            p = (Uint32*)((Uint8*)p + screen->pitch);
        }
        SDL_UnlockSurface(screen);
        size = screen->w * screen->h;
        SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
        sprintf(buf, "This image is %.2f%% black pixels", (100.0 * b) / size);
        text = TTF_RenderText_Solid(font, buf, black);
        rect.x = (screen->w - text->w) / 2;
        rect.y = screen->h / 2 - text->h;
        SDL_BlitSurface(text, NULL, screen, &rect);
        SDL_FreeSurface(text);
        sprintf(buf, "and %.2f%% white pixels.", (100.0 * (size - b)) / size);
        text = TTF_RenderText_Solid(font, buf, black);
        rect.x = (screen->w - text->w) / 2;
        rect.y = screen->h / 2;
        SDL_BlitSurface(text, NULL, screen, &rect);
        SDL_FreeSurface(text);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
        if (b == prevb ? SDL_WaitEvent(&event) : SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT)
                break;
            if (event.type == SDL_VIDEORESIZE)
                SDL_SetVideoMode(event.resize.w, event.resize.h, 32,
                                 SDL_ANYFORMAT | SDL_RESIZABLE);
        }
    }

    TTF_Quit();
    SDL_Quit();
    return 0;
}

Um dies zu kompilieren, müssen Sie definieren FONTPATH, dass auf eine .ttf-Datei der zu verwendenden Schriftart verwiesen wird:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH='"/usr/share/fonts/truetype/freefont/FreeSansBold.ttf"'
    xkcdgolf.c -lSDL_ttf `sdl-config --libs`

Auf den meisten modernen Linux-Rechnern können Sie das fc-matchDienstprogramm verwenden, um die Speicherorte der Schriftarten zu ermitteln. Der Kompilierungsbefehl lautet also:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH=`fc-match --format='"%{file}"' :bold`
    xkcdgolf.c -lSDL_ttf `sdl-config --libs`

(Natürlich können Sie die gewünschte Schrift auch durch Ihre persönliche Lieblingsschrift ersetzen.)

Der Code fordert ausdrücklich kein Anti-Aliasing an, sodass das Fenster nur schwarze und weiße Pixel enthält.

Schließlich ließ ich mich von @ danieros eleganter Lösung für die Größenänderung von Fenstern inspirieren. Sie werden sehen, dass das Programm manchmal zwischen den Zählungen pendelt und sich in einer Umlaufbahn um einen Attraktor befindet, den es niemals erreichen kann. Wenn dies passiert, passen Sie die Größe des Fensters einfach ein wenig an, bis es stoppt.

Und je nach Anforderung sieht es so aus, wenn ich es auf meinem System ausführe:

Dieses Bild besteht zu 3,36% aus schwarzen und zu 96,64% aus weißen Pixeln.

Schließlich sollte ich darauf hinweisen, dass die MAA ein Interview mit Randall Munroe veröffentlicht hat, in dem er die Entstehung des Cartoons # 688 ausführlich bespricht , falls es hier noch niemand gesehen hat .

Brot-Box
quelle
1
Sehr schöne Lösung. Könnten Sie möglicherweise einige Screenshots des laufenden Programms einfügen, die auf @ danieros Beitrag folgen? :)
Alex Brooks
+1, sehr nett! Vielen Dank für das Hinzufügen des Screenshots :) Und der Interview-Link ist interessant, danke!
WendiKidd
9

Bildbeschreibung hier eingeben

Das Bild ist 100x100 und die Zahlen sind genau, und ich meine genau - ich habe ein Bild mit 10000 Pixeln gewählt, damit die Prozentsätze mit zwei Dezimalstellen ausgedrückt werden können. Die Methode war ein bisschen Mathe, ein bisschen Vermutung und ein bisschen Zahlenkalkulation in Python.

Da ich im Voraus wusste, dass die Prozentsätze in vier Ziffern ausgedrückt werden können, zählte ich die Anzahl der schwarzen Pixel in jeder der Ziffern 0 bis 9 in einem 8 Pixel hohen Arial, in dem der Text geschrieben ist schnelle Funktion, weightdie angibt, wie viele Pixel benötigt werden, um eine bestimmte Zahl zu schreiben, mit Nullen aufgefüllt, um 4 Ziffern zu erhalten:

def weight(x):
    total = 4 * px[0]
    while x > 0:
       total = total - px[0] + px[x % 10]
       x = x / 10
    return total

pxist ein Array, das Ziffern auf die Anzahl der erforderlichen Pixel abbildet. Wenn B die Anzahl der schwarzen Pixel ist und W die Anzahl der weißen Pixel ist, haben wir B + W = 10000und wir brauchen:

B = 423 + weight(B) + weight(W)
W = 9577 - weight(B) - weight(W)

Woher kommen die Konstanten? 423 ist die "anfängliche" Anzahl schwarzer Pixel, die Anzahl schwarzer Pixel im Text ohne die Zahlen. 9577 ist die Anzahl der anfänglichen weißen Pixel. Ich musste die Anzahl der anfänglichen schwarzen Pixel mehrmals anpassen, bevor ich Konstanten abrufen konnte, sodass das obige System sogar eine Lösung hat. Dies geschah durch Erraten und Kreuzen meiner Finger.

Das obige System ist furchtbar nichtlinear, so dass Sie offensichtlich vergessen können, es symbolisch zu lösen, aber Sie können einfach jeden Wert von B durchlaufen, W = 10000 - B einstellen und die Gleichungen explizit überprüfen.

>>> for b in range(10000 + 1):
...     if b == weight(b) + weight(10000 - b)+423: print b;
...
562
564
Jack M
quelle
Machen Sie vielleicht ein 250 x 400-Bild, damit Sie es auf 3 Dezimalstellen bringen und in der Zwischenzeit mehr Text anzeigen können.
Joe Z.
Sehr schöne Lösung, einige Brute-Force-Mathematik kann immer diese Art von Problemen lösen!
KPCh
6

QBasic

Weil Nostalgie.

Und weil ich keine Bildbibliotheken wirklich kenne, sind das moderne Sprachen.

SCREEN 9

CONST screenWidth = 640
CONST screenHeight = 350
CONST totalPixels# = screenWidth * screenHeight

accuracy = 6

newWhite# = 0
newGreen# = 0
newBlack# = totalPixels#

DO
    CLS
    white# = newWhite#
    green# = newGreen#
    black# = newBlack#

    ' Change the precision of the percentages every once in a while
    ' This helps in finding values that converge
    IF RND < .1 THEN accuracy = INT(RND * 4) + 2
    format$ = "###." + LEFT$("######", accuracy) + "%"

    ' Display text
    LOCATE 1
    PRINT "Percentage of the screen which is white:";
    PRINT USING format$; pct(white#)
    LOCATE 4
    PRINT white#; "/"; totalPixels#; "pixels"
    LOCATE 7
    PRINT "Percentage of the screen which is black:";
    PRINT USING format$; pct(black#)
    LOCATE 10
    PRINT black#; "/"; totalPixels#; "pixels"
    LOCATE 13
    PRINT "Percentage of the screen which is green:";
    PRINT USING format$; pct(green#)
    LOCATE 16
    PRINT green#; "/"; totalPixels#; "pixels"

    ' Display bar graphs
    LINE (0, 16)-(pct(white#) / 100 * screenWidth, 36), 2, BF
    LINE (0, 100)-(pct(black#) / 100 * screenWidth, 120), 2, BF
    LINE (0, 184)-(pct(green#) / 100 * screenWidth, 204), 2, BF

    newBlack# = pixels#(0)
    newGreen# = pixels#(2)
    newWhite# = pixels#(15)
LOOP UNTIL black# = newBlack# AND white# = newWhite# AND green# = newGreen#

' Wait for user keypress before ending program: otherwise the "Press any
' key to continue" message would instantly make the results incorrect!
x$ = INPUT$(1)


FUNCTION pixels# (colr)
' Counts how many pixels of the given color are on the screen

pixels# = 0

FOR i = 0 TO screenWidth - 1
    FOR j = 0 TO screenHeight - 1
        IF POINT(i, j) = colr THEN pixels# = pixels# + 1
    NEXT j
NEXT i

END FUNCTION

FUNCTION pct (numPixels#)
' Returns percentage, given a number of pixels

pct = numPixels# / totalPixels# * 100

END FUNCTION

Ziemlich unkomplizierte Methode zur Zählung von Ausgaben und Wiederholungen. Das wichtigste "Interessante" ist, dass das Programm zufällig verschiedene Präzisionen für die Prozentsätze versucht - ich habe festgestellt, dass es nicht immer anders konvergiert.

Und die Ausgabe (getestet auf QB64 ):

QBasic-Absatz

DLosc
quelle
3

AWK

... mit netpbm und anderen helfern

Die 'x'-Datei:

BEGIN {
        FS=""
        n++
        while(n!=m) {
                c="printf '%s\n' '"m"% black pixels'"
                c=c" '"100-m"% white pixels'"
                c=c" | pbmtext -space 1 -lspace 1 | pnmtoplainpnm | tee x.pbm"
                n=m
                delete P
                nr=0
                while(c|getline==1) if(++nr>2) for(i=1;i<=NF;i++) P[$i]++
                close(c)
                m=100*P[1]/(P[0]+P[1])
                print m"%"
        }
}

Der Lauf:

$ awk -f x
4.44242%
5.2424%
5.04953%
5.42649%
5.27746%
5.1635%
5.15473%
5.20733%
5.20733%

Das Bild ist als 'x.pbm' geschrieben, ich habe es zum Hochladen in PNG konvertiert:

x.png

Yeti
quelle