• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Advice for drawing complex, line-based designs
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Advice for drawing complex, line-based designs


  • Subject: Re: Advice for drawing complex, line-based designs
  • From: Graham Cox <email@hidden>
  • Date: Wed, 6 May 2009 14:21:23 +1000


On 06/05/2009, at 12:28 PM, Tom Roehl wrote:

I'm working on a project where I'm reading in a file that contains a list of coordinates that are used to define a stitch pattern for embroidery machines. Think of those machines you see at the mall that stitch names and logos on hats and t-shirts. To display the images, I created a document based application that uses each coordinate to build up a NSBezierPath and draw the image using NSBezierPath and lineToPoint. It seems pretty straight forward but there are a few wrinkles that are causing me problems so I thought I would ask for help. I tried to find some answers online with no luck so I apologize if this is basic stuff!

The first issue is the actual drawing of the lines. Since I'm trying to represent stitch patterns, I want to be able to show layers such that if one stitch crosses over another that there is some indication of which line is over the other. Think of way the laces in your shoes look, and that is the effect I am looking for. I thought I could solve it by finding a way to stroke the line with an outline but I couldn't figure out a way to do that. My first solution was to stroke the path in black a couple extra pixels wide and then to stroke the path again with a lighter color.


You can get the outline of a stroked path if you drop down to the CoreGraphics functions. There is CGContextReplacePathWithStrokedPath() which allows you to clip and paint a stroked path in a variety of ways, though if you want to recover that path back into a NSBezierPath, it's somewhat tricky. It can be done but requires use of a non-public function in CG (CGContextCopyPath).

However, stroking a path twice, first in a wider stroke, is a good way to do this if it creates the effect you need. You can deal with the the ends of a path in different ways - use a square cap style instead of butt for the lower stroke for example will cap the ends, or you can easily calculate a line that can be drawn at the right angle across the ends. Of course doing that all adds up in terms of drawing time.


This gave me a nice 3-D effect and was pretty close to what I wanted but it wasn't quite perfect. The problem was in the mitered ends. The stitches are typically back and forth with very narrow angles and the outline only showed up on the edges, but I really needed it to follow the full length of each stitch if that makes sense. My next solution was to create a NSBezierPath for each stitch. This solution gave me the look I wanted but I'm worried that it is a very expensive solution. Since a design can be made up of potentially tens of thousands of stitches I'm creating a huge amount of NSBezierPaths which strikes me as a problem.

It could be expensive, but you won't know until you can measure it.

Presumably it would be unusual to redraw the entire view every time, so ensure you only draw what intersects the update rectangles. use - needsToDrawRect: and -getRectsBeingDrawn:count: to obtain these - just using the rect passed to -drawRect: is probably not good enough when you want to squeeze every bit of performance. There are also ways to store the paths so that only those needing to be drawn can be quickly looked up - iterating a linear array works but can get slow when you have thousands of objects. BSP and R*-Trees are two ways that can help there (though unfortunately there's no built-in support for either).


The next problem is that once I've got the design drawn, I want to be able to zoom in and out and do other types of dynamic modifications of the image. For example, I may want to show or hide individual colors in the design. And of course, the image should be able to be scrolled if it is too big for the window. I had originally used -readFromURL to parse the file and store the line segments in my own internal structure. I then used -drawRect of the NSView to build the NSBezierPaths and draw the image. While this basically worked, I'm pretty sure this is the wrong way to do it.

Why? Sounds entirely reasonable. However, you definitely want to avoid creating the paths from the data in drawRect - instead try to do that somewhere else and then cache the paths resulting. Only draw in drawRect, as far as possible. You only need to compute the drawing paths once, or when the original data changes.



For starters, I think I'm recreating/drawing the image way too much by doing it in -drawRect. Should I be doing my drawing in - initWithFrame instead? Also, I'm struggling with zooming. Since I have all the x/y coordinates stored in an internal structure, should I be recreating the path with an appropriate scale factor? Or should I be trying to scale the created image? If I had all of the image in a single path it would be easy to transform it but how would I do it if I have thousands of paths?

Scale the view instead. This has the effect of automatically scaling any paths you draw, and the drawing code doesn't need to be aware of the zoom scale. Take care to only draw what's needed to be drawn, and you'll find that drawing speed should actually increase as you zoom in (because fewer paths have to be drawn).


NSView's -scaleUnitSquareToSize: is great for implementing zooming, or I have a simple NSView subclass that wraps that up nicely:

http://apptree.net/gczoomview.htm


Any help or suggestions would be greatly appreciated!


If you can reuse individual stitches by moving them to different places, then one way to gain a lot of speed is to use a CGLayer to cache the image. (note: NOT CALayer). This allows you to cache the image on the GPU and stamp it to different places in your view.

--Graham








_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


References: 
 >Advice for drawing complex, line-based designs (From: Tom Roehl <email@hidden>)

  • Prev by Date: Re: NSNotificationQueue & NSOperationQueue thread death
  • Next by Date: Re: NSNotificationQueue & NSOperationQueue thread death
  • Previous by thread: Advice for drawing complex, line-based designs
  • Next by thread: How to align a custom view in a toolbar ???
  • Index(es):
    • Date
    • Thread