Archive for the 'Macintosh' Category

How to implement a vector brush

Since I’ve previously written about how to implement a basic bitmap brush I thought it would be fun to figure out how to build a vector brush, like what you would find in Adobe Illustrator or Fireworks. A vector brush works the same as a bitmap brush from the viewpoint of the user, except that the resulting brushing stroke is scalable and otherwise editable after it is made. That’s because the resulting brush stroke ends up being a bezier path. In this post, I’ll show how it’s done.

If you’re impatient, you can always skip to the source code. It is licensed under the MIT license.

Naive Implementation

The direct way to implement the vector brush would be to simply record the mouse movements into a NSBezierPath. On a mouse down, create an NSBezierPath, do a move to the first point, then on subsequent mouse drags add line to’s to the NSBezierPath. Since I already have an NSBezierPath now, I can just draw it to represent the brush stroke. If you run the sample application with all the options under the Options menu (Show Points, Simplify Path, and Fit Curve) turned off, it’ll do exactly that. A brush stroke will look something like:

Naive vector brush

That actually doesn’t look bad. Does that mean I’m done? To answer myself, I’ll turn on the Show Points option under the Options menu:

Naive vector brush show points

Show Points draws an orange, hollow square around each of the points that make up the path. As you can see, there are a ton of them, about 160 in this case. Having that many points means editing the path afterwards is going to be rather difficult. Also, I can see that most of the points are redundant; they aren’t adding any new information to the path.

Brush strokes, simplified

At this point the best way to improve the my brush is to remove all the redundant points from our NSBezierPath. Since all the extra points don’t get in my way until mouse up happens, I’ll simply do some post processing on the NSBezierPath on mouse up. I’ll use the Ramer-Douglas-Peucker algorithm to identify and remove the redundant points. To demonstrate the effects of this algorithm, I’ll turn on the Simplify Path option, and draw another brush stroke:

Simplify path vector brush

You can see a big difference here in the number of points required to make up the path. The sample application outputs (using NSLog) how many points the path had before and after simplifying the path. In this case, the points went down from 170 to 15.

The Idea

The idea behind the Ramer-Douglas-Peucker algorithm is pretty simple. It takes a path and a threshold as parameters. I’ll show an example to demonstrate it. Let’s start with the following path:

Simplify path example 1

I’ve numbered the points for easy reference. To start with I imagine a line between the end points, 1 and 5. I then measure the distance between each of the interior points (2, 3, 5) and the line made by points 1 and 5.

Simplify path example 2

I take the greatest distance (the one between point 3 and my line) and compare it to the threshold passed in. If the distance exceeds the threshold, I have to subdivide the path and recurse. I split the path at the greatest distance (point 3 in this case), which gives me two paths: [1, 2, 3] and [3, 4, 5].

I repeat the process on [1, 2, 3] as my path:

Simplify path example 3

Here my end points are 1 and 3, and my only interior point is 2. Like before I take the distance between point 2 and the line formed by 1 and 3, and compare it to the threshold passed in. In this case, the distance is less than threshold. Thus, I can say the interior points are all redundant and the path [1, 2, 3] can be represented by the endpoints 1 and 3.

Simplify path example 4

Going back to the second recursion mentioned, I repeat the same process on [3, 4, 5]. In this case the distance between point 4 and the line between points 3 and 5 is less than the threshold. Because of that, I can represent the path [3, 4, 5] with just the end points 3 and 5. Thus, the final result is:

Simplify path example 5

The Code

As you might expect the code for this algorithm is rather simple. It’s just one method (not counting the utility methods to access NSBezierPath data), which I’ve added as a category to NSBezierPath.

It takes a threshold as a parameter and returns the simplified path. It needs at least three points on the path before the algorithm will work.

