Corbin's Treehouse - Corbin Dunn, Santa Cruz, CA
Plug Bug
Treehouse
Photography
Videos
Projects
Unicycling
About

Kitty Cabinet


I’m building a little cabinet to hold the kitty litter box, and some accessories. On top I plan to put the cat’s food and water bowls, to keep them out of reach of bad doggies.

Screen Shot 2014-07-19 at 2.06.44 PM.png

Download the sketch up file – Cat Box and Food area.skp

I have a bunch of old redwood from my water tank. I have been drying it out for years, and I just haven’t had the right project for it. Redwood is really soft, and this stuff is partially rotten, so I’m not sure how well it will work out. Here’s how it started out:

IMG_1496.jpg

Jointing, and then planing to 3/4″ gives me:

IMG_1499.JPG

I call it “spalted redwood”, and it is quite beautiful, but really fragile.

Several pieces biscuit jointed together:

IMG_1508.jpg

Face frame setup sample fitting:

IMG_1523.JPG

I need to buy smaller biscuits and a smaller blade for my Porter Cable plate joiner. The 0 size are too big; I need FF, which requires a special blade:

IMG_1525.jpg

Clamping:

IMG_1527.jpg


New Corbin UniBar (unicycle handlebar)


I made a new handlebar for my geared KH26. I was using the KH T-bar, but it feels really flexy and it feels like the seat will eventually break from the pressure I put on it. I had already made a slightly longer and slightly wider T for it out of CroMo steel, but it was just as light as the aluminum original.

Here’s the new bar, completed and installed:

IMG_1456.JPG

New bar, 352 grams:

IMG_1452.JPG

Old bar plus the seat strengthen plate (not needed with my design):

IMG_1453.jpg

So, 150 gram savings in weight by using CroMoly steel. And it is a lot less flexy!

Building process:

1. Fabricated a lug:

IMG_1430.JPG

2. Brazed it onto the seat tube attachment:

IMG_1433.jpg

3. Not shown, brazed on the back tab for the bolt/cincher

4. Cut a T, made a fish mouth in the end, and brazed on the top:

IMG_1448.JPG

5. Inserted the 22.2 tubing into the “lug” and brazed it together. The lug adds strength; I’ve broken handlebars at that location.


Fire LED Cyr Wheel Pattern


I took some demo code and modified it to do a fire pattern in the cyr wheel:


Angular Velocity in cyr wheel


This is a plot of the angular velocity vector when spinning in my cyr wheel. I took the gyro x/y/z value and created a vector (squared each value, added them, and took the square root).

Screen Shot 2014-05-10 at 11.29.03 PM.png

The downward start was from back spin “wind up” hat I did. I then increased velocity to a high speed, and you can clearly see when I give it an extra “umph” each revolution around. It was probably about 7 revs I did in the wheel. Values are (apparently) degrees per second. So, I spin about 1.5 times every second.


Accelerometer woes


I have a accelerometer/gyro/compass combo in my wheel LSM303DHLHC, data sheet: http://www.pololu.com/file/download/LSM303DLHC.pdf?file_id=0J564 found via Pololu’s link: http://www.pololu.com/product/1268

It has been tough to figure out consistent data. First of all, I realized the compass has to be calibrated. There is a sample app included with the LSM303 Adruino code called “Calibrate”. It just reads the compass/magnet data a keeps a running max and min value of what it sees. First off, I kept getting strange values out of it; specifically -4096. I later learned from the Polo website that this is an overflow condition in the chip and it is documented on the data sheet. First of all, it is documented on a data sheet, but not the one for the LSM303DHLHC. I’m sure they use the same accelerometer chip though. The fix according to Pololu is to adjust the gain (and that works). Here are possible values:

Screen Shot 2014-05-02 at 7.43.06 PM.png

The default is 0,0,0 which means a “Sensor input field range” of +/- 1.3 Gauss. I don’t really know what a gauss is, and I’m not sure why a higher or lower gain is important. But with the default value it was overflowing the buffers in the accelerometer chip, so I changed it to: 1, 0, 0 with this:

  _compass.writeMagReg(LSM303::CRB_REG_M, 0b10000000); // gain: ±4.0 Gauss (see datasheet)

