About Monkey 2 › Forums › Monkey 2 Code Library › Visual helpers
This topic contains 8 replies, has 5 voices, and was last updated by 
 Hezkore
 1 year, 1 month ago.
- 
		AuthorPosts
 - 
		
			
				
March 4, 2018 at 6:57 am #13850
I’ve been using these simple classes a lot lately, and they help me debug things more quickly.
The first one is the “Echo” system, it basically prints out values to a screen overlay. You don’t have to worry about where each line falls, only the initial placement of the text box. You can easily extend it, check out how the “Entity” class is extended to add recursive echoing of all entities in a scene.

Usage is simple. For instance, to add the current FPS do:
Monkey1Echo.Add( "FPS="+App.FPS, Color.Green )Then at the end of your render loop you call:
Monkey12345If _drawInfoEcho.Draw( canvas, 10, 8, 0.75 )ElseEcho.Clear()EndIt’s important to call “Clear()” if you’re not drawing anything, otherwise the stacks will explode…
Use Echo.font to set the font to your liking.The second helper is a simple Graph system, extremely helpful when debugging animation problems. It will graph any values over time, with different colors and user specified scale/range.
Use it by adding named values (on every frame, it won’t add a new “channel” twice if it has the same name), like this:Monkey123Graph.Add( "Noise Rx", Entity.LocalRx, Color.Red )Graph.Add( "Noise Ry", Entity.LocalRy, Color.Green )Graph.Add( "Noise Rz", Entity.LocalRz, Color.Blue )And then call at the end of your render loop, just like Echo, something like:
Monkey123If _drawGraphGraph.DrawAll( canvas, Height * 0.3, 1.0 )EndThe “height” value is how tall the positive values of the curve will be, in pixels, and the range is the maximum visible value. Better names? Amplitude maybe?
Anyway, here’s the code for Echo:
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495Namespace util#Import "<mojo>"#Import "<std>"Using mojo..Using std..Class EchoGlobal font:FontPrivateGlobal _textStack:= New StringStackGlobal _colorStack:= New Stack<Color>Public'************************************* Public static functions *************************************'Use this to add text to the Echo DisplayFunction Add( text:String, color:Color = Color.White )_textStack.Push( text )_colorStack.Push( color )End'This will echo the entire mojo3d scene hierarchy, recursivelyFunction Add( scene:Scene )Add( "Scene", Color.LightGrey )For Local e := EachIn scene.GetRootEntities()e.Echo( ". " )EndEnd'Draws all echo messagesFunction Draw( canvas:Canvas, x:Int=0, y:Int=0, rectAlpha:Float = 0.5, border:Int = 5 )If font Then canvas.Font = fontcanvas.PushMatrix()Local lineY := 2Local maxWidth := 0'Figure out dimensionsFor Local n := 0 Until _textStack.Lengthlocal text := _textStack[ n ]Local size := canvas.Font.TextWidth( text )If( size > maxWidth ) maxWidth = sizeNext'Draw rectIf rectAlpha > 0.01canvas.Alpha = rectAlphacanvas.Color = New Color( 0, 0, 0 )canvas.DrawRect( x-border, y+lineY-border, maxWidth+border+border, (canvas.Font.Height*_textStack.Length)+border+border )End'Draw textFor Local n := 0 Until _textStack.Lengthlocal text := _textStack[ n ]canvas.BlendMode = BlendMode.Alphacanvas.Alpha = 1.0canvas.Color = _colorStack[ n ]canvas.DrawText( text, x, y+lineY )lineY += canvas.Font.HeightNextcanvas.PopMatrix()Clear()End'Clears stacks. MUST BE CALLED when not drawing the current messages or the stacks will grow until they explode.Function Clear()_textStack.Clear()_colorStack.Clear()EndEnd'******************************** Extensions ********************************Class Entity ExtensionMethod Echo( tab:String )util.Echo.Add( tab + Name )For Local c := Eachin Childrenc.Echo( tab + ". " )NextEndEndAnd for Graph…
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139Namespace util#Import "<mojo>"#Import "<std>"Using mojo..Using std..Class GraphField color:ColorGlobal bgAlpha:= 0.75Global rightBorder := 10Global leftBorder := 100PrivateField values:= New Deque<Float>Global cursor:= 0Global all:= New Map<String, Graph>'************************************* Public methods *************************************PublicMethod New( name:String, color:Color )Self.color = colorall.Add( name, Self )End'************************************* Private methods *************************************PrivateMethod AddValue( v:Float )Local c := cursorIf cursor >= values.Lengthvalues.AddLast( v )Elsevalues[ cursor ] = vEndEndMethod Draw( canvas:Canvas, scale:Float )Local w := canvas.Viewport.Width - rightBorder - leftBorderLocal h := canvas.Viewport.HeightFor Local x := 0 Until values.LengthLocal y0:DoubleLocal y:= (-values[x] * scale) + h/2.0If x = 0y0 = yElsey0 = (-values[x-1] * scale) + h/2.0Endcanvas.DrawLine( x-1+leftBorder, y0, x+leftBorder, y)Nextcanvas.DrawCircle( cursor+leftBorder, (-values[cursor] * scale) + h/2.0, 3 )End'************************************* Public static functions *************************************PublicFunction Add( name:String, value:Double, color:Color = Color.White )Local g := all[ name ]If Not gNew Graph( name, color )Elseg.AddValue( value )EndEndFunction DrawAll( canvas:Canvas, height:Double, range:Double )Local w := canvas.Viewport.Width - rightBorder - leftBorderLocal h := canvas.Viewport.HeightLocal mid := h/2Local top := mid-heightLocal bottom := mid+height-1Local scale := height/rangeLocal interval := 60canvas.Font = Nullcanvas.Alpha = bgAlphacanvas.Color = Color.Blackcanvas.DrawRect( leftBorder, top,w, height*2)canvas.Alpha = 1.0canvas.DrawLine( leftBorder, top, leftBorder+w, top )canvas.DrawLine( leftBorder, bottom, leftBorder+w, bottom )canvas.DrawRect( rightBorder, top, leftBorder-rightBorder, height*2 )canvas.Color = Color.LightGreycanvas.DrawLine( leftBorder, mid, leftBorder+w, mid )canvas.DrawLine( cursor+leftBorder, top, cursor+leftBorder, bottom )canvas.Color = Color.DarkGreyFor Local x := leftBorder To w Step intervalcanvas.DrawLine( x, top, x, bottom )Nextcanvas.Color = Color.Whitecanvas.DrawText( range, leftBorder, top, 1, 0 )canvas.DrawText( "0", leftBorder, mid, 1, 1 )canvas.DrawText( -range, leftBorder, bottom, 1, 1 )Local ty := 0For Local name := Eachin all.KeysLocal g := all[name]If gcanvas.Color = g.colorcanvas.DrawText( name, leftBorder, mid-ty, 1, 0 )g.Draw( canvas, scale )ty -= canvas.Font.HeightEndNextIf cursor = wFor Local g := Eachin all.Valuesg.values.PopFirst()NextElsecursor += 1EndEndEndHope that helps.
Cheers!March 4, 2018 at 11:44 am #13853Nice!
Thanks for the source code.
I did something like this on Bmax… but this is much ‘cleaner’ (and I like the Extension thing everytime I see it in use!)March 4, 2018 at 1:02 pm #13855Woah. This is very good. Very helpful, indeed.
Thank you.
March 4, 2018 at 6:51 pm #13867and I like the Extension thing everytime I see it in use!
Yeah, it’s one of my favorite M2 features! I usually find it cleaner to add a method to an existing class instead of making up a new one or a new function that takes the object as an argument. Also makes it really easy to add recursion, as you can see in the Entity example.
March 5, 2018 at 7:57 am #13876Very nice Ethernaut.
I’d love for this to be a simple module you could just import and use everywhere, not just for Mojo3D.Something like this should definitely be part of Monkey2 by default.
Each module, like Mojo3D and Mojo, could then use Echo themselves to give the user some info about entities, images or whatever.Btw the graph crashes at canvas.DrawCircle( cursor+leftBorder, (-values[cursor] * scale) + h/2.0, 3 ) in debug mode.
“Deque index out of range”Also, if you let the graph work its way to the end, then make the window smaller, the graph continue outside of the screen.
March 5, 2018 at 8:46 am #13879not just for Mojo3D
If you comment out the Entity and Scene related lines it should work just fine in ‘vanilla’ Mojo.
Btw it seems to crash at canvas.DrawCircle( cursor+leftBorder, (-values[cursor] * scale) + h/2.0, 3 ) in debug mode.
Not crashing here! Are you in the latest dev branch?
March 5, 2018 at 8:47 am #13880Also, if you let the graph work its way to the end, then make the window smaller, the graph continue outside of the screen
Yeah, it needs more polish if it’s gonna be a module… I kept it basic for now just so I could keep moving.
Volunteers?March 6, 2018 at 8:10 am #13890These are tremendously important components, thanks for posting.
Perhaps we could drop various ideas in case there is interest into making a full featured profiling framework, if it’s really useful and needed by the people who use Monkey. One such idea would be to attach some sort of console input and logging mechanism which can be useful when debugging the application at runtime. http://monkeycoder.co.nz/forums/topic/mojox-gui-components/ .
March 7, 2018 at 12:06 am #13898If you comment out the Entity and Scene related lines it should work just fine in ‘vanilla’ Mojo.
Yeah, but I mean if it were a module it should probably stay clear of using other module specific things, like Mojo3D.
Not crashing here! Are you in the latest dev branch?
I sure am!
You can fix it by doing something like:
canvas.DrawCircle( cursor+leftBorder, (-values.Last() * scale) + h/2.0, 3 )
So basically, use values.Last() instead of values[cursor]
And I’d suggest setting radius to something like 2.25 instead of 3.
3 very often seems to look like a square, where as 2.25 more consistently looks like a circle.Yeah, it needs more polish if it’s gonna be a module… I kept it basic for now just so I could keep moving.
Volunteers?I won’t be fixing it up, I sadly have too much to do right now.
But I really think Mark should add something like this in the future.
Maybe even tools like “frame step” so you can jump one “frame” forward to inspect things more closely. - 
		AuthorPosts
 
You must be logged in to reply to this topic.