Koch curve

From Rosetta Code
Koch curve 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.

Draw Koch curve. See details: Koch curve

Kotlin[edit]

Translation of: Ring

This incorporates code from other relevant tasks in order to provide a runnable example. The image produced is saved to disk where it can be viewed with a utility such as EOG.

// Version 1.2.41
 
import java.awt.Color
import java.awt.Graphics
import java.awt.image.BufferedImage
import kotlin.math.*
import java.io.File
import javax.imageio.ImageIO
 
val Double.asI get() = this.toInt()
 
class Point(var x: Int, var y: Int)
 
class BasicBitmapStorage(width: Int, height: Int) {
val image = BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR)
 
fun fill(c: Color) {
val g = image.graphics
g.color = c
g.fillRect(0, 0, image.width, image.height)
}
 
fun setPixel(x: Int, y: Int, c: Color) = image.setRGB(x, y, c.getRGB())
 
fun getPixel(x: Int, y: Int) = Color(image.getRGB(x, y))
 
fun drawLine(x0: Int, y0: Int, x1: Int, y1: Int, c: Color) {
val dx = abs(x1 - x0)
val dy = abs(y1 - y0)
val sx = if (x0 < x1) 1 else -1
val sy = if (y0 < y1) 1 else -1
var xx = x0
var yy = y0
var e1 = (if (dx > dy) dx else -dy) / 2
var e2: Int
while (true) {
setPixel(xx, yy, c)
if (xx == x1 && yy == y1) break
e2 = e1
if (e2 > -dx) { e1 -= dy; xx += sx }
if (e2 < dy) { e1 += dx; yy += sy }
}
}
 
fun koch(x1: Double, y1: Double, x2: Double, y2: Double, it: Int) {
val angle = PI / 3.0 // 60 degrees
val clr = Color.blue
var iter = it
val x3 = (x1 * 2.0 + x2) / 3.0
val y3 = (y1 * 2.0 + y2) / 3.0
val x4 = (x1 + x2 * 2.0) / 3.0
val y4 = (y1 + y2 * 2.0) / 3.0
val x5 = x3 + (x4 - x3) * cos(angle) + (y4 - y3) * sin(angle)
val y5 = y3 - (x4 - x3) * sin(angle) + (y4 - y3) * cos(angle)
 
if (iter > 0) {
iter--
koch(x1, y1, x3, y3, iter)
koch(x3, y3, x5, y5, iter)
koch(x5, y5, x4, y4, iter)
koch(x4, y4, x2, y2, iter)
}
else {
drawLine(x1.asI, y1.asI, x3.asI, y3.asI, clr)
drawLine(x3.asI, y3.asI, x5.asI, y5.asI, clr)
drawLine(x5.asI, y5.asI, x4.asI, y4.asI, clr)
drawLine(x4.asI, y4.asI, x2.asI, y2.asI, clr)
}
}
}
 
fun main(args: Array<String>) {
val width = 512
val height = 512
val bbs = BasicBitmapStorage(width, height)
with (bbs) {
fill(Color.white)
koch(100.0, 100.0, 400.0, 400.0, 4)
val kFile = File("koch_curve.jpg")
ImageIO.write(image, "jpg", kFile)
}
}
Output:
Image is similar to Ring entry.

Perl 6[edit]

Works with: Rakudo version 2018.03

Koch curve, actually a full Koch snowflake.

use SVG;
 