- (NSBezierPath *) fb_simplify:(CGFloat)threshold
{
    if ( [self elementCount] <= 2 )
        return self;

Next it calculates the distance between the interior points and the line formed by the two end points. It remembers the largest distance and where in the path that point is.

    CGFloat maximumDistance = 0.0;
    NSUInteger maximumIndex = 0;

    // Find the point the furtherest away
    for (NSUInteger i = 1; i < ([self elementCount] - 1); i++) {
        CGFloat distance = FBDistancePointToLine([self fb_pointAtIndex:i], [self fb_pointAtIndex:0], [self fb_pointAtIndex:[self elementCount] - 1]);
        if ( distance > maximumDistance ) {
            maximumDistance = distance;
            maximumIndex = i;
        }
    }

FBDistancePointToLine() is a function I wrote that calculates the distance between a point and a line. fb_pointAtIndex is just a helper method that returns the point on the path at that index.

Now that it has the greatest distance, it checks to see if that distance is significant. If it is, then it recurses on itself, passing in the two halves split by the greatest distance. When the recursive calls return it joins the two results back into one, and returns those.

    if ( maximumDistance >= threshold ) {
        // The distance is too great to simplify, so recurse
        NSBezierPath *results1 = [[self fb_subpathWithRange:NSMakeRange(0, maximumIndex + 1)] fb_simplify:threshold];
        NSBezierPath *results2 = [[self fb_subpathWithRange:NSMakeRange(maximumIndex, [self elementCount] - maximumIndex)] fb_simplify:threshold];

        [results1 fb_appendPath:[results2 fb_subpathWithRange:NSMakeRange(1, [results2 elementCount] - 1)]];
        return results1;
    }

fb_subpathWithRange: is another utility method. It makes a copy of part of the path specified, and returns it. fb_appendPath appends one path to another, except it also will remove spurious move to’s and replace them with line to’s.

Finally, if the greatest distance isn’t significant, then all the interior points are redundant. In this case, just create a new path with the two end points.

    NSBezierPath *path = [NSBezierPath bezierPath];
    [path fb_copyAttributesFrom:self];
    [path moveToPoint:[self fb_pointAtIndex:0]];
    [path lineToPoint:[self fb_pointAtIndex:[self elementCount] - 1]];
    return path;
}

The only new utility method this time is fb_copyAttributesFrom:, which copies over attributes like caps, joins, and miters to the target path.

Curvy is better

So now I have a much simplified path representing my brush stroke. But my path is comprised of lines only, which are quite limited in representing anything other than a line. I’d like to have a way to represent curves. That way editing the path afterward would be even more powerful, plus I could reduce the number of total points on my path even more. Enter bezier curves.

Defining bezier curves is beyond the scope of this article. As far as this post is concerned they are a parametric curve defined by four points: two end points and two control points.

To further improve my brush stroke, I’m going to take my simplified path of lines, and fit a series of bezier curves to it. The algorithm for doing this is documented in Graphics Gems 1 in the article titled “An Algorithm for Automatically Fitting Digitized Curves” by Philip J. Schneider. Unfortunately, Graphics Gems is out of print. A Kindle version is available on Amazon, but it is 1) US$80 and 2) a poor quality scan. I would suggest buying the book used if you want a copy.

You can see the results of curve fitting by turning on the Fit Curve options in the Options menu and drawing a stroke. Here’s an example:

Fit curve vector brush

The black boxes are bezier curve control points. In this case using both path simplification and curve fitting reduced the number points from 197 to 7. Even better, bezier curves give the user more flexibility and power when it comes to editing the path.

Curve fitting overview

The curve fitting technique is somewhat involved and makes use of several different algorithms. I’ll only cover the methods unique to the algorithm and skip over a lot of details. That said, the sample code is complete and commented, so it can be used to fill in the details.

To start with, I’ll assume the function Q(t) represents a bezier curve that fits the points on my path. The parameter, t, ranges from 0 to 1, and Q(t) outputs x,y coordinates on my bezier curve. The first thing to do is calculate estimated values of t that match up to the points on my path. That is, Q(t[index]) should equal the point on my path at index.

After generating inputs for Q(t), the next step is to try to fit one bezier curve to the all the points on the path given the inputs. (I’ll describe the exact process later.) I’ll then compute how far the resulting bezier curve falls from the points on my path. If the curve is close enough, then I’m done.

