⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

{{libheader|GDIP}}

; LSystem RosettaCode

#SingleInstance, Off
#NoEnv

	setbatchlines -1

	pToken := Gdip_Startup()
	OnExit, Cleanup

	;Dragon curve
	DragonCurveIter  := 15
	DragonCurveRules := "X>X+YF,Y>FX-Y"
	DragonCurveStart := "FX"
	DragonCurveCmds  := "F+-"
	DragonCurveCmds2 := "draw 3,angle -90,angle +90"
	
	linecount := DispLSystem( DragonCurveIter, DragonCurveRules, DragonCurveStart, DragonCurveCmds, DragonCurveCmds2, 0 )

	MsgBox % "Done!`nThere are " linecount " lines in this L-System.`nCreated in " errorlevel " seconds.`nThis is an average of " (linecount / errorlevel) " lines per second."
return

Esc::
GuiClose:
Cleanup:
	Gdip_Shutdown(pToken)
	ExitApp

DispLSystem( iter, rules, start, names, commands, live = 0 ) {
	DllCall("QueryPerformanceFrequency", "int64*", qpcf), DllCall("QueryPerformanceCounter", "int64*", qpcs)
	
	turtle := LSystem( iter, rules, start )
	cnt := LSys2Lines( turtle, names, commands, lines, w, h )
	
	MakeLayeredWindow( hwnd, hdc, hbm, obm, gfx, w, h )
;	UpdateLayeredWindow(hwnd, hdc)
	DrawLinesToLayeredWindow( lines, cnt, hwnd, hdc, gfx, live )
	UpdateLayeredWindow(hwnd, hdc)
	CleanupLayeredWindow( hwnd, hdc, hbm, obm, gfx )
	
	DllCall("QueryPerformanceCounter", "int64*", qpce)
	return cnt, ErrorLevel := (qpce - qpcs) / qpcf
}

