Monday, January 21, 2013

More Objective-C Drawing Context Pleasantries

It's been a little over half a year since I first made my pleasant Objective-C drawing contextpublic, and I haven't been idle. In the process of retrofitting my own code to use MPWDrawingContext and adding more and more graphics (for example, I now do my icons in code), I've discovered a lot about making drawing with code a more pleasant experience.

Blocks

Blocks seem to be a really wonderful match for a graphics context, and most of the changes involve blocks in some way.

Bracketing operations such as gsave/grestore now have block versions, so the Objective-C block structure reflects the nesting:

    [context ingsave:^(Drawable c ){
        [c translate:@[ @130 ,@140]];
        [c setFont:[context fontWithName:@"ArialMT" size:345]];
        [c setTextPosition:NSMakePoint(0, 0)];
        [c show:@"\u2766"];
    }];
This is somewhat more compact than the plain code, which for correctness should also have a @try/@finally block wrapped around the basic drawing so exceptions don't mess up the graphics state stack.
    [context gsave];
    [context translate:@[ @130 ,@140]];
    [context setFont:[context fontWithName:@"ArialMT" size:345]];
    [context setTextPosition:NSMakePoint(0, 0)];
    [context show:@"\u2766"];
    [context grestore];
Similar for drawing shadows:
    [context withShadowOffset:NSMakeSize(0, -8 * scale) blur:12 * scale  color:[context  colorGray:0 alpha: 0.75] draw:^(Drawable c ){
        [[[c setFillColorGray:0.9 alpha:1.0] ellipseInRect:ellipseRect] fill];
    }];
Again, this seems a little clearer than having to explicitly set and unset, makes it harder to miss the end of the bracket when moving code around and remains exception-safe.
    [context sethadowOffset:NSMakeSize(0, -8 * scale) blur:12 * scale  color:[context  colorGray:0 alpha: 0.75]];
    [[[context setFillColorGray:0.9 alpha:1.0] ellipseInRect:ellipseRect] fill];
    [context clearShadow];

Stored, delayed and repeated drawing

You can create an object for later drawing by sending the -laterWithSize:(NSSize)size content:(DrawingBlock)commands message. For example, here is a simple diamond shape:
    NSSize diamondSize=NSMakeSize(16,16);
        id diamond = [context laterWithSize:diamondSize
                              content:^(id  context){
            id red = [context colorRed:1.0 green:0.0 blue:0.0 alpha:1.0];
            [context setFillColor:red];
            [[context moveto:diamondSize.width/2 :2] 
				lineto:diamondSize.width-2 :diamondSize.height/2];
            [[context lineto:diamondSize.width/2 :diamondSize.height-2]
				lineto:2 :diamondSize.height/2];
            [[context closepath] fill];
        }];
We can now draw this anywhere we want, and at any scale or orientation, using the -drawImage: message.
    [context drawImage:diamond];
You also have layerWitSize:content: and bitmapWithSize:content: messages if you want to specifically use CGLayer or CGImage instead, but using laterWithSize:content: preserves maximum quality, and it will automatically switch to a CGLayer when rendering to a PDF context in order to minimize PDF file size.

Patterns

I talked about patterns earlier. What I didn't mention then was that this is just the ability to use a stored set of drawing commands (see previous section) as a color:
    [context setColor:diamond];
I am not going to post the comparison to plain CG here, you can read it in the original Apple documentation.

I should note that this currently works for colored patterns, not for uncolored patterns, due to the fact that I haven't yet exposed color spaces. The basic process will be very similar.

Polymorphic object arguments

Path construction and graphics state messages with point arguments are now available in a version that takes a single object argument, in addition to the format with anonymous float arguments (moveto:(float)x :(float)y).
  • moveto:
  • lineto:
  • translate:
  • scale:
The single argument can be an Objective-C array:
    [context moveto:@[ @10, @20]];
Alternatively, any custom object that responds to count and either objectAtIndex:, (float)realAtIndex: or getReals:(float*)buffer length:(int)maxLen can be used. The scale: message can also take a single NSNumber and will treat that as uniform x and y scale.

Linecap parameters

Linecap parameters can now be set using distinct message:
  • setlinecapRound
  • setlinecapButt
  • setlinecapSquare
Having multiple messages rather than a single message with a parameter probably seems odd, but it actually reduces the number of names involved, and these names are nicely scoped to this context. The constant strings or enums that are typically used have global scope and therefore tend to need ugly and hard-to-remember prefixes:
    [context setlinecapRound];