If the bezier curve isn’t close enough to fit, I’ll see if it’s close enough that it makes sense to refine the inputs (t). If it is, I’ll repeatedly refine the input parameters (t) using Newton’s Method, until the resulting bezier curve fits sufficiently or I try more than four times.

If refining the input parameters doesn’t lead to a fit, it’s time to give up on one bezier curve, divide the path, and recurse. I’ll split the path into two at the point the bezier curve is furtherest from (i.e. where the greatest error is at). I’ll then recurse on the two halves, and then combine the two resulting curves into one bezier path for the final result.

The method fb_fitCubicToRange: in the NSBezierPath (FitCurve) category contains the code for the process described above if you want more detail.

Fitting a bezier curve to points

Remember that a bezier curve is defined by four points, like below.

Bezier curve points

Here points 1 and 4 are the end points and 2 and 3 are the control points. The orange lines just visually connect the control point with its corresponding end point. The blue curve is the bezier itself.

If we’re given a path of points to fit a curve to:

Path points

it’s easy to select the end points of the bezier curve; they’re simply the end points of the path (1 and 6 here). The direction of the control points relative to their end points is also simple to determine: simply compute the vector between the path end points and their nearest neighbor. In this case the direction of the left control point is the vector between points 1 and 2 in the path. The right control point direction is the vector between points 5 and 6. The only unknown in constructing our bezier curve is the distance between the control points and their respective end point.

Stated another way:

leftEndPoint = first point in path
rightEndPoint = last point in path
leftControlPoint = leftAlpha * leftTangent + leftEndPoint
rightControlPoint = rightAlpha * rightTangent + rightEndPoint

Where leftAlpha is the distance between the leftEndPoint and leftControlPoint, and rightAlpha is the distance between the rightEndPoint and the rightControlPoint. leftTangent and rightTangent are the unit vectors between the end points of the path and their closest neighbors.

By controlling the distance of the control points from their end points, I can affect the curve of the bezier. I want to chose distances (leftAlpha and rightAlpha) such that it best fits the points in the path. Formally stated, I want to minimize the squared errors as represented by:

S = SUM( (point[i] - Q(t[i])) ^ 2 )

Where point[i] is the point on the path at index i, t[i] is one of the estimated parameters I generated earlier, and Q is bezier curve. Remember that point[i] should be equal to Q(t[i]), assuming the bezier curve fits perfectly my points. Here I’m calculating how far each point on the bezier curve is off, squaring that, then adding it all up (squared errors). This is the error I calculate when I’m trying to determine if a bezier curve is close enough that I’m done. Right now though, I want to minimize S when calculating leftAlpha and rightAlpha.

Using the least squares approach I can write the equations I want to solve as:

d(S) / d(leftAlpha) = 0
d(S) / d(rightAlpha) = 0

Here d() is derivative, usually written as the Greek letter delta. S is the squared errors defined above.

At this point I do need to introduce the definition of a bezier curve:

Q(t) = SUM(V[i] * Bernstein[i](t))

Where i ranges between [0..3], and V is one of the points on the bezier curve. V[0] is the leftEndPoint, V[1] the leftControlPoint, V[2] the rightControlPoint, and V[3] the rightEndPoint. Berstein[i] is a polynomial that is highly regarded as boring for this discussion. (Look at the code if you really want to know.)

Now that I have Q defined, I can substitute it in the equation S, and then S into the two equations I want to solve. After a lot of mathematical voodoo and gymnastics (see the paper for all of that), I arrive at:

leftAlpha = det(X * C2) / det(C1 * C2)
rightAlpha = det(C1 * X) / det(C1 * C2)

Here det() is determinant, * is the dot product, and X, C1, and C2 are all 2×1 matrices defined below.

C1 = [SUM(A1[i] * A1[i]), SUM(A1[i] * A2[i])]
C2 = [SUM(A1[i] * A2[i]), SUM(A2[i] * A2[i])]
X = [SUM(partOfX * A1[i]), SUM(partOfX * A2[i])]