; lsystem and conversion functions
LSystem( cn, Vr, st ) {
;
; cn: iterations
; Vr: list of variables and their rules
;    (V>Result)[,...]
;    Where
;      V: Any single-character variable (must be valid as an AHK variable)
;          a-z 1-9 _ (case insensitive)
;      Result: The rule that this variable results in when replaced
;          [All characters except for comma] (must not start with a quote char: ")
;          OR
;          "[All characters]" (starts and ends with quote char. any literal quote chars inside must be duplicated)
; st : start string
;
; Any non-variable character is treated as a constant
; 
	it := 1
	while it := RegExMatch(Vr, "\s*(\w)\s*>\s*(?:(?!"")([^,]*)|""((?:[^""]|"""")*)"")\s*(?:,|$)", __, it + StrLen(__))
		%__1% := __2 RegExReplace(__3, """""", """"), Vl .= __1 ;parse Variables+Rules
	loop % cn
	{
		tp := "" st, st := ""
		loop, parse, tp
			st .= InStr(Vl, A_LoopField) ? %A_LoopField% : A_LoopField
	}
	return st
}
LSys2Lines( ls, chars, commands, byref lines, byref w, byref h ) { ;based on l-system
	l := 0
	, st%l%x := 0
	, st%l%y := 0
	, st%l%a := 0
	, i := 0
	lines_cap := VarSetCapacity(lines, strlen(ls) * 8)
	maxx := maxy := minx := miny := 0
	loop, parse, commands, `,, %A_Space%%A_Tab%
		cmd%A_Index% := A_LoopField
	loop, parse, ls
	{
		if !p := InStr(chars, A_LoopField)
			continue
		else if inStr(cmd%p%, "save")
			ol := l++
			, st%l%x := st%ol%x
			, st%l%y := st%ol%y
			, st%l%a := st%ol%a
		else if inStr(cmd%p%, "restore")
			l--
		else if inStr(cmd%p%, "angle")
			st%l%a += substr(cmd%p%, 6)
		else if inStr(cmd%p%, "draw")
		{
			d  := SubStr(cmd%p%, 5) + 0     ;distance (in pixels)
			AddLine( lines, i
				, Round(st%l%x)
				, Round(st%l%y)                           
				, Round(st%l%x += cos(st%l%a/57.2957795) * d)
				, Round(st%l%y += sin(st%l%a/57.2957795) * d) )
			, maxx := maxx > st%l%x ? maxx : st%l%x
			, maxy := maxy > st%l%y ? maxy : st%l%y
			, minx := minx < st%l%x ? minx : st%l%x
			, miny := miny < st%l%y ? miny : st%l%y
		}
	}
	xoff := round(abs(minx)) + 5
	yoff := round(abs(miny)) + 5
	w := round(maxx) + xoff + 5
	h := round(maxy) + yoff + 5
	
	OffsetPoints(lines, i, xoff, yoff)
	return i//4
}

; line functions
AddLine( ByRef lines, ByRef i, x1, y1, x2, y2 ) {
	static lines_sz = 0
	if (i = 0 || lines_sz = 0)
		lines_sz := VarSetCapacity(lines)
	if (lines_sz < i * 4 + 16)
		MemPreAlloc(lines, lines_sz, i + 16), lines_sz := VarSetCapacity(lines) 
	  NumPut(x1, lines, 4 * i++, "int")
	, NumPut(y1, lines, 4 * i++, "int")
	, NumPut(x2, lines, 4 * i++, "int")
	, NumPut(y2, lines, 4 * i++, "int")
}
OffsetPoints( ByRef lines, cnt, xoff, yoff ) {
	i := 0
	while i < cnt
		NumPut(NumGet(lines, i * 4, "int") + (i & 1 ? yoff : xoff), lines, 4 * i++, "int")
}
DrawLinesToLayeredWindow( ByRef lines, cnt, hwnd, hdc, gfx, live = 0, color = 0xFF009900, lnw = 1 ) {
	cnt *= 4
	i := 0
	pPen := Gdip_CreatePen(color, 1)
	while i < cnt
	{
		Gdip_DrawLine(gfx, pPen
			, NumGet(lines, 4 * i++, "int")
			, NumGet(lines, 4 * i++, "int")
			, NumGet(lines, 4 * i++, "int")
			, NumGet(lines, 4 * i++, "int"))
	;	if live && !Mod(A_Index, live)
	;		UpdateLayeredWindow(hwnd, hdc)
	}
	Gdip_DeletePen(pPen)
}

; Display functions
MakeLayeredWindow( ByRef hwnd, ByRef hdc, ByRef hbm, ByRef obm, ByRef gfx, w, h, bg = 0xccCCccCC, guiNum = 5 ) {
	w += 1, h += 1
	Gui, %guiNum%:+LastFound +AlwaysOnTop +Owner +E0x80000 -Caption
	Gui, %guiNum%:Show, w%w% h%h% NoActivate
	OnMessage(0x201, "WM_LBUTTONDOWN")

	;gdi
	hwnd := WinExist()
	hdc  := CreateCompatibleDC()
	hbm  := CreateDIBSection(w, h) ;needs to be the same size as gui
	obm  := SelectObject(hdc, hbm)
	gfx  := Gdip_GraphicsFromHDC(hdc)

	;background
	pBr := Gdip_BrushCreateSolid(bg)
	Gdip_FillRoundedRectangle(gfx, pBr, 0, 0, w, h, 3)
	Gdip_DeleteBrush(pBr)
}
CleanupLayeredWindow( ByRef hwnd, ByRef hdc, ByRef hbm, ByRef obm, ByRef gfx ) {
	SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
	, Gdip_DeleteGraphics(gfx)
}
WM_LBUTTONDOWN() { ;for dragging the gui
		static mx, my, wx, wy, hwnd
		hwnd := WinExist()
		CoordMode, Mouse, Screen
		mousegetpos, mx, my
		WinGetPos, wx, wy
		settimer, GuiDrag, 10
	return

	GuiDrag:
		CoordMode, Mouse, Screen
		setwindelay, -1
		if !GetKeyState( "LButton", "P" )
			settimer, %A_ThisLabel%, Off
		else
		{
			MouseGetPos, nmx, nmy
			WinMove, ahk_id %hwnd%, , wx + (nmx - mx), wy + (nmy - my)
		}
	return
}

; binary memory functions
MemPreAlloc( ByRef var, len, add, fillbyte = 0 ) {
	VarSetCapacity(t, len+add, fillbyte)
	MemMove(&t, &var, len)
	VarSetCapacity(var, len+add, 0)
	MemMove(&var, &t, len+add)
}
MemMove( dest, src, len ) {
	; may need to call VarSetCapacity(var, -1) afterwards for display
	DllCall( "msvcrt\memmove", "UInt", dest+0, "UInt", src+0, "Int", len, "Cdecl int" )
}

Output:

[[File:AutoHotkeyDragonCurve.png]]