Another Programming Blog

(mostly iOS and Ruby on Rails)

WebGL: Creating a Landscape-mesh With Three.js Using a PNG-heightmap

Ok, this isn’t very complicated, even though it looks kind of cool.

The aim is to go from this:

Flat heightmap example png

to this:

Three.js landscape

The idea is that the brighter a pixel on the heightmap is, the higher the elevation of the resulting mesh is. So the first step is to get the values for all the pixels in the PNG. This is accomplished by drawing the image onto a canvas, and then getting the pixel data from it. It looks something like this:

canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var data = canvas.getContext('2d').getImageData(0,0, img.height, img.width).data;

The next step is to create a planar mesh with ThreeJS and go through all the pixel and set their z-value, their elevation, according to the color of the pixel.

for (var i = 0, l = geometry.vertices.length; i < l; i++)
{
  var terrainValue = terrain[i] / 255;
  geometry.vertices[i].z = geometry.vertices[i].z + terrainValue * 200 ;
}

Please keep in mind that in this case the width in pixels is 1 more than the number of segments in the plane. (If there’s one segment, theres 2 vertices, two segments, 3 vertices, and so forth..) If that’s not the case more work is required, because the landscape will be “skewed” if height and width dont correspond correctly.

And that’s it.

I also afterwards call

geometry.computeFaceNormals();
geometry.computeVertexNormals();

so the shading according to the light-source is correct.

You can get the code from Github.

On Programmatically Checking Linux-passwords

Klaus Trainer has just posted a short article on how to write a short program, that checks usernames and passwords on a Linux machine.

I was at first confused why it works, since there was no salt. But in fact it’s there, inside the entry in /etc/shadow.

Here’s the format:

$<HASHING-METHOD>$<SALT>$<HASHED-PASSWORD (base64)>

So for example if we have a row like this:

$6$qUgyc2fC$hljmJlJU7TV4gm8GDZd51eZnxNgWp3rUJ49kSblRLssLqxux5K.xSIcIn2QjL27jXOREBfXiB1WES3SEIPHk10

Then the 6 means that SHA512 was used, and qUgyc2fC is the salt.

How to Keep Up With the Pace of Change

Seth Godin recently published a short post called “The full stack keeps getting taller”, in which he basically advices us to leave a particular field of interest as soon as it’s taken for granted - when it becomes part of the lower stack. A commodity, perhaps.

However, while top of the stack changes quickly, the bottom of the stack changes slowly.

It’s the principle of inertia. If you have two soccerballs, and one is filled with lead instead of air, then you’d need to apply more work to accelerate it to the same speed as the other.

What I mean is this: There’s much more built on top of TCP/IP than there is, for instance, on top of Gulp.js.

And if it’s easier to go from the basics to a specialization than vice versa [1], it pays to have a solid foundation, and a good grasp of core technologies.

This way, jumping from technology to technology should require much less effort.

Footnotes

[1] I think this is the case, especially if we have not neglected to learn new things in the past. If we ever only cared about Java programming, and never bothered to look at Assembler or Clojure, we might end up a one trick pony. But if there’s a little experience in different fields under our belt, we get very good at adapting.

macThesaurus: A Mac-app Written With Node-webkit

I decided to play around with node.js, specifically with node-webkit.

Node-webkit allows programmers to write desktop-apps with Javascript, and even package them for distribution, for example via the Mac App Store. (Check out this list of apps written with it.)

To play with it, I’ve decided to write a tiny thesaurus-app.

  • It should lookup and display synonyms and related words using a (German) thesaurus via the JSON-API at openthesaurus.de.
  • To be really useful, it should be possible to bring the app to the foreground using a keyboard shortcut. In my case it’s Shift+Alt+T.
  • It should be possible to dismiss the app by tapping ESC

It worked quite nicely. The app doesn’t really feel like a web-app, even though it utilizes web-app technologies. And I could even create a binary for the App Store.

Here’s a short video of the app in action:

And here’s the source on Github.

Monty Hall Simulation

Here’s a simulation of the Monty Hall Problem.

# this is about http://en.wikipedia.org/wiki/Monty_Hall_problem