Where

partOfX = (points[i] - (leftEndPoint * Bernstein0(t[i]) + leftEndPoint * Bernstein1(t[i]) + rightEndPoint * Bernstein2(t[i]) + rightEndPoint * Bernstein3(t[i])))

And

A1[i] = leftTangent * Bernstein1(t[i])
A2[i] = rightTangent * Bernstein2(t[i])

If you’re interested in how this all plays out in code, check out the method fb_fitBezierInRange: in the NSBezierPath (FitCurve) category.

Calculating parameters (i.e. t)

The parameters (t) are useful for correlating the points on my path with the bezier curve I’m trying to fit. I know that t has to range from 0 to 1, and Q(t) is supposed to correspond to the points on my path. Q(0) should be the first point on my path, and Q(1) should be the last.

The initial estimate of t uses the chord length method. Ideally, t would be the length of curve from the left end point to the point corresponding to t, divided by the total length of the curve. However, the chord length method makes the assumption that a straight line is a reasonable estimate of a curve. So to estimate t, I measure the length of the line segments from the left end point to the point corresponding to t, then divide that by the length of all the line segments.

You can inspect the implementation of this in the fb_estimateParametersUsingChordLengthMethodInRange: method.

If my initial estimate proves to be poor, I can further refine it using Newton’s Method. Newton’s Method says I can refine each t value by:

t = t - f(t) / f'(t)

In my case

f(t) = (Q(t) - point) * Q'(t) = 0

Here (Q(t) – point) is the vector from a point on the bezier curve to the corresponding point on my path. Q’(t) is the first derivative, which is tangent to the bezier curve, Q, at t. (Q(t) – point) is orthogonal to Q’(t), which means the dot product of the two is zero.

Newtons method parameters

Based on that, f’(t) is

f'(t) = (Q(t) - point) * Q''(t) + Q'(t) * Q'(t)

The NewtonsMethod() function in NSBezierPath+FitCurve.m implements this.

Conclusion

Although I skipped over a lot of detail when it came to curve fitting, hopefully this post has been helpful in understanding how it works, in addition to explaining the vector brush. For the details that I skipped over, the code should fill in the blanks. The vector brush is a fun tool to play with and the curve fitting algorithm has other applications too.

Illuminate is in the wild

I officially released Illuminate today. If you’ve ever had difficulty finding a window, or just regularly have a ton of them open, or Exposé just isn’t cutting it for you, you should definitely give the free 30-day trial a try.

If you’d like to know more about Illuminate, you can always visit the product page or read the press release.

Illuminate Beta Program

Update: I’m no longer able to take any more people into the beta program at this time. Thanks for your interest!


If you’re anything like me you have a lot of windows open at any one time. I usually have an ungodly number of Safari and Xcode windows open full of research and code. The problem is I often lose track of which window is where, and have difficulty getting to the window I want. The respective windows menus at this point are at an unwieldy length and Exposé just shows me such tiny thumbnails that I can’t tell if that one’s the header file I’m looking for or the latest Penny Arcade.

It was for this reason I wrote Illuminate. Sometimes I can recognize the window by sight, but I only want to see a reasonably number of them at a time. Sometimes I know the window name and I just want to go directly there. Other times I don’t remember either, just a key phrase I know appears in the window. Illuminate can handle all of these cases for me.

Illuminate is now in a “closed” beta. If you love trying out new software and giving constructive feedback, please sign up at the Fortunate Bear website. Edit: The beta is currently full.

The Fortunate Bear FAQ

As some of the more astute readers might have noticed, the sidebar and about page of this blog have changed to remove references to Order N and their products and services. You might be wondering what that’s all about. I’ve decided to answer your potential questions in FAQ format, in order to sound more pretentious.

So what’s the deal with Order N?

They’re still around, successful, and offering custom development services for the Mac and iOS platforms. However, I sold my shares in the company back in October and I’m no longer part of Order N.