vs.
    [context setLinecap: kCGContextLinecapRound];

Future

Another reason to be as purely message-based as possible is that it makes bridging to other languages easier, for example for interactive drawing environments: Creating a badge (youtube).

I've also started experimenting with other outputs, for example creating a version of the same badge composed of CALayer objects using the same drawing commands. Other output should follow, for example web with SVG or HTML 5 Canvas or direct OpenGL textures.

I also want to finally add image processing operations both stand-alone and as chained drawing contexts, as well as getting more complex text layout options in there.

p.s.: now on hacker news

(The People) 45874 : 1 (Carmen Ortiz)

As of right now, the White House Petition to remove Carmen Ortiz has garnered 45874 signature in the 9 days since January 12th. The one asking to recognize her 'outstanding' contributions, active for 5 days now received a slightly smaller number: 1. Maybe I should graph this?

Monday, January 7, 2013

Dependency Injection is a Virtue

DHH recently made a claim of Dependency Injection not being entirely virtuous, and got support from Tim Bray. While both make good points, they are both wrong.

Having hard-coded class-names like in the example Time.now is effectively the same as communicating via global variables. DHH's suggestion of stubbing out the Time class's now is selling us mutable global variables as the solution to global variables. Or more precisely: passing an argument to a method by modifying a global variable that the method reads out and restoring the state of the global variable afterward.

If that's "better", I don't really want to see "worse", and not wanting that sort of thing has nothing to do with being a Java drone limited by the language. And my experience with time-dependent systems tells me that you really want to pass the time into such a system generally, not just for unit testing.

Of course, having n-levels of AbstractFactoryFactory indirection could actually be argued as being worse, as Tim convincingly does, but that's one implementation of DI that's hobbled by Java's limitations. For a DI solution that's actually simple and elegant, check out Newspeak's  module system (PDF): there is no global namespace, modules are parametrized and all names dynamically resolved.

If you want synthetic time for a module, just instantiate that module with your own Time class.

Sunday, January 6, 2013

Objective-C is TIOBE Language of the Year Second Time in a Row

The TIOBE Index shows Objective-C as "Language of the year 2012". As it also won the honor in 2011, this now makes it only the 2nd language to win the award twice (Python won 2007 and barely edged out Objective-C in 2010), and the only one to win it twice in a row.

Although this surge in popularity is certainly largely due to the popularity of the iOS ecosystem (iPhone, iPad, AppStore), the fact that it continues despite Apple loosening its policies and alternatives popping up suggests that there may be more going on: maybe developers are discovering that Objective-C is a pretty fun and productive language, warts and all?

It'll be interesting to see wether Objective-C has now peaked, as Tiobe predicts, or wether it manages to expand back into other areas that could benefit from its qualities.

As last year, table and graphic reproduced here because Tiobe doesn't appear to keep an archive:

Position

Jan 2013

Position

Jan 2012

Delta in Position

Programming Language

Ratings

Jan 2013

Delta 

Jan 2012

Status

1

2

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

C

17.855%

+0.89%

  A

2

1

http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif

Java

17.417%

-0.05%

  A

3

5

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

Objective-C

10.283%

+3.37%

  A

4

4

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

C++

9.140%

+1.09%

  A

5

3

http://www.tiobe.com/content/paperinfo/tpci/images/Down.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Down.gif

C#

6.196%

-2.57%

  A

6

6

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

PHP

5.546%

-0.16%

  A

7

7

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

(Visual) Basic

4.749%

+0.23%

  A

8

8

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

Python

4.173%

+0.96%

  A

9

9

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

Perl

2.264%

-0.50%

  A

10

10

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

JavaScript

1.976%

-0.34%

  A

11

12

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

Ruby

1.775%

+0.34%

  A

12

24

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

Visual Basic .NET

1.043%

+0.56%

  A

13

13

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

Lisp

0.953%

-0.16%

  A

14

14

http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif

Pascal

0.932%

+0.14%

  A

15

11

http://www.tiobe.com/content/paperinfo/tpci/images/Down.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Down.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Down.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Down.gif

Delphi/Object Pascal

0.919%

-0.65%

  A

16

17

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

Ada

0.651%

+0.02%

  B

17

23

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

MATLAB

0.641%

+0.13%

  B

18

20

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

Lua

0.633%

+0.07%

  B

19

21

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

Assembly

0.629%

+0.08%

  B

20

72

http://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gifhttp://www.tiobe.com/content/paperinfo/tpci/images/Up.gif

Bash

0.613%

+0.49%

  B

Tpci trends 2013