I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

Peripheral drift illusion

From Rosetta Code
(Redirected from Peripheral Drift Illusion)
Peripheral drift illusion is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

Generate and display a Peripheral Drift Illusion

The image appears to be moving even though it is perfectly static.

Provide a link to show the output, either by running the code online or a screenshot uploaded to a suitable image host.

References


Julia[edit]

Line color tables taken from the Wren example. See the output on imgur.

using Gtk, Colors, Cairo
 
function CodepenApp()
# left-top, top-right, right-bottom, bottom-left
LT, TR, RB, BL = 1, 2, 3, 4
edges = [
[LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB],
[LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL],
[TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL],
[TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT],
[RB, TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT],
[RB, RB, TR, TR, LT, LT, BL, BL, RB, RB, TR, TR],
[BL, RB, RB, TR, TR, LT, LT, BL, BL, RB, RB, TR],
[BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB, RB],
[LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB],
[LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL],
[TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL],
[TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT]]
W, B = colorant"white", colorant"darkgray"
colors = [
[W, B, B, W],
[W, W, B, B],
[B, W, W, B],
[B, B, W, W]]
win = GtkWindow("Peripheral drift illusion", 230, 230) |> (can = GtkCanvas())
@guarded draw(can) do widget
ctx = Gtk.getgc(can)
function line(x1, y1, x2, y2, colr)
set_source(ctx, colr)
move_to(ctx, x1, y1)
line_to(ctx, x2, y2)
stroke(ctx)
end
set_source(ctx, colorant"yellow")
rectangle(ctx, 0, 0, 250, 250)
fill(ctx)
set_line_width(ctx, 2)
for x in 1:12
px = 18 + x * 14
for y in 1:12
py = 18 + y * 14
set_source(ctx, colorant"skyblue")
rectangle(ctx, px, py, 10, 10)
fill(ctx)
carray = colors[edges[y][x]]
line(px, py, px + 9, py, carray[1])
line(px + 9, py, px + 9, py + 9, carray[2])
line(px + 9, py + 9, px, py + 9, carray[3])
line(px, py + 9, px, py, carray[4])
end
end
end
showall(win)
draw(can)
condition = Condition()
endit(w) = notify(condition)
signal_connect(endit, win, :destroy)
showall(win)
wait(condition)
end
 
CodepenApp()
 

Nim[edit]

Translation of: Julia
Library: gintro

A translation using Gtk via the gintro bindings for Nim, so the code is quite different. We chose also different sizes for the window and the grid, and colors closer to those of the codepen demo.

See window screenshot here.

import gintro/[glib, gobject, gtk, gio, cairo]
 
const
Width = 600
Height = 460
 
type
Color = array[3, float]
Edge {.pure.} = enum LT, TR, RB, BL
 
const
 
Edges = [[LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB],
[LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL],
[TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL],
[TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT],
[RB, TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT],
[RB, RB, TR, TR, LT, LT, BL, BL, RB, RB, TR, TR],
[BL, RB, RB, TR, TR, LT, LT, BL, BL, RB, RB, TR],
[BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB, RB],
[LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB],
[LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL],
[TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL],
[TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT]]
 
Black: Color = [0.0, 0.0, 0.0]
Blue: Color = [0.2, 0.3, 1.0]
White: Color = [1.0, 1.0, 1.0]
Yellow: Color = [0.8, 0.8, 0.0]
 
Colors: array[Edge, array[4, Color]] = [[White, Black, Black, White],
[White, White, Black, Black],
[Black, White, White, Black],
[Black, Black, White, White]]
 
#---------------------------------------------------------------------------------------------------
 
proc draw(area: DrawingArea; context: Context) =
## Draw the pattern in the area.
 
func line(x1, y1, x2, y2: float; color: Color) =
context.setSource(color)
context.moveTo(x1, y1)
context.lineTo(x2, y2)
context.stroke
 
context.setSource(Yellow)
context.rectangle(0, 0, Width, Height)
context.fill()
 