What do you do now?

Mainly sit around in my underwear, playing solitaire.

…uh, anything else?

Sometimes I pet my cats, Luna and Pasha.

Alright, I guess we’re done here.

Wait, before you go, let me tell you about my new company.

You could’ve mentioned that earlier.

And miss out on all the interesting tidbits about Mac indie life?

Anyway, so you have a new company?

Yep, still shiny and everything.

…anything you want to say about it?

Yeah, it’s going to be awesome, sweet, sick, and possibly jivetastic, if that’s still a word.

I’m not sure that was ever a word. What’s the company going to do?

Be awesome. Gonna have our own tree fort and everything. See:

Klubhouse.jpg

It’s like Panic’s Founder’s Room, only cooler. I’m also working on the official company super secret handshake, but I can’t think of anything after “patty cake, patty cake, baker’s man.”

Wow, there’s so many things wrong with that, I don’t know where to begin. But I see the company name appears to “Fortunate Bear.” Where does the name come from?

Insomnia induced delirium, and possibly the sheer jivetasticness of myself.

Really?

No, I just randomly matched up adjectives and nouns until I found one I liked the sound of, and my wife wouldn’t divorce me over.

Is Fortunate Bear going to be a software company or what?

Oh, right, software. Yes, software will be involved in the process. I’m not much for playing solitaire with real cards. It’s the reason why the iPad is so magical.

Is the whole FAQ going to be this way? Because there’s a netiquette FAQ I could be hosting.

Alright, fine. Fortunate Bear is a software company focused on creating Mac and iOS apps to sell under the Fortunate Bear brand. I am the sole owner.

What about contracting/freelancing?

I might have to do some in the early years of Fortunate Bear, but it will be kept a bear minimum (see what I did there?) and phased out as soon as possible. I am not currently available for contracting. I’m focusing on building my own products.

What can you tell me about the products you’re working on?

I’m working on a Mac app for window management. It’s like Exposé except it doesn’t suck. I’ll be announcing a beta for it soon.

What about Hearts Attack?

I bought the source and rights for Hearts Attack from Order N. I’m renaming it, adding a few features, and changing the way it will generate money. I’m hoping to release it soon, but not until I get my Mac app out.

How can I get more information about Fortunate Bear and its products?

This blog will continue to be the main place I write and make announcements. However, I have a placeholder site for Fortunate Bear. You can sign up for the company newsletter to get announcements about products as well as tips and tricks after the products are released. There’s also an official Fortunate Bear Twitter account you can follow.

Anything else you want to add?

It’s gonna be jivetastic.

Please shut up.

Compiling OpenCL Kernels with Xcode

The talk I’m working on for NSConference 2010 involves a decent amount of OpenCL kernel code. Originally, I thought I would develop the kernel code the same way I develop Core Image kernel code: build the kernel in Quartz Composer and then copy paste the code into Xcode when I got it working.

Unfortunately, I’ve not had much luck with Quartz Composer in way of meaningful compiler errors. Sometimes it returns good errors, sometimes inscrutable ones.

Quartz Composer

Composer: Wait, wait, I remember this… it’s COBOL, right?

 

However, I’ve found debugging OpenCL kernels to be much easier thanks to the invention of a function I like to call “printf.” If you run your kernel on the CPU, then you can print out values just like in C. With debugging solved, I just needed to figure out how to find compiler errors without having to run my code.

Unlike Core Image, OpenCL actually requires you to compile your kernel code explicitly before using it. The API’s required to do this will return nice compiler error messages and everything. The obvious thing to do would be wrap up these API’s in a command line tool and invoke the tool from Xcode, which is what I did.

The Code

You can download the Xcode project for clc, the OpenCL Compiler here. It requires Mac OS X 10.6 Snow Leopard and the associated Xcode tools. To install, just build the project and copy the resulting binary into your path somewhere. I used ~/bin.

