Skip to main content

Posts

Connecting fields

Quick Recipe To connect field a in obj1 to field b in obj2 , use this: obj2 startScript: #b: when: {obj1. #aChanged} Now for the whole story ... Problem A colleague of mine wanted to make a drop-down list, where the options are not just set once, but provided and updated by the application. So, of course, when the options in the application changes, the items of the list widget have to be set to this new value. Nothing easier than that, just write a handler: onOptionsChanged     <on: optionsChanged in: app>     listWidget items: app options HOWEVER, he wanted to build this programmatically, not using a separate method. So, he easily came up with the following: listWidget startScript : #items: withArguments: {app options} when: {app. #optionsChanged} HOWEVER, this does not work as intended because the arguments to the script are evaluated only once, rather than every time the script is triggered. Well, this is what blocks are for, right? ...

Scripts in Croquet

Tao asked for a "Tweak & Croquet" tutorial. I don't have time right now to actually write one, but here's some sample code anyway. Using scripts is easy and useful, even without the Tweak GUI. Just use #startScript: to run some method as a script. Inside a script, you can use loops and anything you like, just throw in a wait to account for time. Like, to animate the color of a frame, you could use this method (just add it to your TeapotMorph ): animateColorFor: aFrame [ 0 to: 360 do: [:hue | aFrame material color: (Color h: hue s: 1.0 v: 1.0). self wait: 0.01] ] repeat This changes the color every 10 ms, and you can start it from the initializeDefaultSpace method: self startScript: #animateColorFor: withArguments: {someFrame}. Here is something that does not loop forever, but finishes after one cycle: jump: aFrame | v g | v := 0@1@0. g := 0@-0.05@0. [aFrame translation y >= 0] whileTrue: [ a...

Tweak Tutorial

Andreas posted a BankAccount and ATM tutorial. This nicely demonstrates some of the basic Tweak concepts such as fields, events, triggers, and handlers, as well as introducing UI aspects like players, costumes, updating etc.

Print-Quality Screenshots

For high-quality prints you need high-quality screenshots. This means very high resolution, and nice anti-aliasing. Just grabbing the screen produces rather unpleasant results ( screenshot , 80 KB, 800x600 pixels). With normal OpenGL rendering you get rarely more than screen resolution, and anti-aliasing quality very much depends on your graphics board. So what to do? Tiled Rendering comes to the rescue. Instead of rendering the whole image at once, we render smaller portions of the scene, and then arrange the tiles into a large picture. However, just pointing the camera at each tile will not work as intended, the perspective would change from tile to tile. What is needed instead is to construct partial viewing frustums that together exactly recreate the whole frustum. This sounds like a lot of math, but actually it is quite simple: gluPerspective: fov aspect: aspect zNear: near zFar: far tile: rect | cotangent radians w h | radians := (fov/2.0) degreesToRadians. cotan...

Change events are special

I just spent a few hours chasing a very mysterious bug, where an event handler was properly called the first time, but never again. This was a rather unspecial on:in: handler: onFooInBar <on: foo in: bar> ... After initialization it was properly registered in the event map, but at some point the event map entry just vanished. There also was another on: handler which did some stuff whenever bar changed. To "call" that stuff, we just signaled a barChanged event somewhere else: self signal: #barChanged. It turned out this innocuously looking line was responsible for the trashed on:in: handler! What happened? Well, an on:in: handler must always be registered to the object that currently is occupying the bar field. But what if bar changes? Then we must unregister the foo handler in the old bar , and re-register it with the new bar . That's why the system installs a barChanged handler behind the scenes, which normally receives the new and old values of bar ...

About Scripts

We were looking into another Croquet performance problem the other day so we fired up a message tally (world menu - debug - start MessageTally). Curiously enough, 70 percent was taken by ScriptScheduler>>runActiveScripts ! Unfortunately, the tally did not further differentiate this item. But what are scripts, anyway? Everyone knows that Smalltalk is all about objects and messages, so what the heck are scripts? Well, Croquet and Tweak are not just using Smalltalk as you know it (and the underlying Squeak still is pretty much vanilla Smalltalk-80), but instead improve on it by implementing a new enriched object system. There still are objects (the entities of the system) and messages (their means of communication). But where in Smalltalk methods are invoked synchronously by a message send, we now have asynchronous method invocations as well, which are called "scripts". Synchronous in this context means that the sender sends a message, which invokes a method in the receiv...