for x in 0..11:
let px = float(86 + x * 36)
for y in 0..11:
let py = float(16 + y * 36)
context.setSource(Blue)
context.rectangle(px, py, 24, 24)
context.fill()
let carray = Colors[Edges[y][x]]
context.setLineWidth(2)
line(px, py, px + 23, py, carray[0])
line(px + 23, py, px + 23, py + 23, carray[1])
line(px + 23, py + 23, px, py + 23, carray[2])
line(px, py + 23, px, py, carray[3])
 
#---------------------------------------------------------------------------------------------------
 
proc onDraw(area: DrawingArea; context: Context; data: pointer): bool =
## Callback to draw/redraw the drawing area contents.
 
area.draw(context)
result = true
 
#---------------------------------------------------------------------------------------------------
 
proc activate(app: Application) =
## Activate the application.
 
let window = app.newApplicationWindow()
window.setSizeRequest(Width, Height)
window.setTitle("Peripheral drift illusion")
 
# Create the drawing area.
let area = newDrawingArea()
window.add(area)
 
# Connect the "draw" event to the callback to draw the pattern.
discard area.connect("draw", ondraw, pointer(nil))
 
window.showAll()
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
let app = newApplication(Application, "Rosetta.Illusion")
discard app.connect("activate", activate)
discard app.run()

Perl[edit]

use strict;
use warnings;
 
my $svg = <<'EOD';
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="1200" height="825">
<rect width="100%" height="100%" fill="#d3d004" />
<defs>
<g id="block">
<polygon points="-25,-25,-25,25,25,25" fill="white" />
<polygon points="25,25,25,-25,-25,-25" fill="black" />
<rect x="-20" y="-20" width="40" height="40" fill="#3250ff" />
</g>
</defs>
EOD

 
for my $X (1..15) {
for my $Y (1..10) {
my $r = int(($X + $Y) / 2) % 4 * 90;
my $x = $X * 75;
my $y = $Y * 75;
my $a = $r > 0 ? "rotate($r,$x,$y) " : '';
$svg .= qq{<use xlink:href="#block" transform="$a translate($x,$y)" />\n}
}
}
$svg .= '</svg>';
 
open my $fh, '>', 'peripheral-drift.svg';
print $fh $svg;
close $fh;

See offsite SVG image

Phix[edit]

Library: Phix/online

You can run this online here.

--
-- demo\rosetta\Peripheral_Drift_Illusion.exw
-- ==========================================
--
-- converted from https://codepen.io/josetxu/pen/rNmXjrq
--
with javascript_semantics
include pGUI.e
Ihandle dlg, canvas
cdCanvas cdcanvas

constant title = "Peripheral Drift Illusion",
         CD_LIGHT_OLIVE = #d3d004,
         CD_PALE_BLUE = #3250ff,
         dxy = {{45,45},{0,45},{0,0},{45,0},{45,45},{0,45}}

function redraw_cb(Ihandle /*ih*/, integer /*posx*/, /*posy*/)
    integer {width, height} = IupGetIntInt(canvas, "DRAWSIZE")
    cdCanvasActivate(cdcanvas)
    cdCanvasSetBackground(cdcanvas, CD_LIGHT_OLIVE)
    cdCanvasClear(cdcanvas)
    integer c = 0,
            cy = floor(height/2)*2-81
    while cy>100 do
        integer d = c,
                cx = 81
        while cx<width-100 do
            cdCanvasSetForeground(cdcanvas, CD_WHITE)
            cdCanvasBox(cdcanvas, cx, cx+45, cy, cy-45)
            cdCanvasSetForeground(cdcanvas, CD_BLACK)
            cdCanvasBegin(cdcanvas, CD_FILL)
            for i=4 to 6 do
                integer {dy,dx} = dxy[i-d]
                cdCanvasVertex(cdcanvas, cx+dx, cy-dy)
            end for
            cdCanvasEnd(cdcanvas)
            cdCanvasSetForeground(cdcanvas, CD_PALE_BLUE)
            cdCanvasBox(cdcanvas, cx+4, cx+41, cy-4, cy-41)
            d = remainder(d+(odd(cx)==odd(cy)),4)
            cx += 63
        end while
        c = remainder(c+4-even(cy),4)
        cy -= 63
    end while
    cdCanvasFlush(cdcanvas)
    return IUP_DEFAULT
end function

