Archive for the ‘Cocoa Coding’ Category

Potentials – Added Plot Switching

Tuesday, May 7th, 2019

Building Great Code

This afternoon I found an article about creating contours on data, and while it looks very reasonable, it was more than I could dig into and really get done in a few minutes. So, I looked to be able to flip between plots on the app - either V(x,y) or E(x,y), and to make it simple - I decided to just use the menu items and that would then make keyboard shortcuts easy, and the state of the plot would be reflected in the menu.

Potentials Plot Switching

With the menu items added, I simply had to make the IBOutlets for the the NSMenuItems, and then make IBActions for the menu item selections, and the code was all pretty simple:

- (IBAction) plotVxy:(id)sender
{
  BOOL                 error = NO;
 
  SimWorkspace*        ws = nil;
  // first, make sure we have a workspace to use
  if (!error) {
    ws = [self getWorkspace];
    if (ws == nil) {
      error = YES;
      NSLog(@"[MrBig -plotVxy:] - there is no defined workspace with which to run
              this simulation. Please make sure there is before calling this method.");
    }
  }
 
  // next, see if we need to switch views
  if (!error) {
    if ([[self getPlotExy] state] == 1) {
      [[self getPlotVxy] setState:1];
      [[self getPlotExy] setState:0];
      // plot the Voltage on the workspace
      if ([ws getResultantVoltage] != nil) {
        [[self getResultsView] plotVoltage:ws];
      }
    }
  }
}

And we automatically implement the mutual exclusive nature of the toggle, and if we have no data to plot, we don't waste time, but if we do, we switch the view. Simple.

These little updates are a lot of fun, and it's certainly an aspect of macOS coding that I haven't done a lot of - Graphics.

Potentials – Adding the Electric Field to the Plotting

Monday, May 6th, 2019

Building Great Code

In the original code, we did a point-by-point calculation of the magnitude and direction of the electric field based on the results of the solution of the potential on the workspace. This was fine when it was just writing it out to the file, but when we want to do the plotting of the data, it makes sense to compute the magnitude and direction of the field for all points, and save them. So that's what we've added in this most recent update.

Potentials adds Electric Field

We added the ivars, setters and getters to the SimWorkspace class, and then simplified the getters to use the ivars and now the computed values. Finally, we added in the calculation of the field to the end of the -runSim: method, and we got what we needed. At this point, we need to be able to switch between the voltage and electric field, and then add isobars, and finally, the directions on the electric field.

There is plenty yet to do, but it's really starting to take shape, and as we keep making progress, the app is really starting to come together. 🙂

Potentials – Adding Multiple Colors to Graphing

Sunday, May 5th, 2019

Building Great Code

The graphs in Potentials are OK... but they are only interpolating between two colors. It's OK... but it's not as nice as I'd like it. What I wanted was to be able to have several colors in the mix - so I could come up with colors like Black Body Radiation. Then it came to me, that I could bracket the values with an array of colors, like:

  NSColor*    spectrum[] = {[NSColor redColor],
                            [NSColor orangeColor],
                            [NSColor yellowColor],
                            [NSColor cyanColor]};
  int         stages = 4;
  double      dc = 1.0/(stages - 1);

And then, for each point to plot:

  double      x = 0.0;
  int         ilow = 0;
  for (int r = 0; r < _rowCnt; r++) {
    for (int c = 0; c < _colCnt; c++) {
      x = _values[r][c];
      ilow = MIN((int)(x/dc), (stages-2));
      x -= ilow * dc;
      gc = [ResultsView interpolate:(x/dc)
                        withColorsBetween:spectrum[ilow]
                        and:spectrum[ilow+1]];

Where we're picking the value up, then finding the two bordering colors on that value, and then moving the value to the [0..1] range and then interpolating between those two colors.

In the end, it looks great:

Potentials Multi Color Graphing

It's a really nice improvement, and next we have to work on the Electric field.

Potentials – Fixed up the Simulation Results

Saturday, May 4th, 2019

Building Great Code

This afternoon I was playing with the simulation data for Potentials and started to log out all the values going in, and coming out, of the dgbsv_ call, and I started to wonder if the memory was being cleared as it was returned from malloc. Given that I had seen the vDSP functions, it made sense to use those - as opposed to just calling memset, as they handle doubles and integers, and I didn't want to mess up any framing on the doubles.

When I put in that code, which was really as simple as:

  // clear out the vector with zeros
  vDSP_vclrD(b, 1, ldab*nrhs);

and similar function calls for the other matrix allocations, the code just worked.

Potentials Simulation Fixed

And not just that... you can now edit the input in the editor window, and re-run it. Perfectly. Also, it's easy to reload different simulation sets, and re-run those, as well. This is exactly what I was hoping to see as the next big step. Now I need to be able to plot the electric field as well - magnitude and direction.

Potentials – Adding Plotting of Output Data

Friday, May 3rd, 2019

Building Great Code

This morning, I was able to finish up the plotting of the output data in Potentials. It's not generating the right numbers, but the NSView Quartz 2D drawing is working, and the linear color interpolation is doing its job as well. What seems to be the problem is the cLAPACK call to solve the system.

Potentials can Draw

I know this was all working years ago, so it has to be in the cLAPACK code in the Accelerate Framework in macOS - and there, anything is possible. Could be the format of the banded matrix storage changed, or the format of the numbers or structure of the A or b in the equation... but it's something... because I looked at the output, and the numbers just are not right.

The blank area at the bottom is where the data was either NaN or -Inf, and I know that's not right. And then the potential field distribution is all wrong. No way this is right. So I'll come up with a simpler test case, two line conductors, and a smaller grid, and see what's happening in more detail. It shouldn't be too hard to see what's up.

Then, it's just a matter of seeing what's next. Things are taking shape! 🙂

Potentials – Adding Initial Drawing to the App

Wednesday, May 1st, 2019

Building Great Code

Today I got back to Potentials and wanted to add in the initial drawing context to the app - by splitting the UI from being just an editor to an editor at the bottom of the view, and a NSView subclass on the top. The bottom editor will be the complete width of the window, and the height will be fixed at a handful of lines. Then the drawing area is the stretchable area above that.

I had to re-learn a few things - like where the outlets and actions are held, and it turned out that it really was all about the nib file format, and once it was converted to an xib, I was able to find everything where I expected to see it.

Potentials Starts Drawing

There were a few AutoLayout issues that were easy enough to correct with the xib file, and then it was a few simple drawing functions, and it's working just great. I have a lot more to do, but I think the Voltage plots are going to be pretty simple... just a color range on the values, and away we go.

It's nice to be back in ObjC and macOS... such a fun platform. 🙂

Upgrading Old Interface Builder Files

Wednesday, May 1st, 2019

xcode.jpg

This morning I was working on my Potentials app for macOS, and I realized that the Auto Layout constraints were not being set properly, and the Xcode 10.2.1 UI wasn't showing the existing constraints, either. I noticed this was still a nib file, and I know Xcode has moved to xib files many years ago, so I looked up how to upgrade the file's contents.

The solution was pretty easy:

  $ cd Base.lproj/
  $ ibtool MainMenu.nib --upgrade --write MainMenu.xib

and then I can add the MainMenu.xib to the project, and delete the MainMenu.nib from the project. Check it all in, and we're done.

My concern is that Xcode should have said something. Like "Upgrade this file", or "Let me upgrade this file"... but leaving it alone and not really working right - that's not really a good thing to do. Still... I've been using these tools long enough, I had a feeling it was something like this.

Updated Potentials to Xcode 10.2, and up to GitHub

Monday, April 15th, 2019

GitHub Source Hosting

This morning I was looking back at an older project I had on macOS - Potentials, an engine that solves the 2D Poisson equation for a space where you can place conductors, dielectrics, and charge sheets, all of different shapes and sizes, and then use the vecLib CLAPACK call to solve the system of equations very quickly. Finally, it outputs the V(x,y) and E(x,y) to a file for plotting.

I remember writing it on a NeXTSTEP 3.3 Turbo Color Slab, because there was a plotting package that could read the data file, and until I could get around to putting that code into the app, I'd be able to check the results on the plotting package - HippoDraw. Sadly, that app moved to what it is today, on Windows, and I was just not motivated enough to do the plotting on NeXTSTEP.

This morning, I decided to get it out, and get it going on macOS 10.14.4 and Xcode 10.2. I had expected some challenges, and I was not disappointed, but I was also not denied.

The first thing was to allow Xcode 10.2 to tell me of the issues. Some were easy localization fixes, others weren't as easy to see. For example, vecLib has been merged into the Accelerate Framework. So in the code, where I'd had:

  #import <vecLib/clapack.h>

I had to replace it with:

  #import <Accelerate/Accelerate.h>

And then I dropped the vecLib Framework from the list of Frameworks to link to - face it - it was gone. 🙂 Then I needed to add in the Accelerate Framework to resolve the CLAPACK functions. This was something that I had forgotten to do, but it's really not too bad.

Changes to Potentials for Xcode 10

At this point, it built and ran just fine.

I then removed the old origin from the git repo, created a new repo on GitHub, and then pushed it all up there. It's so much more convenient to have it up there, as opposed the old git server that I had running on my own network. Just not even close.

Finish it off with a decent README, and we are now ready to work on writing that visualization of the output that I've wanted. Because it seems that there is no really good open source ObjC graphing packages for flattened 3D plots into 2D, and that's what I need - something that can plot these electric fields in 2D. Something to work on going forward.

More Problems with Object Designs

Wednesday, April 3rd, 2019

xcode.jpg

This morning I was playing around with the CryptoQuip solver, and realizing that the mapping code I had could be made a little simpler if I used the fact that ASCII character representations are all numerically ascending, and that makes character math something to use - as opposed to simple look-ups. For instance, if we look at the existing code for +createPatternText::

  1. + (NSString*) createPatternText:(NSString*)text
  2. {
  3. const char *src = [[text lowercaseString] UTF8String];
  4. NSUInteger len = [text length];
  5. const char *ascii = "abcdefghijklmnopqrstuvwxyz";
  6. char pattern[255];
  7. for (NSUInteger i = 0; i < len; ++i) {
  8. pattern[i] = ascii[strchr(src, src[i]) - src];
  9. }
  10. pattern[len] = '\0';
  11. return [NSString stringWithUTF8String:pattern];
  12. }

the look-up on line 8, using the const char array defined on line 5, is really just an offset calculation, and can be replaced with:

  1. + (NSString*) createPatternText:(NSString*)text
  2. {
  3. const char *src = [[text lowercaseString] UTF8String];
  4. NSUInteger len = MIN([text length], 255);
  5. char pattern[255];
  6. for (NSUInteger i = 0; i < len; ++i) {
  7. pattern[i] = 'a' + (strchr(src, src[i]) - src);
  8. }
  9. pattern[len] = '\0';
  10. return [NSString stringWithUTF8String:pattern];
  11. }

and the result is exactly the same. But now, we're not creating the const char array on the stack, on each call, and the offset into the array is the same addition as we are doing here. Simpler.

But when we go to Swift, we see that the beauty and elegance of the underlying data is being clouded with the all-too-common object oriented design of Character.

  1. var pattern: String {
  2. get {
  3. let ascii: [Character] = ["a","b","c","d","e","f","g","h","i","j","k",
  4. "l","m","n","o","p","q","r","s","t","u","v",
  5. "w","x","y","z"]
  6. var ans = ""
  7. let src = Array(self.lowercased().utf8)
  8. for c in src {
  9. ans.append(ascii[src.firstIndex(of: c)!])
  10. }
  11. return ans
  12. }
  13. }

Sadly, we can't simplify line 9 because the Character object doesn't allow for construction based on an ASCII value. Sure, it has all kinds of nice interrogation methods to test and see if it's ASCII, or uppercase, or a number... but you can't actually do the simple math of adding 1 to 'a' and getting 'b'. That is a shame, and it's not something you can easily add because the Character class would need a new -init: method.

So while I really like parts of Swift 5, I think they didn't really think about all the uses that ObjC fits into when making the new classes. It's a process, I get that, but it's causing folks to write code around these issues, and that's a mistake. No one will go back and fix their code when, and if, they add a way to do this.

Adding Pattern Generation to CryptoQuip

Tuesday, April 2nd, 2019

xcode.jpg

This morning I took a little time to bring the pattern matching code from the Clojure and Swift codebases of the Quip solver to the ObjC version because I noticed the other day that the ideas of the pattern matching were added after the ObjC version was working, and I hadn't back-filled them to the older code. I know it won't make a difference in the execution time, as this is all done before the attack is started, but the old code really was a mess, and the new code had to be cleaner.

The original code had the following method on the CypherWord:

- (BOOL) matchesPattern:(NSString*)plaintext
{
    BOOL  match = YES;
 
    // make sure that we have something to work with
    if (match && (([self getCypherText] == nil) || (plaintext == nil))) {
        match = NO;
    }
 
    // check the lengths - gotta be the same here for sure
    if (match && ([[self getCypherText] length] != [plaintext length])) {
        match = NO;
    }
 
    /*
     * Assume that each pair of characters is a new map, and then test that
     * mapping against all other cyphertext/plaintext pairs that SHOULD match
     * in the word. If we get a miss on any one of them, then we need to fail.
     */
    if (match) {
        unichar    cypher, plain, c, p;
        NSUInteger len = [plaintext length];
        for (NSUInteger i = 0; (i < len) && match; ++i) {
            // get the next possible pair in the two words
            cypher = [[self getCypherText] characterAtIndex:i];
            plain = [plaintext characterAtIndex:i];
            // check all the remaining character pairs
            for (NSUInteger j = (i+1); (j < len) && match; ++j) {
                c = [[self getCypherText] characterAtIndex:j];
                p = [plaintext characterAtIndex:j];
                if (((cypher == c) && (plain != p)) ||
                    ((cypher != c) && (plain == p))){
                    match = NO;
                    break;
                }
            }
        }
    }
 
    return match;
}

And it worked because it checked each pair of characters in the plaintext and cypher text, and looked for mismatches... but it wasn't very clean, and the same scanning had to be done on the same word over and over. So it wasn't all that efficient.

Then I added a class method to CypherWord to generate the pattern:

+ (NSString*) createPatternText:(NSString*)text
{
    const char *src = [[text lowercaseString] UTF8String];
    NSUInteger  len = [text length];
    const char *ascii = "abcdefghijklmnopqrstuvwxyz";
    char        pattern[255];
    for (NSUInteger i = 0; i < len; ++i) {
        pattern[i] = ascii[strchr(src, src[i]) - src];
    }
    pattern[len] = '\0';
    return [NSString stringWithUTF8String:pattern];
}

In the setter we then added:

- (void) setCypherText:(NSString*)text
{
    _cyphertext = text;
    _cypherSize = [text length];
    _cypherPattern = [CypherWord createPatternText:text];
}

So that setting the CypherText set the length and the pattern as well. Not bad at all, and the overhead is minimal for the puzzles we're dealing with.

At this point, we can then re-write the -matchesPattern: to be:

- (BOOL) matchesPattern:(NSString*)plaintext
{
    return ([self getCypherText] != nil) && (plaintext != nil) &&
           ([self length] == [plaintext length]) &&
           [[self getCypherPattern] isEqualToString:[CypherWord
             createPatternText:plaintext]];
}

and leverage the fact that we have instance variables for the length and pattern, and still use all the short-circuit techniques to make sure that we don't make a bad call, or sloppy comparison.

Nothing amazing, but it's nice to get this version up to the same design as the others.