Most of the code is self explanatory, with the possible exception of the output. Unlike most compilers, clc doesn’t output object code because the host and runtime CPU/GPU’s could easily be different. It could simply copy the source code as is for the output, but that was a little too obvious for my tastes. Instead clc compresses the source code and stuffs it into a binary plist. It has the slight benefit of potentially being smaller on disk, and making your source code negligibly less accessible to prying eyes, if you’re into that sort of thing.

There are definite possibilities for improvement. For example, there could be an option to encrypt the source code to be more “secure.” Or it could store the binary code generated by the host machine in the output on the chance that the host and runtime machines have the same hardware.

Setting up Xcode

To use clc, you’ll need to set up a build rule in your app’s target settings to run all OpenCL source files through it. Selecting the menu item Project > Edit Active Target, and then selecting the Rules tab, should land you here:

TargetSettings.png

Add another rule, set it to process OpenCL source files using a Custom script. For the custom script enter:

~/bin/clc ${INPUT_FILE_PATH} -o ${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/Contents/Resources/${INPUT_FILE_BASE}.clar

Finally, you’ll need to tell Xcode where you’re putting the output file, which is:

${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/Contents/Resources/${INPUT_FILE_BASE}.clar

By default, Xcode won’t put OpenCL source files (.cl) into the target, meaning they won’t get compiled. For each of your OpenCL source files
you’ll need to explicitly add them to your target. There are several ways to do this, but the easiest is probably selecting all of them, and doing a Get Info (File > Get Info). Switch to the Targets tab and check the target they should be compiled into.

Runtime Code

At runtime, it’s pretty easy to retrieve the original OpenCL source code using the + (NSString *) openCLSourceWithData:(NSData *)data error:(NSError **)error; method on ONOpenCLArchive. However, there are a couple of convenience methods on NSBundle that makes retrieving the kernel easier. For example:

#import "ONOpenCLArchive.h"

NSString *kernelSource = [[NSBundle mainBundle] openCLResource:@"MyKernel"];

That’s pretty much all there is to it.

Conclusion

Although not ground breaking, this little tool has certainly been helpful to me by finding compiler errors at compile time instead of runtime. Hopefully it will be useful to other people as well.

levitra 125 mg
cheapest prices on generic viagra
buy free viagra viagra
buy viagra onli
buy viagra online and get prescription
cheap overnight viagra
huricane ike home repair wanted
buy online pill viagra
cheap viagra online uk
50mg cialis
buy cheap deal viagra viagra viagra
cheap cialis canada
36 hour cialis
mobile home roof repair
cheap levitra online
20 mg cialis
home study computer repair course dvd
buy cheap generic viagra
buy viagra in great britain
buy viagra without prescription
buy viagra in london
buy cheap viagra uk
cheap pill viagra
viagra and coupon
generic viagra 20mg pills erections
voagra online without prescription
viagra 50 mg
viagra and cialis and
bruces home repair salem or
cheap online generic viagra
viagra 100mg price
buy viagra in spain
cialis 5m tablets
cheapest price for generic viagra
mobile home repair rochester wa
buy viagra softtabs
cheapest price viagra
viagra buy it online now
rx cialis 100mg
buy low price viagra
cheapest place to buy viagra
buy cheap online uk viagra
home foundation repair
100mg viagra
buy pill viagra
buy viagra online
voagra online without prescription
buy viagra in canada
generic viagra 100mg
cheap online viagra viagra
buy line viagra where
federal grants for home repair
cheap prescription viagra
cheapest generic price viagra
10 generic levitra for 19.95
buy prescription viagra
levitra online prescription
buy levitra 50mg
cialis brand cialis 100mg
buy cialis online 50mg
in home tv repair canton nc
buy viagra in toronto
viagra by mail canada
home repair tax deductable
100 mg cialis us pharmacy
viagra brand viagra 100mg
mobile home floor repair
buy viagra online
100mg levitra
levitra 25 mg order
viagra 20 mg
buy viagra next day delivery
cheapest uk supplier viagra
viagra and cialis cheap
cialis 10mg 20mg
cheapest 4 quantity of viagra
buy levitra online
buy cialis online viagra
250 mg viagra
home window repair in sc
buy generic viagra viagra
buy cheap generic viagra online
cheap cialis canada
cheapest price on viagra
cheap viagra canada
viagra by the pill
buy keyword online viagra
cheapest prescription viagra
buy viagra on-line
50 mg viagra retail price
buy online drug viagra pharmacy
cheap 25mg levitra
viagra buy ionline
cialis 100mg price
levitra without prescription
home repair denver coloradao
buy discount viagra
buy cheap online prescription viagra
viagra 25 mg
buy now online viagra
levitra 10mg 20mg
buy cost low viagra
purchase levitra online
buy viagra onlines
buy viagra in australia
ron hazelton home repair
buy cheap online viagra viagra
cheap online pill viagra
100 mg levitra us pharmacy
cheap phizer viagra
bruces home repair
viagra by phone
buy online online viagra viagra
buy cheap viagra online u
cheap levitra canada
cialis online prescription
cheap levitra online
buy locally viagra
buy cheap cialis generic levitra viagra
buy cialis internet
cheap viagra uks
sears home repair
buy viagra generic
50 mg cialis retail price
order 50mg levitra
viagra 50mg
cialis online
cialis 20
cheapest price for viagra
buy site viagra
discount drugs levitra 100mg
cheapest brand viagra
buy levitra online 50mg
home repair replacing circuit breakers
buy viagra low cost
buy prescription vaniqa viagra
discount drugs cialis 100mg
buy cheapest viagra
buy discount propecia
home computer repair
buy cheapest online viagra
levitra 50mg
viagra buy online
home cd repair
cialis 25 mg
viagra buy viagra
buy in online uk viagra
home remedies to repair scratched cds
buy discount levitra
cheapest place buy viagra online
buy deal deal price viagra
buy online prescription viagra
viagra by mail order
buy viagra pill
viagra buy uk
250 mg levitra
buy deal herbal viagra viagra
buy pfizer viagra
buy cheap viagra online now uk
buy viagra internet
cheapest line viagra
buy viagra professional
buy cialis without prescription
buy viagra in uk
cialis 10 mg
viagra and cialas
good review sites for home repair
home repair vancouver
cheapest generic viagra and canada
cialis no prescription
10 generic cialis for 19.95
rx viagra 100mg
levitra 25 mg
buy viagra in malaysia
buy online levitra cialis viagra
vista home premium repair
cheap cialis canada
buy discount levitra
cheap viagra uk
buy online sale viagra
home appliance do it yourself repair
viagra by money order
levitra no prescription
cialis 20mg
cialis 20mg reviews
50 mg cialis
cialis reviews
buy cialis no prescription
buy generic viagra online
buy viagra removethis
buy levitra online viagra
cialis 24
buy cheap deal pill viagra
levitra 50 mg
weed eater home repair
mobile home repair remodeling
buy kamagra viagra
cialis 100mg
cialis 20 mg
cheap order site viagra
generic cialis 100mg
cialis 50 mg
buy viagra london
buy cheap discount levitra
cheap discount levitra
viagra no prescription
viagra 25 mg order
buy in online usa viagra
buy sale viagra
10mg cialis
buy viagra pills
viagra brand
buy viagra 50mg
buy viagra locally
cialis no prescription
cheap sale viagra
mobile home skirting repair
cialis 1
cheap prescription viagra without
buy real viagra online pharmacy
cheap viagra st
cialis 32
cheap online softtabs viagra
cheaper viagra levitra cyalis
buy viagra in mexico
buy online uk viagra
buy cheap viagra
cialis 125 mg
buy cialis viagra
viagra without prescription
buy online purchase viagra
cialis 50mg online
order 50mg cialis
buy lady uk viagra
50 mg levitra retail price
cheap site viagra
home repair grants
buy viagra usa
buy viagra uk
levitra 50mg online
36 hour levitra
buy online prescription viagra without
buy 100 mg viagra
cheapest generic viagra and cialis
buy viagra line
cheap online purchase viagra
buy generic viagra buy
cialis 2005
cheapest in uk viagra
buy viagra online alternative viagra
viagra buy oonline
scottsdale home repair
generic cialis wholesale 100mg
buy generic viagra
mobile home repair
buy cheap cialis
cheap viagra online prescription
buy cialis no prescription
buy now viagra
buy viagra sale
home repair grant
buy viagra in the uk
cialis black 800mg
buy pharmacy pill viagra
home electrical repair
order levitra
do it yourself home repair
cheap online sales viagra
buy no online prescription viagra
viagra 100mg
buy real viagra online
viagra by mail
buy viagra in bangkok
50 mg viagra
viagra buy in uk online
home repair frozen pipes
viagra buy viagra online
cheap pharmaceutical viagra
cheapest price for viagra and cialis
buy cheap cialis
buy viagra no prescription
canada pharmacy discounted levitra 100
buy real viagra
50 mg levitra
buy viagra in new zealand
home appliance repair
buy viagra london
buy cheap generic online viagra
buy viagra no prescription
36 hour viagra
mobile home repair parts
buy kamagra viagra india
buy price viagra
buy pill price price viagra
cialis 10
buy now levitra
cheap online viagra
buy cheapest viagra online
canada pharmacy discounted viagra 100
cheapest generic substitute viagra
buy later now pay viagra
cheap online price price viagra
buy say viagra
100 mg viagra us pharmacy
20mg cialis
cialis without prescription
order levitra online
cheapest place to buy viagra online
viagra by overnight delivery
buy cheap deal online viagra viagra
generic levitra wholesale 100mg
buy line viagra
buy viagra phuket
levitra 100mg
cheap price viagra
cialis 10
cheap 25mg viagra
cialis 25 mg order
viagra online prescription
generic cialis
cialis black
cheap online order viagra
buy online order viagra
viagra brazil
levitra 20 mg
buy online sale viagra viagra
viagra buy now pay later
amana refrigerator home repair
buy cialis now
registar repair windows xp home
cheap pill pill sale viagra
generic levitra 20mg pills erections
cialis 36 hours
cheapest online viagra
viagra buy australia
buy viagra online 50mg
buy cheap viagra online uk
cheapest cialis
buy viagra inte
buy prescription viagra without
cheapest generic viagra
levitra 100mg price
viagra 125 mg
viagra buy generic
home repair
buy viagra toronto
home repair handymen in fernley nevada
cialis without prescription
cialis 2005
cheap viagra without prescription
canada pharmacy discounted cialis 100
buy in uk viagra
buy pharmaceutical viagra
30mg cialis
discount drugs viagra 100mg
cialis 2.5
buy real viagra pharmacy online
buy cheap viagra on
cialis 20mg
home repair maintenance
home window repair
buy cheap viagra in uk
250 mg cialis
buy viagra in united kingdom
cialis 30mg
buy generic viagra pharmacy online
buy cheap viagra online now
cialis 20
tamiflu
100mg cialis
buy online price viagra
buy online online pill viagra viagra
10mg levitra
buy viagra soft
buy levitra no prescription
home repair plumbing
american pacific home repair
cheapest regalis viagra
home repair business
buy in spain viagra
buy free online sale viagra viagra
buy cheapest online place viagra
cialis 20 mg
buy generic no online prescription viagra
buy viagra line
10 generic viagra for 19.95
cialis 5mg
buy internet viagra
viagra by mail catalog
buy free viagra on internet
order 50mg viagra
buy cialis 50mg
cheapest site viagra
buy discount cialis
buy cheap viagra on the net
cheap soft tab viagra
cheap 25mg cialis
buy cialis online now
viagra and cialis
cialis 50mg
voagra online without prescription
cialis 10mg
usa cialis
buy cheap levitra
5mg cialis
viagra 10mg 20mg
buy viagra online at cheap price
cheapest cheap viagra
cheap viagra online order viagra now
home entertainment repair tips
40 grams of cialis
buy levitra canada