role Lindenmayer {
has %.rules;
method succ {
self.comb.map( { %!rules{$^c} // $c } ).join but Lindenmayer(%!rules)
}
}
 
my $flake = 'F--F--F' but Lindenmayer( { F => 'F+F--F+F' } );
 
$flake++ xx 5;
my @points = (50, 440);
 
for $flake.comb -> $v {
state ($x, $y) = @points[0,1];
state $d = 2 + 0i;
with $v {
when 'F' { @points.append: ($x += $d.re).round(.01), ($y += $d.im).round(.01) }
when '+' { $d *= .5 + .8660254i }
when '-' { $d *= .5 - .8660254i }
}
}
 
say SVG.serialize(
svg => [
width => 600, height => 600, style => 'stroke:rgb(0,0,255)',
:rect[:width<100%>, :height<100%>, :fill<white>],
:polyline[ points => @points.join(','), :fill<white> ],
],
);

See: Koch snowflake

Variation using 90° angles:

use SVG;
 
role Lindenmayer {
has %.rules;
method succ {
self.comb.map( { %!rules{$^c} // $c } ).join but Lindenmayer(%!rules)
}
}
 
my $koch = 'F' but Lindenmayer( { F => 'F+F-F-F+F', } );
 
$koch++ xx 4;
my @points = (450, 250);
 
for $koch.comb -> $v {
state ($x, $y) = @points[0,1];
state $d = -5 - 0i;
with $v {
when 'F' { @points.append: ($x += $d.re).round(.01), ($y += $d.im).round(.01) }
when /< + - >/ { $d *= "{$v}1i" }
}
}
 
say SVG.serialize(
svg => [
width => 500, height => 300, style => 'stroke:rgb(0,0,255)',
:rect[:width<100%>, :height<100%>, :fill<white>],
:polyline[ points => @points.join(','), :fill<white> ],
],
);

See: Koch curve variant with 90° angles

Ring[edit]

 
# Project : Koch curve
# Date  : 2018/04/20
# Author : Gal Zsolt (~ CalmoSoft ~)
# Email  : <[email protected]>
 
load "guilib.ring"
 
paint = null
 
new qapp
{
win1 = new qwidget() {
setwindowtitle("Koch curve")
setgeometry(100,100,500,600)
label1 = new qlabel(win1) {
setgeometry(10,10,400,400)
settext("")
}
new qpushbutton(win1) {
setgeometry(150,500,100,30)
settext("draw")
setclickevent("draw()")
}
show()
}
exec()
}
 
func draw
p1 = new qpicture()
color = new qcolor() {
setrgb(0,0,255,255)
}
pen = new qpen() {
setcolor(color)
setwidth(1)
}
paint = new qpainter() {
begin(p1)
setpen(pen)
 
koch(100, 100, 400, 400, 4)
 
endpaint()
}
label1 { setpicture(p1) show() }
 
func koch x1, y1, x2, y2, it
 
angle = 60*3.14/180
x3 = (2*x1+x2)/3
y3 = (2*y1+y2)/3
 
x4 = (x1+2*x2)/3
y4 = (y1+2*y2)/3
 
x = x3 + (x4-x3)*cos(angle)+(y4-y3)*sin(angle)
y = y3 - (x4-x3)*sin(angle)+(y4-y3)*cos(angle)
 
if (it > 0)
koch(x1, y1, x3, y3, it-1)
koch(x3, y3, x, y, it-1)
koch(x, y, x4, y4, it-1)
koch(x4, y4, x2, y2, it-1)
else
paint.drawline(x1, y1, x3, y3)
paint.drawline(x3, y3, x, y)
paint.drawline(x, y, x4, y4)
paint.drawline(x4, y4, x2, y2)
ok
 

Output image:

Koch curve

Sidef[edit]

Using the LSystem class defined at Hilbert curve.

var rules = Hash(
F => 'F+F--F+F',
)
 
var lsys = LSystem(
width: 800,
height: 800,
 
xoff: -210,
yoff: -90,
 
len: 8,
angle: 60,
color: 'dark green',
)
 
lsys.execute('F--F--F', 4, "koch_snowflake.png", rules)

zkl[edit]

Translation of: Ring

Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl

var width=512, height=512, img=PPM(width,height,0xFfffFF);   // white canvas
var angle=(60.0).toRad();
const green=0x00FF00;
 
fcn koch(x1,y1, x2,y2, it){
x3,y3 := (x1*2 + x2) /3, (y1*2 + y2) /3;
x4,y4 := (x1 + x2*2)/3, (y1 + y2*2)/3;
x:=x3 + (x4-x3)*angle.cos() + (y4-y3)*angle.sin();
y:=y3 - (x4-x3)*angle.sin() + (y4-y3)*angle.cos();
 
if(it>0){
it-=1;
koch(x1,y1, x3,y3, it);
koch(x3,y3, x, y, it);
koch(x, y, x4,y4, it);
koch(x4,y4, x2,y2, it);
}else{
x,y, x1,y1, x2,y2, x3,y3, x4,y4 =
T(x,y, x1,y1, x2,y2, x3,y3, x4,y4).apply("toInt");
img.line(x1,y1, x3,y3, green);
img.line(x3,y3, x, y, green);
img.line(x, y, x4,y4, green);
img.line(x4,y4, x2,y2, green);
}
}
 
koch(100.0,100.0, 400.0,400.0, 4);
img.writeJPGFile("koch.zkl.jpg");

Image at koch curve


Using a Lindenmayer system and turtle graphics to draw a Koch snowflake:

lsystem("F--F--F", Dictionary("F","F+F--F+F"), "+-", 4)  // snowflake
//lsystem("F", Dictionary("F","F+F--F+F"), "+-", 3) // curve
: turtle(_);
 
fcn lsystem(axiom,rules,consts,n){ // Lindenmayer system --> string
foreach k in (consts){ rules.add(k,k) }
buf1,buf2 := Data(Void,axiom).howza(3), Data().howza(3); // characters
do(n){
buf1.pump(buf2.clear(), rules.get);
t:=buf1; buf1=buf2; buf2=t; // swap buffers
}
buf1.text // n=4 snow flake --> 1,792 characters
}
 
fcn turtle(koch){
const D=10.0;
dir,deg60, x,y := 0.0, (60.0).toRad(), 20.0, 710.0; // turtle; x,y are float
img,color := PPM(850,950), 0x00ff00;
foreach c in (koch){
switch(c){
case("F"){ // draw forward
dx,dy := D.toRectangular(dir);
tx,ty := x,y; x,y = (x+dx),(y+dy);
img.line(tx.toInt(),ty.toInt(), x.toInt(),y.toInt(), color);
}
case("-"){ dir-=deg60 } // turn right 60*
case("+"){ dir+=deg60 } // turn left 60*
}
}
img.writeJPGFile("kochSnowFlake.zkl.jpg");
}

Image at Koch snow flake