Which is +/- 4.0 gauss. The gain reading values are lower, and don’t overflow anymore. That’s a plus. I’m using the Pololu modified sample “Pololu MinIMU-9 + Arduino AHRS (Attitude and Heading Reference System)” to get cool graphs/pictures:

.Screen Shot 2014-05-02 at 7.50.08 PM.png
It is a python script, and has a bunch of dependancies, but they are easy to find and download for the Mac. I had to tweak the script to get it to run:

1. Fix the serial port:

ser = serial.Serial(port=‘/dev/tty.usbmodem23981′,baudrate=115200, timeout=1)

2. Toss in a “rate(100)” in the while loop:

while True:

rate(100)

It took me a while to discover that last bit, and I only figured it out after reading a demo script.

I still was getting strange results; not quite an accurate position. So, I outputted the raw x/y/z magnet data to a comma delimitated file and opened it in Grapher:


Screen Shot 2014-05-02 at 7.52.16 PM.png

You can create a Grapher.app doc on the Mac (the app comes with OSX) and do a 3d graph. Add a Point Set, and at the top of the window click Edit Points…and import a file. I imported two; one is red, one is blue. Notice I have some rather odd stray points; I think these are causing part of my problem.

I might try to do some type of better averaging of the min/max values and through out the highest and lowest absolute values. More fun math to figure out….but that might correct my problems.


NSTableView Tips: Doing Animations with Core Data


For my “Cyr Wheel Pattern Editor” app I am using CoreData and an NSTableView. However, I’m sort of “manually” doing bindings for the array content itself to get animations in the View Based NSTableView.

Here’s what my model looks like:

Screen Shot 2014-04-29 at 9.09.53 PM.png

The CDPatternSequence has a children array of CDPatternItems:

@interface CDPatternSequence : NSManagedObject

@property (nonatomic, retain) NSOrderedSet *children;

@end

Now, if you have Xcode generate the standard code you might have noticed the standard NSOrderedSet accessors aren’t implemented for you; this is a bug in CoreData, but here is a good post on stack overflow about it.

The minimal implementation I needed was this (note: CDPatternChildrenKey == @”children”):

- (void)removeChildrenAtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
    NSMutableOrderedSet *tmpOrderedSet =
       [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:CDPatternChildrenKey]];
    [tmpOrderedSet removeObjectsAtIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:CDPatternChildrenKey];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
}

- (void)insertObject:(CDPatternItem *)value inChildrenAtIndex:(NSUInteger)idx {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
    NSMutableOrderedSet *tmpOrderedSet =
      [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:CDPatternChildrenKey]];
    [tmpOrderedSet insertObject:value atIndex:idx];
    [self setPrimitiveValue:tmpOrderedSet forKey:CDPatternChildrenKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
}

- (void)insertChildren:(NSArray *)values atIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
    NSMutableOrderedSet *tmpOrderedSet =
       [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:CDPatternChildrenKey]];
    [tmpOrderedSet insertObjects:values atIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:CDPatternChildrenKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
}

The main reason to do this is to get the proper KVO notifications to know what is happening. My NSWindowController subclass is the delegate and datasource for my tableview. Each row is managed by an NSViewController, and as I previously I simply have an array of NSViewControllers that starts out with NSNull placeholders and lazily load them when the table requests them:

- (void)_resetPatternViewControllers {
    _patternViewControllers = NSMutableArray.new;
    for (NSInteger i = 0; i < self.document.patternSequence.children.count; i++) {
        [_patternViewControllers addObject:[NSNull null]]; // placeholder
    }
    [_tableView reloadData];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return _patternViewControllers.count;
}

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn
                                                              row:(NSInteger)row {
    CDPatternItemViewController *vc = [self _patternViewControllerAtIndex:row];
    // don't set the identifier so it isn't reused...
    return vc.view;
}