num_rounds = 5000
num_correct_guesses_keep = 0
num_correct_guesses_switch = 0

num_rounds.times do

        doors = [false, false, false]
        doors[rand(3)] = true

        # first pick = random

        player_pick = rand(3)
        show_bad_door = doors.index(true)

        # show a bad door that is not the price_door and not player's pick

        while (doors[show_bad_door] || show_bad_door == player_pick)
                show_bad_door = rand(3)
        end

        # second pick, player choses to switch to the other door
        
        remaining_doors = [0, 1, 2]
        remaining_doors.delete(show_bad_door)
        remaining_doors.delete(player_pick)

        player_switch_pick = remaining_doors.first        

        win = if doors[player_pick]
                num_correct_guesses_keep += 1
                "yes"
        else 
                "no"
        end

        win_switch = if doors[player_switch_pick]
                num_correct_guesses_switch += 1
                "yes"
        else 
                "no"
        end

        puts "doors: #{doors}, show_bad_door: #{show_bad_door}, "\
        "player keeps: #{player_pick}, player wins: #{win}, "\
        "player wins if he switches: #{win_switch}"

end

win_ratio = ( num_correct_guesses_keep.to_f / num_rounds.to_f * 100.to_f ).round(2)
win_ratio_pick = ( num_correct_guesses_switch.to_f / num_rounds.to_f * 100.to_f ).round(2)
puts "correct: #{num_correct_guesses_keep}, correct-switch: #{num_correct_guesses_switch}, "\
         "win-ratio: #{win_ratio}%, win-ratio switch: #{win_ratio_pick}%"

Switching really doubles the probability to win. Dang it. ;)

Reminder to Self: Code-Folding in XCode (With Keyboard Shortcuts)

Just a reminder for myself. How to fold and unfold code in XCode:

  • ⌘ + ⌥ + ⇧ + ← fold all methods in file.
  • ⌘ + ⌥ + ⇧ + → unfold all methods in file.
  • ⌘ + ⌥ + ← fold block underneath the cursor.
  • ⌘ + ⌥ + → unfold block underneath the cursor.

New App: Zones

I’ve just uploaded my new app. It is called “Zones” and it’s one of those desktop helper tools for Mac OS X. You can take a look at Zones here.

It was fun to do and I learned a bunch of stuff about Mac OS X. It was probably also the last side-project ever written in Objective-C for me, since the Apple world is switching to Swift now. (Which is good.)

Sadly, I cannot distribute the app through the AppStore, because it cannot be sandboxed using the APIs it does (Accessibility). Which means I will probably never earn enough money to buy a happy meal with it.

How to Revert iOS8 Beta 1 Back to iOS 7.1.1

I needed to go back to iOS 7 after a few apps, most notably Whatsapp, no longer worked properly. I also had no restore points for some reason.

Here’s a way to turn your iOS 8 iPhone back into an iOS 7 iPhone:

  1. Attach phone to Mac
  2. Start iTunes
  3. Hold Power and Home Button on the phone. When it restarts let go of the power button and only keep the Home button pressed.
  4. iTunes will say it detected a phone in recovery-mode
  5. Proceed with the recovery instructions.

You don’t need to donwload any images before hand, iTunes will do it for you.

In my case I ended up with a completely blank iPhone. But I could restore Contacts, some settings etc. from iCloud.

iOS: Restrict Panning of UIPageViewController to a Certain Area

Another iOS code-snippet:

I had a UIPageViewController in scrolling-mode that would allow to pan between view-controllers using a finger gesture. However, I wanted to restrict the panning area to a certain area of the screen, for example as shown in this screenshot:

Restricted Panning Area UIPageViewController

I do it by subclassing the UIPageViewController, finding its UIScrollView, and adding a new UIPanGestureRecognizer to that scrollView.

I set my subclassed UIPageViewController to be the delegate of that new UIPanGestureRegognizer. I then implement two delegate methods:

#import "LZPageViewController.h"

@interface LZPageViewController ()

@end

