drawRect without resetting the NSView

jspoon
Unregistered
 
Post: #1
I'm still working on my little simulation thing. I have my drawRect set up like this:

Code:
- (void)drawRect:(NSRect)rect
{
    if (startFlag == YES) {
        
        // Randomly draw a map on the view.
        
        // Give all the  mobile elements a
        // place on the map that makes sense.
        
        startFlag = NO;
    }
    
    // Draw all the elements.
}


I set the startFlag to YES in the initialization method. Once I get everything working, I'll make it so you can change settings and click a button to reset the simulation by changing startFlag back to YES. The idea is that since the background is random, I don't want to generate it every time.

After the first draw, I use a timer which calls a method that tells each element to move (if it wants to) then [self setNeedsDisplay] which results in just the elements being drawn. My problem is that this blanks out the whole view and then just draws the elements.

My ideal solution would be to get the view to redraw only the elements leaving the background the same. This is doable because all my elements are single pixels so it's easy for them to redraw the background behind themselves. In fact, at some point I might want them to leave a trail.

Otherwise, I suppose there must be some way of saving the map as an image and starting every draw by loading that.

I can't figure out how to do either of these things, though. The best I can think of how to do is saving my seed value and reconstructing the map for every frame which is really inefficient. Can someone give me insight on which of these solutions is better and how to go about it?
Quote this message in a reply
Apprentice
Posts: 19
Joined: 2004.10
Post: #2
The best thing to do is probably call setNeedsDisplayInRect:* with an object's old rect, and setNeedsDisplayInRect:* with the object's new location.

Then in your drawRect: code, change it to:

Code:
-(void)drawRect:(NSRect)inRect
{
  NSEnumerator *e;
  id obj;

  e = [objects objectEnumerator]; // assumes onscreen items are in NSArray
  while(obj = [e nextObject])
  {
    if (NSIntersectsRect([obj rect], inRect))
      [obj draw];
  }
}


This is the best (simple) approach. The disadvantage is that (last I checked) cocoa is not good at managing the update rect, and just performs a union of all rects...


To speed it up, you can maintain your own update list. If you want some help, post a follow up.
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #3
jfaller Wrote:...
This is the best (simple) approach. The disadvantage is that (last I checked) cocoa is not good at managing the update rect, and just performs a union of all rects...

To speed it up, you can maintain your own update list. If you want some help, post a follow up.

Creating a union of all update rects is typically faster than drawing a lot of single pixels or small rects, so this is not a bad thing per se.

What you should do is cache the background in an NSImage and draw from that, copying bits is fairly fast, faster than recreating the whole thing each frame, at least.
Quote this message in a reply
jspoon
Unregistered
 
Post: #4
Well, I tried to get what jfaller suggested to work and failed, which is a reflection solely on my abilities of implementing things I don't quite understand and also on the state of my codebase going into it. Three words: object-oriented spagetti. The way I was trying to do things, my objects were calling each other back and forth 4-5 times per operation. That's what happens when I stay up late trying to solve a problem rather than cutting my losses by going to bed.

Today, I saw DoG's and decided to give it a try since I was basically going to have to start over-I could hardly understand what I was trying to do last night let alone fix it. I didn't see a really simple way to put the contents of a view into an NSImage, so I poked around and found NSBitmapImageRep which I can initWithFocusedViewRect: and drawAtPoint. Using these I can keep previous views by just adding 2 lines to my drawRect method and not have to change any of my other objects to compensate.

At the same time as I was cutting the old stuff out I rethought things and in all shortened my code by about a third and reduced the method calls for figuring out where and how to draw each object by half, so at least some good came out of it.

Thanks for the help, both of you. Now, to plow ahead inadvisably making problems to fix tomorrow.
Quote this message in a reply
jspoon
Unregistered
 
Post: #5
jspoon Wrote:Using these I can keep previous views by just adding 2 lines to my drawRect method and not have to change any of my other objects to compensate.

Or three lines, if I remember to release last tick's frame before creating the new one.

I don't know if I should be proud of finding my first memory leak or worried that I messed up this way and probably will again in the future. It was pretty simple figuring out where it was, I didn't debug, just looked at activity monitor and saw I was eating 2 MB per tick (I have ticks set to 5 seconds until I know things are working the way they should be) and that's the only thing that could be that big.

Now I just have (probably) one last bug to find and I'm done with this little project. Thanks to everyone who helped me here and in the other thread.

PS-has anyone else noticed this forum doesn't play nice with Safari or maybe Macs in general? When I'm in this reply to thread screen editing text is slow and Safari is using 60% of the CPU which is how much I have free. Minimize the window and it drops to nearly nothing.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  drawRect not being called IckyThump 4 7,369 Aug 13, 2009 06:48 AM
Last Post: IckyThump
  Mouse Events in NSView Chandhu 1 4,159 Feb 28, 2008 02:08 AM
Last Post: kuon_
  drawRect in NSOpenGLView help fakeOne 3 4,517 Sep 3, 2006 09:04 AM
Last Post: Xenos
  NSView loaded from bundle won't draw... Joseph Duchesne 1 3,526 Feb 12, 2006 03:33 AM
Last Post: Cochrane
  drawRect: is not executed after setNeedsDisplay:YES Moridin 7 8,464 Oct 19, 2005 12:42 AM
Last Post: Moridin