Code archives/Graphics/2d lightning

This code has been declared by its author to be Public Domain code.

Download source code

2d lightning by AdamStrange2015
taken from the german blitz site and modified to support color
SuperStrict



Type TLightning
	Const MIN_SEG_LENGTH:Int = 20			'Minimum segment length
	Const MIN_SEG_BRANCH_LENGTH:Int = 5		'Minimum segment length on branches
	Const BRANCH_ANGLE_MOD:Int = 40			'Max angle difference between mainlightning and branches
	
	Field glowred:Int = 100
	Field glowgreen:Int = 100
	Field glowblue:Int = 255
	
	Field segments:TList = CreateList()		'List of segments of the mainlightning
	Field branches:TList = CreateList()		'List of branches
	Field branches2:TList = CreateList()	'Branches of the segmentized branches
	Field generations:Int = 4				'Iterations of seperation of the mainlightning
	Field offset:Int = 10					'Segments offset
	
	
	Rem
	bbdoc:Creates a new lightning from x1, y1 to x2, y2.
	Public Function
	End Rem
	Function getInstance:TLightning(x1:Int, y1:Int, x2:Int, y2:Int)
		Local instance:TLightning = New TLightning
		instance.setStartEndPoint(x1, y1, x2, y2)
		
		Return instance
	End Function
	
	
	Rem
	bbdoc:Changes the color of the lightning
	Public Function
	End Rem
	Method SetGlowColor(r:Int, g:Int, b:Int)
		glowred = r
		glowgreen = g
		glowblue = b
	End Method


	Rem
	bbdoc:Sets new start- and endpoint coordinates.
	Public Method
	End Rem
	Method setStartEndPoint(x1:Int, y1:Int, x2:Int, y2:Int)
		'Create segment from x1,y1 to x2,y2
		Local startpoint:TPoint = TPoint.getInstance(x1, y1)
		Local endpoint:TPoint = TPoint.getInstance(x2, y2)
		Local firstSeg:TSegment = TSegment.getInstance(startpoint, endpoint)
		
		'Remove old lightning
		Self.segments.Clear()
		Self.branches.Clear()
		Self.branches2.Clear()
		
		'Create the lightning out of the first segment
		Self.segments.AddFirst(firstSeg)
		Self.segmentize(Self.segments, Self.generations, Self.offset)
		Self.branches = Self.createBranches(Self.segments)
		Self.segmentize(Self.branches, Self.generations - 1, Self.offset - 7, True)
		Self.branches2 = Self.createBranches(Self.branches)
		Self.segmentize(Self.branches2, Self.generations - 3, Self.offset - 7, True)
	End Method
	
	
	Rem
	bbdoc:Draws the lightning (Scale must be set to 1.0/1.0)
	Public Method
	End Rem
	Method draw()
		SetBlend(ALPHABLEND)
	
		'Branch2 Glow
		Self.setDrawings(0.4, 3, True)
		Self.drawSegments(Self.branches2)
		'Branches2
		Self.setDrawings(0.5, 1)
		Self.drawSegments(Self.branches2)
		
		'Branch Glow
		Self.setDrawings(0.5, 5, True)
		Self.drawSegments(Self.branches)
		'Branches
		Self.setDrawings(0.6, 1)
		Self.drawSegments(Self.branches)
		
		'Glow Paths
		Self.setDrawings(0.09, 20, True)
		Self.drawSegments(Self.segments)
		Self.setDrawings(0.25, 11, True)
		Self.drawSegments(Self.segments)
		Self.setDrawings(0.55, 6, True)
		Self.drawSegments(Self.segments)
		'Original Paths
		SetColor(255, 255, 255)
		SetAlpha(0.45)
		Local counter:Int = 0
		For Local seg:TSegment = EachIn Self.segments
			counter:+1
			Local thickness:Float = 0.3 * Float(Self.segments.Count() - counter)
			If thickness < 2.0 Then thickness = 2.0
			SetLineWidth(thickness)
			DrawLine(seg.startpoint.x, seg.startpoint.y, seg.endpoint.x, seg.endpoint.y)
		Next
	End Method
	
	
	Rem
	bbdoc:Private Method (don__COMMENT18__
	Draws a list of segments.
	End Rem
	Method drawSegments(givenList:TList)
		Local realLineWidth:Float = GetLineWidth()
		For Local seg:TSegment = EachIn givenList
			Local lineWidth:Float = realLineWidth/2
			
			Local ps1point:TPoint
			Local ps2point:TPoint
			Local pe1point:TPoint
			Local pe2point:TPoint
			Local lastSeg:TSegment = seg.lastSegment
			Local nextSeg:TSegment = seg.nextSegment
			
			Local tvec:TPoint = seg.endpoint.sub(seg.startpoint)
			Local tvecn:TPoint = tvec.normalize()
			Local tnorm:TPoint = tvec.normalize().normal()
			
			If (lastSeg=Null) Then
				ps1point = seg.startpoint.add(tnorm.mult(lineWidth))
				ps2point = seg.startpoint.add(tnorm.mult(-lineWidth))
			Else
				Local lvec:TPoint = lastSeg.endpoint.sub(lastSeg.startpoint)
				Local lnorm:TPoint = lvec.normalize().normal()
				
				ps1point = seg.startpoint.add(lnorm.mult(lineWidth))
				ps2point = seg.startpoint.add(lnorm.mult(-lineWidth))
			EndIf
			
			If (nextSeg=Null) Then
				lineWidth = lineWidth/2
			EndIf
			
			pe1point = seg.endpoint.add(tnorm.mult(-lineWidth))
			pe2point = seg.endpoint.add(tnorm.mult(lineWidth))
			
			
			Local corners:Float[] = [ps1point.x,ps1point.y,ps2point.x,ps2point.y,pe1point.x,pe1point.y,pe2point.x,pe2point.y]
			
			DrawPoly(corners)
		Next
	End Method
	
	
	Rem
	bbdoc:Private Method (don__COMMENT19__
	Sets the color to "Glow-Color"
	End Rem
	Method setDrawings(alpha:Float, lineThickness:Float, isGlowing:Byte = False)
		SetColor(255, 255, 255)
		If isGlowing Then SetColor(glowred, glowgreen, glowblue)
		SetAlpha(alpha)
		SetLineWidth(lineThickness)
	End Method
	
	
	Rem
	bbdoc:Private Method (don__COMMENT20__
	Splits a line (segment) into two lines, which are moved by maxOffset at one point (start or endpoint of the line).
	End Rem
	Method segmentize(givenList:TList, generations:Int, offset:Int, isBranch:Byte = False)
		'Preconditions
		If givenList.IsEmpty() Then Return
		Local minLength:Int = TLightning.MIN_SEG_LENGTH
		If isBranch Then minLength = TLightning.MIN_SEG_BRANCH_LENGTH
		
		'Start segmentation
		For Local i:Int = 1 To generations
			Local workList:TList = givenList.Copy()
			For Local seg:TSegment = EachIn workList
			
					'Check if the segments length allows a split into 2 segments
					If TDistance.pointToPoint(seg.startpoint.x, seg.startpoint.y, seg.endpoint.x, seg.endpoint.y) >= minLength Then
						'The original segment is obsolete, as we replace it with 2 new ones
					Local lastSegment:TSegment = seg.lastSegment
					Local nextSegment:TSegment = seg.nextSegment
		    			givenList.Remove(seg)
			
			    		Local midpoint:TPoint = seg.getMidPoint()
						Local randomAngle:Int = 90
						If Rand(1, 2) = 1 Then randomAngle = -90
						'Move the midpoint
						midpoint.x:+(Cos(seg.angle + randomAngle) * offset)
						midpoint.y:+(Sin(seg.angle + randomAngle) * offset)
			
						'Add the new segments
						Local seg1:TSegment = TSegment.getInstance(seg.startpoint, midpoint)
						Local seg2:TSegment = TSegment.getInstance(midpoint, seg.endpoint)
						seg1.lastSegment = lastSegment
						seg1.nextSegment = seg2
						seg2.lastSegment = seg1
						seg2.nextSegment = nextSegment
						
						If (lastSegment<>Null) Then lastSegment.nextSegment = seg1
						If (nextSegment<>Null) Then nextSegment.lastSegment = seg2
						
						givenList.AddLast(seg1)
						givenList.AddLast(seg2)
					End If
				Next
			Next
	End Method
	
	
	Rem
	bbdoc:Private Method (don__COMMENT27__
	Creates a branch at every segments startpoint.
	End Rem
	Method createBranches:TList(givenList:TList)
		'Preconditions
		If givenList.IsEmpty() Then Return Null
	
		Local returnList:TList = New TList
		Local i:Int = 0
	
		For Local seg:TSegment = EachIn givenList
			i:+1
			Local branchAngle:Int = seg.angle + Rand(-TLightning.BRANCH_ANGLE_MOD, TLightning.BRANCH_ANGLE_MOD)
			Local branchLength:Int = Float((Rnd(1.0, 3.0) * TDistance.pointToPoint(seg.startpoint.x, seg.startpoint.y, seg.endpoint.x, seg.endpoint.y))) * Float((Float(i) / Float(givenList.Count())))
			Local branchX:Int = seg.endpoint.x + Cos(branchAngle) * branchLength
			Local branchY:Int = seg.endpoint.y + Sin(branchAngle) * branchLength
			Local branchpoint:Tpoint = TPoint.getInstance(branchX, branchY)
			
			Local branch:TSegment = TSegment.getInstance(seg.endpoint, branchpoint)
			
			returnList.AddLast(branch)
		Next
		Return returnList
	End Method
	
End Type



Rem
bbdoc:Defines a linesegment between two points in 2D space.
End Rem
Type TSegment
	Field startpoint:TPoint
	Field endpoint:TPoint
	Field angle:Int
	Field lastSegment:TSegment
	Field nextSegment:TSegment
	
	
	Rem
	bbdoc:Constructor.
	End Rem
	Function getInstance:TSegment(startpoint:TPoint, endpoint:TPoint)
		Local instance:TSegment = New TSegment
		instance.startpoint = startpoint
		instance.endpoint = endpoint
		instance.angle = ATan2((endpoint.y - startpoint.y), (endpoint.x - startpoint.x))
		Return instance
	End Function
	
	
	Rem
	bbdoc:Returns a point on the mid of the segment.
	End Rem
	Method getMidPoint:TPoint()
		Local midpoint:Tpoint = New TPoint
		midpoint.x = (startpoint.x + endpoint.x)/2
		midpoint.y = (startpoint.y + endpoint.y)/2
		Return midpoint
	End Method
End Type



Rem
bbdoc:2D Point helper class.
End Rem
Type TPoint
	Field x:Float
	Field y:Float
	
	
	Rem
	bbdoc:Constructor.
	End Rem
	Function getInstance:TPoint(x:Float, y:Float)
		Local instance:TPoint = New TPoint
		instance.x = x
		instance.y = y
		Return instance
	End Function
	
	Rem
	bbdoc:Addiert zwei TPoints zusammen (Vektorrechnung)
	End Rem
	Method add:TPoint(other:TPoint)
		Return getInstance(x+other.x,y+other.y)
	End Method
	
	Rem
	bbdoc:Subtrahiert zwei TPoints (Vektorrechnung)
	End Rem
	Method sub:TPoint(other:TPoint)
		Return getInstance(x-other.x,y-other.y)
	End Method
	
	Rem
	bbdoc:Multipliziert einen TPoint mit einem Skalar (Vektorrechnung)
	End Rem
	Method mult:TPoint(f:Float)
		Return getInstance(x*f,y*f)
	End Method
	
	Rem
	bbdoc:Liefert die Laenge des Vektors (Vektorrechnung)
	End Rem
	Method length:Float()
		Return Sqr(x*x+y*y)
	End Method
	
	Rem
	bbdoc:Normalisiert den Vektor (Vektorrechnung)
	End Rem
	Method normalize:TPoint()
		Return mult(1.0/length())
	End Method
	
	Rem
	bbdoc:Erstellt einen Normalvektor (Vektorrechnung)
	End Rem
	Method normal:TPoint()
		Return getInstance(y,-x)
	End Method
	
	Rem
	bbdoc:Liefert das Skalarprodukt zweier Vektoren (Vektorrechnung)
	End Rem
	Method dot:Float(other:TPoint)
		Return (x*other.x+y*other.y)
	End Method
End Type



Rem
bbdoc:Helper tool to calculate the distance between 2 points in a 2D environment.
End Rem
Type TDistance Abstract
	
	Rem
	bbdoc:Berechnet die Distanz zwischen 2 Punkten.
	End Rem
	Function pointToPoint:Double(x1:Double, y1:Double, x2:Double, y2:Double)
		Return Sqr(TDistance.quad(x2 - x1) + TDistance.quad(y2 - y1))
	End Function
	
	
	Rem
	bbdoc:Quadriert einen Integer.
	End Rem
	Function quad:Double(v:Double)
		Return (v * v)
	End Function
	
End Type



'Test
'Include "Lightning.bmx"

Graphics 1024, 768
Global blitz:TLightning = TLightning.getInstance(100, 100, 500, 500)
blitz.setGlowColor(255,0,0)
Global oldMausX:Int
Global oldMausY:Int
SetClsColor(10, 10, 10)

Repeat
	Cls
	
	If MouseX() <> oldMausX Or MouseY() <> oldMausY Then
		oldMausX = MouseX()
		oldMausY = MouseY()
		blitz.setStartEndPoint(100, 100, MouseX(), MouseY())
	End If
	
	blitz.draw()
	
	Flip
Until AppTerminate()

Comments

jankupila2015
Cool!


Code Archives Forum