function map_cb(Ihandle ih)
    atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
    IupGLMakeCurrent(canvas)
    if platform()=JS then
        cdcanvas = cdCreateCanvas(CD_IUP, canvas)
    else
        cdcanvas = cdCreateCanvas(CD_GL, "10x10 %g", {res})
    end if
    cdCanvasSetBackground(cdcanvas, CD_PARCHMENT)
    return IUP_DEFAULT
end function

function canvas_resize_cb(Ihandle /*canvas*/)
    integer {cw, ch} = IupGetIntInt(canvas, "DRAWSIZE")
    atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
    cdCanvasSetAttribute(cdcanvas, "SIZE", "%dx%d %g", {cw, ch, res})
    return IUP_DEFAULT
end function

procedure main()
    IupOpen()
    canvas = IupGLCanvas("RASTERSIZE=780x600") -- (sensible restore size)
    sequence cb = {"MAP_CB", Icallback("map_cb"),
                   "ACTION", Icallback("redraw_cb"),
                   "RESIZE_CB", Icallback("canvas_resize_cb")}
    IupSetCallbacks(canvas, cb)
    dlg = IupDialog(canvas,`TITLE="%s",PLACEMENT=MAXIMIZED`,{title})
    IupShow(dlg)
    if platform()!=JS then
        IupMainLoop()
        IupClose()
    end if
end procedure
main()

Raku[edit]

use SVG;
 
my @blocks = (1..15 X 1..10).map: -> ($X, $Y) {
my $x = $X * 75;
my $y = $Y * 75;
my $a = (my $r = ($X + $Y) div 2 % 4 * 90) > 0 ?? "rotate($r,$x,$y) " !! '';
:use['xlink:href'=>'#block', 'transform'=>"{$a}translate($x,$y)"]
}
 
'peripheral-drift-raku.svg'.IO.spurt: SVG.serialize(
svg => [
:1200width, :825height,
:rect[:width<100%>, :height<100%>, :fill<#d3d004>],
:defs[
:g[
:id<block>,
:polygon[:points<-25,-25,-25,25,25,25>, :fill<white>],
:polygon[:points<25,25,25,-25,-25,-25>, :fill<black>],
:rect[:x<-20>, :y<-20>, :width<40>, :height<40>, :fill<#3250ff>]
]
],
|@blocks,
]
)

See offsite SVG image

Wren[edit]

Library: DOME

This reproduces the codepen image and does indeed appear to move although it's static. See the output on imgur

import "dome" for Window
import "graphics" for Canvas, Color
 
// signifies the white edges on the blue squares
var LT = 0
var TR = 1
var RB = 2
var BL = 3
 
var Edges = [
[LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB],
[LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL],
[TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL],
[TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT],
[RB, TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT],
[RB, RB, TR, TR, LT, LT, BL, BL, RB, RB, TR, TR],
[BL, RB, RB, TR, TR, LT, LT, BL, BL, RB, RB, TR],
[BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB, RB],
[LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL, RB],
[LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL, BL],
[TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT, BL],
[TR, TR, LT, LT, BL, BL, RB, RB, TR, TR, LT, LT]
]
 
var Light_olive = Color.hex("#d3d004")
var Pale_blue = Color.hex("#3250ff")
 
var W = Color.white
var B = Color.black
 
var Colors = [
[W, B, B, W],
[W, W, B, B],
[B, W, W, B],
[B, B, W, W]
]
 
class PeripheralDrift {
construct new() {
Window.resize(1000, 1000)
Canvas.resize(1000, 1000)
Window.title = "Peripheral drift illusion"
}
 
init() {
Canvas.cls(Light_olive)
for (x in 0..11) {
var px = 90 + x * 70
for (y in 0..11) {
var py = 90 + y * 70
Canvas.rectfill(px, py, 50, 50, Pale_blue)
drawEdge(px, py, Edges[y][x])
}
}
}
 
drawEdge(px, py, edge) {
var c = Colors[edge]
Canvas.line(px, py, px + 46, py, c[0], 4)
Canvas.line(px + 46, py, px + 46, py + 46, c[1], 4)
Canvas.line(px, py + 46, px + 46, py + 46, c[2], 4)
Canvas.line(px, py + 46, px, py, c[3], 4)
}
 
update() {}
 
draw(alpha) {}
}
 
var Game = PeripheralDrift.new()