- (CDPatternItemViewController *)_patternViewControllerAtIndex:(NSInteger)index {
    id currentObject = [_patternViewControllers objectAtIndex:index];
    CDPatternItemViewController *result;
    if (currentObject == [NSNull null]) {
        CDPatternItem *item = [self.document.patternSequence.children objectAtIndex:index];
        result = [CDPatternItemViewController new];
        result.patternItem = item;
        [_patternViewControllers replaceObjectAtIndex:index withObject:result];
    } else {
        result = (CDPatternItemViewController *)currentObject;
    }
    return result;
}

In my CDPatternItemViewController XIB, I bind stuff to the “patternItem”, which is my CoreData model object.

So, here’s the fun part. I simply observe the children of my container model object and do updates to the table based on that. It makes it *really easy* to simply modify your model and have the table automatically reflect the changes you made. Again, I have a window controller subclass, so I do it here:

- (void)windowDidLoad {
    [super windowDidLoad];

    [self _resetPatternViewControllers]; // This is shown above

    // Watch for changes
    [self.document.patternSequence.children addObserver:self
              forKeyPath:CDPatternChildrenKey options:0 context:nil];

    // Drag and drop registration below… (snipped out)
}

Then, all the real work is done in the KVO callback.

- (void)_observeValueForChildrenChangeOfObject:(id)object change:(NSDictionary *)change
                                       context:(void *)context {
    NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
    switch (changeKind) {
        case NSKeyValueChangeSetting: {
            [self _resetPatternViewControllers];
            break;
        }
        case NSKeyValueChangeInsertion: {
//            if (_draggedRowIndexes != nil) break; // Done manually in the drag operation
            NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
            if (indexes) {
                NSMutableArray *emptyObjects = NSMutableArray.new;
                for (NSInteger i = 0; i < indexes.count; i++) {
                    [emptyObjects addObject:[NSNull null]];
                }
                [_patternViewControllers insertObjects:emptyObjects atIndexes:indexes];
//                [NSAnimationContext beginGrouping];
//                [NSAnimationContext.currentContext setDuration:.3];
                [_tableView insertRowsAtIndexes:indexes withAnimation:NSTableViewAnimationEffectFade];
//                [NSAnimationContext endGrouping];
            } else {
                [self _resetPatternViewControllers];
            }
            break;
        }
        case NSKeyValueChangeRemoval: {
//            if (_draggedRowIndexes != nil) break; // Done manually in the drag operation
            NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
            if (indexes) {
                [_patternViewControllers removeObjectsAtIndexes:indexes];

                [_tableView removeRowsAtIndexes:indexes withAnimation:NSTableViewAnimationEffectFade];
            } else {
                [self _resetPatternViewControllers];
            }
            break;
        }
        case NSKeyValueChangeReplacement: {
            NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
            if (indexes) {
                [_patternViewControllers removeObjectsAtIndexes:indexes];
                // replace w/null
                NSMutableArray *emptyObjects = NSMutableArray.new;
                for (NSInteger i = 0; i < indexes.count; i++) {
                    [emptyObjects addObject:[NSNull null]];
                }
                [_patternViewControllers insertObjects:emptyObjects atIndexes:indexes];
                [_tableView reloadDataForRowIndexes:indexes columnIndexes:[NSIndexSet indexSetWithIndex:0]];
            } else {
                [self _resetPatternViewControllers];
            }
            break;
        }
        default: {
            NSAssert(NO, @"internal error: change not known");
            break;
        }
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:CDPatternChildrenKey]) {
        [self _observeValueForChildrenChangeOfObject:object change:change context:context];
    } else {
        NSAssert(NO, @"bad observation");
    }

}

The code above keeps the view controller array in sync with the model, and also keeps the table in sync with the model.

You can see I have some comments about drag and drop. I can write a post on how to do that, if people are interested.


NSTableView Tips: View Controller per row


Sometimes you may find your application has rather complex rows, and you want an NSViewController to manage each row in a “View Based” NSTableView. This is fairly easy to do, and I did it in my Cyr Wheel pattern editing application. My “Cell View” that I want for each row was designed in Xcode like:

Screen Shot 2014-04-26 at 8.52.47 PM.png

A simple NSViewController subclass:

@interface CDPatternItemViewController : NSViewController
   // the thing we are editing
   @property(weak) CDPatternItem *patternItem;
@end