@implementation LZPageViewController
{
  UIPanGestureRecognizer *_scrollViewPanGestureRecognzier;
}

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view.
  
  for (UIView *view in self.view.subviews) {
    if ([view isKindOfClass:[UIScrollView class]])
    {
      UIScrollView *scrollView = (UIScrollView *)view;
      _scrollViewPanGestureRecognzier = [[UIPanGestureRecognizer alloc] init];
      _scrollViewPanGestureRecognzier.delegate = self;
      [scrollView addGestureRecognizer:_scrollViewPanGestureRecognzier];
    }
  }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
  return NO;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
  if (gestureRecognizer == _scrollViewPanGestureRecognzier)
  {
    CGPoint locationInView = [gestureRecognizer locationInView:self.view];
    if (locationInView.y > SOME_VALUE)
    {
      return YES;
    }
    return NO;
  }
  return NO;
}

In the last override I decide if I want to “eat the event” (reply YES) or if I want the original UIPanGestureViewRecognizer of the UIScrollView to handle it (reply NO). So, the YES-reply means the UIPageViewController will not scroll to the next ViewController.

iOS: How to Resize and Rotate an UIImage - Thread Safe Version

Here are two code snippets for rotating and resizing UIImage’s without using UIGraphicsBeginImageContext(), because it is not thread-safe and can lead to weird behavior.

In my case the camera-preview when taking a picture in an app I was writing, turned black from time to time. The problem was that I was rotating and resizing the images using UIGraphicsBeginImageContext().

Here’s some code I’ve found and tweaked to resize images:

- (UIImage *)imageByScalingToSize:(CGSize)targetSize
{
  UIImage *newImage = nil;
  
  CGFloat targetWidth = targetSize.width;
  CGFloat targetHeight = targetSize.height;

  CGContextRef bitmap = CGBitmapContextCreate(NULL,
                                              targetWidth,
                                              targetHeight,
                                              CGImageGetBitsPerComponent(self.CGImage),
                                              4 * targetWidth, CGImageGetColorSpace(self.CGImage),
                                              (CGBitmapInfo)kCGImageAlphaNoneSkipLast);
  
  CGContextDrawImage(bitmap, CGRectMake(0, 0, targetWidth, targetHeight), self.CGImage);
  
  CGImageRef ref = CGBitmapContextCreateImage(bitmap);
  newImage = [UIImage imageWithCGImage:ref];
  
  if(newImage == nil) NSLog(@"could not scale image");
  CGContextRelease(bitmap);
  
  return newImage ;
}

And here’s how to rotate them:

- (UIImage*)rotateInRadians:(CGFloat)radians
{
        CGImageRef cgImage = self.CGImage;
        const CGFloat originalWidth = CGImageGetWidth(cgImage);
        const CGFloat originalHeight = CGImageGetHeight(cgImage);

        const CGRect imgRect = (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, 
                .size.width = originalWidth, .size.height = originalHeight};
        const CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, CGAffineTransformMakeRotation(radians));

        CGContextRef bmContext = NYXImageCreateARGBBitmapContext(rotatedRect.size.width, rotatedRect.size.height, 0);
        if (!bmContext)
                return nil;

        CGContextSetShouldAntialias(bmContext, true);
        CGContextSetAllowsAntialiasing(bmContext, true);
        CGContextSetInterpolationQuality(bmContext, kCGInterpolationHigh);

        CGContextTranslateCTM(bmContext, +(rotatedRect.size.width * 0.5f), +(rotatedRect.size.height * 0.5f));
        CGContextRotateCTM(bmContext, radians);

        CGContextDrawImage(bmContext, (CGRect){.origin.x = -originalWidth * 0.5f,  .origin.y = -originalHeight * 0.5f, 
                .size.width = originalWidth, .size.height = originalHeight}, cgImage);

        CGImageRef rotatedImageRef = CGBitmapContextCreateImage(bmContext);
        UIImage* rotated = [UIImage imageWithCGImage:rotatedImageRef];

        CGImageRelease(rotatedImageRef);
        CGContextRelease(bmContext);

        return rotated;
}

Because the above takes radians, convert using something like this:

CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};

Just put these methods into a category on UIImage.