Rem bbdoc: noddybox.vectorgfx EndRem Module noddybox.vectorgfx ModuleInfo "Framework: 2D Vector Graphics classes" ModuleInfo "Copyright: Public Domain" ModuleInfo "Author: Ian Cowburn" ModuleInfo "Version: $Revision$" ' $Id$ Strict Import brl.linkedlist Import brl.math Import brl.endianstream Import brl.max2d Import noddybox.vector Import noddybox.algorithm Rem bbdoc: Defines a method for drawing vector lines EndRem Type TVectorGfxLineStyle Abstract Rem bbdoc: Draws a vector line. returns: The or'ed mask of all collision IDs overwritten in @colmap by this line, or zero if @colmap is null. about: This routine must draw a line from @x1, @y1 to @x2, @y2 using the colour @r,@g,@b. about: @id, @obj and @line must be written into the collision map @colmap if @colmap is not null and @actor is false. about: If @colmap is set and @actor true, then collisions must be returned but no points written into the collision map. about: If @colmap is set, @actor is true and @list is not null then any collisions should be recorded into this list about: (no requirement for duplicate checking). about: For an example of creating a line drawing routine view the LineSolid type in the private section of this module. EndRem Method Draw:Int(x1:Int, y1:Int, x2:Int, y2:Int, r:Int, g:Int, b:Int, id:Int, obj:TVectorGfxObject, line:Int, colmap:TVectorGfxCollisionMap, actor:Int, list:TList) Abstract End Type Rem bbdoc: Set to use solid lines for drawing. about: This mode draws vector lines as solid colour lines using the current alpha, and is the default mode. EndRem Function VectorGfxSetSolid() Static.linedraw=New LineSolid End Function Rem bbdoc: Set to use alpha lines for drawing. about: This mode draws vector lines using a @line_alpha for the lines, except for the end-points drawn with @end_alpha. EndRem Function VectorGfxSetAlpha(line_alpha:Double,end_alpha:Double) Local l:LineAlpha=New LineAlpha l.la=line_alpha l.ea=end_alpha Static.linedraw=l End Function Rem bbdoc: Set to use thick lines for drawing. about: This mode draws vector lines using solid line, surrounded by points of alpha @line_alpha. EndRem Function VectorGfxSetThickAlpha(line_alpha:Double) Local l:ThickAlpha=New ThickAlpha l.a=line_alpha Static.linedraw=l End Function Rem bbdoc: Set to use a custom drawing method. EndRem Function VectorGfxSetCustom(linedraw:TVectorGfxLineStyle) Static.linedraw=linedraw End Function Rem bbdoc: Defines a point in the vector graphics collision map. EndRem Type TVectorGfxCollision Rem bbdoc: The X co-ordinate of this collision (in the object co-ordinate space). EndRem Field x:Int Rem bbdoc: The Y co-ordinate of this collision (in the object co-ordinate space). EndRem Field y:Int Rem bbdoc: The collision masks at this point. EndRem Field mask:Int Rem bbdoc: The last vector object that wrote at this point. EndRem Field obj:TVectorGfxObject Rem bbdoc: The index of the line in the last vector object that wrote at this point. EndRem Field line:Int Function Create:TVectorGfxCollision(x:Int, y:Int, mask:Int, obj:TVectorGfxObject, line:Int) Local o:TVectorGfxCollision=New TVectorGfxCollision o.x=x o.y=y o.mask=mask o.obj=obj o.line=line Return o End Function End Type Rem bbdoc: Defines a vector graphics collision map. about: Collisions in the library are defined using a bitmask (allowing 32 collision masks). about: Note that with the way that lines are drawn in an object you are likely about: to receive collisions for the IDs used by the lines in the object. EndRem Type TVectorGfxCollisionMap Field width:Int Field height:Int Field xoff:Int Field yoff:Int Field data:TVectorGfxCollision[,] Rem bbdoc: Create a collision map. returns: The created collision map. about: @width and @height are the size of the collision map. EndRem Function Create:TVectorGfxCollisionMap(width:Int, height:Int) Local o:TVectorGfxCollisionMap=New TVectorGfxCollisionMap o.width=width o.height=height o.xoff=0 o.yoff=0 o.Clear() Return o End Function Rem bbdoc: Set the offset of the collision map. about: @x and @y are the offsets into the co-ordinate space where the collision map lives. EndRem Method SetOffset(x:Int, y:Int) xoff=x yoff=y End Method Rem bbdoc: Get a collision point. returns: The collision point, or null if nothing drawn there. about: @x and @y are the co-ordinates (in the objects co-ordinate space, not the collision map space) EndRem Method GetCollision:TVectorGfxCollision(x:Int, y:Int) If x=xoff+width Or y=yoff+height Return Null Else Return data[x-xoff,y-yoff] EndIf End Method Rem bbdoc: Set a collision point. returns: The current collision mask for that point, or zero if the point is out of the map's range. about: @x and @y are the co-ordinates (in the objects co-ordinate space, not the collision map space) EndRem Method SetCollision:Int(x:Int, y:Int, id:Int, obj:TVectorGfxObject, line:Int) If x=xoff+width Or y=yoff+height Return 0 EndIf Local ax:Int=x-xoff Local ay:Int=y-yoff Local cur:TVectorGfxCollision=data[ax,ay] If Not cur cur=TVectorGfxCollision.Create(x,y,id,obj,line) data[ax,ay]=cur Else cur.mask:|id cur.obj=obj cur.line=line EndIf Return cur.mask End Method Rem bbdoc: Clear the collision map EndRem Method Clear() data=New TVectorGfxCollision[width,height] End Method End Type Rem bbdoc: Defines a 2D vector graphics object EndRem Type TVectorGfxObject Rem bbdoc: The points making up this object EndRem Field points:TVectorGfxPoint[] Rem bbdoc: The lines making up this object EndRem Field lines:TVectorGfxLine[] Rem bbdoc: The X co-ordinate of the object EndRem Field x:Int Rem bbdoc: The Y co-ordinate of the object EndRem Field y:Int Rem bbdoc: The angle of the object, as degrees*10. Must be within the range 0-3599 inclusive. EndRem Field ang:Int Rem bbdoc: The scale of the object. EndRem Field scale:Double Field rotated:TVectorGfxPoint[] Rem bbdoc: Creates a new empty object returns: The created object. EndRem Method New() points=Null lines=Null rotated=Null x=0 y=0 ang=0 scale=1 End Method Rem bbdoc: Clones this object about: Returns a copy of this object, with its own copy of the line and points. EndRem Method Clone:TVectorGfxObject() Local o:TVectorGfxObject=New TVectorGfxObject o.x=x o.y=y o.ang=ang o.scale=scale o.SetPoints(points) o.SetLines(lines) Return o End Method Rem bbdoc: Sets the points for this object about: @pa is an array of points for this object. EndRem Method SetPoints(pa:Object[]) points=New TVectorGfxPoint[pa.length] For Local f:Int=0 Until pa.length points[f]=TVectorGfxPoint(pa[f]) Next End Method Rem bbdoc: Sets the lines for this object. about: @la is the array of lines for this object. EndRem Method SetLines(la:Object[]) lines=New TVectorGfxLine[la.length] For Local f:Int=0 Until la.length lines[f]=TVectorGfxLine(la[f]) Next End Method Rem bbdoc: A line's adjusted co-ordinates. returns: The rotated points (in object co-ordinates) of the line. about: @i is the index of the line to return the points for. about: Calling it before drawing then will cause runtime errors. EndRem Method AdjustedCoords:TVectorGfxPoint[](i:Int) Local p:TVectorGfxPoint[]=New TVectorGfxPoint[2] Local l:TVectorGfxLine=lines[i] p[0]=rotated[l.i1] p[1]=rotated[l.i2] Return p End Method Rem bbdoc: A line's normal. returns: The normal of the selected line. about: @i is the index of the line to return the normal of. This will return the normal due to the rotation of the object after the object is drawn. about: Calling it before drawing then will cause runtime errors. EndRem Method Normal:TVector(i:Int) Local l:TVectorGfxLine=lines[i] Local v:TVector=TVector.Create(rotated[l.i2].y-rotated[l.i1].y,rotated[l.i1].x-rotated[l.i2].x) v.Normalise() Return v End Method Rem bbdoc: Loads the object from the supplied @filename. returns: The loaded object, or null if the file could not be opened. EndRem Function Load:TVectorGfxObject(filename:String) Local s:TStream=ReadStream(filename) If Not s Return Null EndIf s=LittleEndianStream(s) Local o:TVectorGfxObject=New TVectorGfxObject o.x=s.ReadInt() o.y=s.ReadInt() o.ang=s.ReadInt() Local l:TList=CreateList() Local i:Int=s.ReadInt() For Local f=0 Until i l.AddLast(TVectorGfxPoint.Load(s)) Next o.SetPoints(l.ToArray()) l.Clear() i=s.ReadInt() For Local f=0 Until i l.AddLast(TVectorGfxLine.Load(s)) Next o.SetLines(l.ToArray()) s.Close() Return o End Function Rem bbdoc: Saves the object to the supplied @filename. EndRem Method Save(filename:String) Local s:TStream=WriteStream(filename) If Not s Return Null EndIf s=LittleEndianStream(s) s.WriteInt(x) s.WriteInt(y) s.WriteInt(ang) s.WriteInt(points.length) For Local p:TVectorGfxPoint=EachIn points p.Save(s) Next s.WriteInt(lines.length) For Local l:TVectorGfxLine=EachIn lines l.Save(s) Next s.Close() End Method Rem bbdoc: Draw the object. returns: A bitmask of collisions. about: Draws the object. If @colmap is null then no collision processing takes place. about: If @actor is true then collision processing takes place, but the object will about: not write into the collision map. about: If @list is not null then any collisions will be written into this list. This only applies if @colmap is set and @actor true. EndRem Method Draw:Int(colmap:TVectorGfxCollisionMap=Null, actor:Int=False, list:TList=Null) Local col:Int=0 rotated=New TVectorGfxPoint[points.length] For Local f:Int=0 Until points.length Local p:TAlgoPointD=DoRotateD(points[f].x*scale,points[f].y*scale,ang) rotated[f]=TVectorGfxPoint.Create(p.x,p.y) Next Local i:Int=0 For Local l:TVectorGfxLine=EachIn lines col:|Static.linedraw.draw(x+rotated[l.i1].x,y+rotated[l.i1].y,x+rotated[l.i2].x,y+rotated[l.i2].y,l.r,l.g,l.b,l.id,Self,i,colmap,actor,list) i:+1 Next Return col End Method End Type Rem bbdoc: Defines a point EndRem Type TVectorGfxPoint Field x:Int Field y:Int Rem bbdoc: Create a point. returns: The created point. about: @x and @y are the co-ordinates of the point. EndRem Function Create:TVectorGfxPoint(x:Int, y:Int) Local o:TVectorGfxPoint=New TVectorGfxPoint o.x=x o.y=y Return o End Function Function Load:TVectorGfxPoint(s:TStream) Local o:TVectorGfxPoint o=New TVectorGfxPoint o.x=s.ReadInt() o.y=s.ReadInt() Return o End Function Method Save(s:TStream) s.WriteInt(x) s.WriteInt(y) End Method End Type Rem bbdoc: Defines a line EndRem Type TVectorGfxLine Field i1:Int Field i2:Int Field id:Int Field r:Int Field g:Int Field b:Int Rem bbdoc: Create a line. returns: The created line. about: @i1 and @i2 are the indexes into the point list. @r, @g and @b is the line's colour. @id is the collision map mask for the line. EndRem Function Create:TVectorGfxLine(i1:Int, i2:Int, r:Int, g:Int, b:Int, id:Int) Local o:TVectorGfxLine=New TVectorGfxLine o.i1=i1 o.i2=i2 o.r=r o.g=g o.b=b o.id=id Return o End Function Function Load:TVectorGfxLine(s:TStream) Local o:TVectorGfxLine o=New TVectorGfxLine o.i1=s.ReadInt() o.i2=s.ReadInt() o.r=s.ReadInt() o.g=s.ReadInt() o.b=s.ReadInt() o.id=s.ReadInt() Return o End Function Method Save:TVectorGfxLine(s:TStream) s.WriteInt(i1) s.WriteInt(i2) s.WriteInt(r) s.WriteInt(g) s.WriteInt(b) s.WriteInt(id) End Method End Type Private Type Static Global linedraw:TVectorGfxLineStyle Function Init() linedraw=New LineSolid End Function End Type Type LineSolid Extends TVectorGfxLineStyle Method Draw:Int(x1:Int, y1:Int, x2:Int, y2:Int, r:Int, g:Int, b:Int, id:Int, obj:TVectorGfxObject, line:Int, colmap:TVectorGfxCollisionMap, actor:Int, list:TList) Local mask:Int=0 Local lp:TList=DoLine(x1,y1,x2,y2) SetColor(r,g,b) For Local p:TAlgoPoint=EachIn lp If colmap If actor Local c:TVectorGfxCollision=colmap.GetCollision(p.x,p.y) If c mask:|c.mask If list list.AddLast(c) EndIf EndIf Else mask:|colmap.SetCollision(p.x,p.y,id,obj,line) EndIf EndIf Plot(p.x,p.y) Next Return mask End Method End Type Type LineAlpha Extends TVectorGfxLineStyle Field la:Double Field ea:Double Method Draw:Int(x1:Int, y1:Int, x2:Int, y2:Int, r:Int, g:Int, b:Int, id:Int, obj:TVectorGfxObject, line:Int, colmap:TVectorGfxCollisionMap, actor:Int, list:TList) Local mask:Int=0 Local lp:TList=DoLine(x1,y1,x2,y2) Local p:TAlgoPoint Local a:Double=GetAlpha() SetColor(r,g,b) SetAlpha(ea) p=TAlgoPoint(lp.RemoveFirst()) If colmap If actor Local c:TVectorGfxCollision=colmap.GetCollision(p.x,p.y) If c mask:|c.mask If list list.AddLast(c) EndIf EndIf Else mask:|colmap.SetCollision(p.x,p.y,id,obj,line) EndIf EndIf Plot(p.x,p.y) If lp.Count()=0 Return id EndIf p=TAlgoPoint(lp.RemoveLast()) If colmap If actor Local c:TVectorGfxCollision=colmap.GetCollision(p.x,p.y) If c mask:|c.mask If list list.AddLast(c) EndIf EndIf Else mask:|colmap.SetCollision(p.x,p.y,id,obj,line) EndIf EndIf Plot(p.x,p.y) SetAlpha(la) For Local p:TAlgoPoint=EachIn lp If colmap If actor Local c:TVectorGfxCollision=colmap.GetCollision(p.x,p.y) If c mask:|c.mask If list list.AddLast(c) EndIf EndIf Else mask:|colmap.SetCollision(p.x,p.y,id,obj,line) EndIf EndIf Plot(p.x,p.y) Next SetAlpha(a) Return mask End Method End Type Type ThickAlpha Extends TVectorGfxLineStyle Field a:Double Method PlotPoint(x,y) SetAlpha(a) DrawRect(x-1,y-1,3,3) SetAlpha(1.0) Plot(x,y) End Method Method Draw:Int(x1:Int, y1:Int, x2:Int, y2:Int, r:Int, g:Int, b:Int, id:Int, obj:TVectorGfxObject, line:Int, colmap:TVectorGfxCollisionMap, actor:Int, list:TList) Local mask:Int=0 Local lp:TList=DoLine(x1,y1,x2,y2) SetColor(r,g,b) For Local p:TAlgoPoint=EachIn lp If colmap If actor Local c:TVectorGfxCollision=colmap.GetCollision(p.x,p.y) If c mask:|c.mask If list list.AddLast(c) EndIf EndIf Else mask:|colmap.SetCollision(p.x,p.y,id,obj,line) EndIf EndIf PlotPoint(p.x,p.y) Next Return mask End Method End Type Static.Init()