The controller code is what is interesting. I use an NSWindowController subclass, and keep an array of the view controllers:

@interface CDPatternEditorWindowController () {
@private
  NSMutableArray *_patternViewControllers;


I decided to lazily load the view controllers. On initialization/load of my window I fill the array with NULL:

- (void)_resetPatternViewControllers {
  _patternViewControllers = NSMutableArray.new;
  for (NSInteger i = 0; i < self._patternSequence.children.count; i++) {
    [_patternViewControllers addObject:[NSNull null]]; // placeholder
  }
  [_tableView reloadData];
}



_patternSequence is simply a wrapper around my model object, stored in my Core Data backed NSDocument; it has an array of things I want to show in the table

- (CDPatternSequence *)_patternSequence {
   return self.document.patternSequence;
}


Then, the view based tableview method that requests the view simply creates and caches it:


- (CDPatternItemViewController *)_patternViewControllerAtIndex:(NSInteger)index {
    id currentObject = [_patternViewControllers objectAtIndex:index];
    CDPatternItemViewController *result;
    if (currentObject == [NSNull null]) {
        CDPatternItem *item = [self._patternSequence.children objectAtIndex:index];
        result = [CDPatternItemViewController new];
        result.patternItem = item;
        [_patternViewControllers replaceObjectAtIndex:index withObject:result];
    } else {
        result = (CDPatternItemViewController *)currentObject;
    }
    return result;
}

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    CDPatternItemViewController *vc = [self _patternViewControllerAtIndex:row];
    // don't set the identifier so it isn't reused...
    return vc.view;
}



This should be fairly straightforward, and easy to read. But if you have questions let me know and I will clarify.

I don't cleanup the view controllers when the items are removed from the table. I could use the NSTableView delegate method tableView:didRemoveRowView:forRow: to drop my NSViewController cache at this time if I so wanted. However, my table is small..and I don't bother with this.



NSTableView Tips: Not delaying the first responder


I have a little home-brew Cocoa app for making Cyr Wheel patterns. The UI is built with an NSTableView and looks like this:

Screen Shot 2014-04-26 at 7.31.00 PM.png

Now normally when you try to directly click on one of the NSTextFields the table will first select the row. Then a second click will allow you to start editing. For most tables, this is the behavior you want. For something like what I have, I want to avoid this, and allow the first responder to go through. This can easily be done by subclassing NSTableView and overriding:

- (BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event {
   return YES; // no delay
}

NSTableView’s default logic is a bit complex that allows things to go through or not based on several properties. This method is declared in NSResponder.h, and it is sent up the responder chain when the text field itself attempts to become first responder. When NSTableView gets it, it does some determination to determine if it should go through or not. If it wasn’t an already selected row, it returns NO; this causes the table to get the mouseDown: and do the selection logic. br/>I don’t care what control was clicked; I want it to go through without selecting, and always return YES.


How to make a cyr wheel: Insert detail


Here’s some details on my cyr wheel inserts that I make (aka: roue cyr):


Implementing delete in an NSTableView


I’ve seen many ways to implement delete in an NSTableView. Many are good. Many hardcode references to something they shouldn’t, and those are bad.

Here’s an easy way:

Subclass NSTableView and override keyDown:


- (void)keyDown:(NSEvent *)theEvent {
    // handle delete
    unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
    if (key == NSDeleteCharacter && self.selectedRow != -1) {
        [NSApp sendAction:@selector(delete:) to:nil from:self];
    } else {
      [super keyDown:theEvent];
  }
}

This just lets the responder chain take care of it. Your window controller can easily handle it by just implementing delete:

- (IBAction)delete:(id)sender {
    if (self.tableView.selectedRowIndexes.count > 0) {
        [_tableView beginUpdates];
        [[self _patternSequence] removeChildrenAtIndexes:self.tableView.selectedRowIndexes];
        [_tableView endUpdates];
    }
}

EDIT: I probably should only call super keyDown if it wasn’t the delete key…otherwise we beep.



(c) 2008-2012 Corbin Dunn

Corbin's Treehouse is powered by WordPress. Made on a Mac.

Subscribe to RSS feeds for entries and comments.

14 queries. 0.734 seconds.