If I have a NSView with 10 subviews, and I drag one of the subviews, what is the easiest way to determine which of the remaining views is closes to the dragged one? My code is working but I somehow feel I'm using a monkey wrench to fine tune a violin. Is there a more elegant way?
subviews is an array of the subviews of the parent view of this view (so it includes this view)
the toolTip of the sub views contain their page # formatted like "Page_#"
- (void) mouseUp: (NSEvent* ) e {
//set up a ridiclous # for current distance
float mtDistance = 12000000.0;
//get this page number
selfPageNum = [[[self toolTip] substringFromIndex:5] intValue];
//set up pageViews array
pageViews = [[NSMutableArray alloc] init];
//loop through the subviews
for (int i=0; i<[subviews count]; i++) {
//set up the view
thisView = [subviews objectAtIndex:i];
//filter for view classes
NSString* thisViewClass = [NSString stringWithFormat:#"%#", [thisView className]];
if ( [thisViewClass isEqual:#"Page"]) {
//add to the pageViews array
[pageViews addObject:thisView];
if (self != thisView) {
//get the view and self frame
NSRect movedViewFrame = [self frame];
NSRect thisViewFrame = [thisView frame];
//get the location area (x*y)
float movedViewLoc = movedViewFrame.origin.x * (movedViewFrame.origin.y * -1);
float thisViewLoc = thisViewFrame.origin.x * (thisViewFrame.origin.y * -1);
//get the difference between x locations
float mtDifference = movedViewLoc - thisViewLoc;
if (mtDifference < 0) {
mtDifference = mtDifference * -1;
}
if (mtDifference < mtDistance) {
mtDistance = mtDifference;
closesView = thisView;
}
}
}//end class check
}//end loop
//....more stuff
}
http://en.wikipedia.org/wiki/Distance
sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)))
Edit: Since you only need to find the shortest distance, not exact distances, you don't need to take the square root. Thanks for the insight Abizern
pow((x2 - x1), 2) + pow((y2 - y1), 2)
Related
I'm trying to optimize the performance in one of my components. The component needs to draw some (10 to 200) rectangles in it's drawRect method, which is triggered about 20 times per second.
Everything works when I use the CGContextFillRect method on each CGRect separately. I want to test if grouping the drawing into one single call with CGContextFillRects on an array of CGRects would increase performance.
The method CGContextFillRects gives me a compiler error No matching function for call to 'CGContextFillRects'.
This code is inside a .mm file. Should I import something before the CGContextFillRects method can be used?
This is what i'm trying to do:
- (void) drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
//check if some objects are present
if (self.leftDrawBuffer && self.rightDrawBuffer){
UInt32 xPosForRect = self.leftPadding;
NSMutableArray *rectsToFill = [[NSMutableArray alloc] init];
for (int drawBufferLRIndex = 0; drawBufferLRIndex < 2; drawBufferLRIndex++){
Float32 *drawBuffer_ptr = self.leftDrawBuffer;
if (drawBufferLRIndex > 0){
drawBuffer_ptr = self.rightDrawBuffer;
}
for (int i=0; i< kAmountOfBarsPerChannel; i=i+1){
Float32 amp = drawBuffer_ptr[i];
Float32 blockNumber = 1.0f;
UInt32 yPosForRect = self.bounds.size.height - self.heightPerBlock;
while (blockNumber <= self.blocksPerLine && blockNumber / self.blocksPerLine < amp){
CGRect rect= CGRectMake(xPosForRect, yPosForRect, self.widthPerBlock, self.heightPerBlock);
[rectsToFill addObject:[NSValue valueWithCGRect:rect]];
//Using the method below works and gives me the expected result
//CGContextFillRect(context, rect);
blockNumber++;
yPosForRect -= self.heightPerBlock + self.vPaddingPerBlock;
}
xPosForRect += self.widthPerBlock + self.hPaddingPerBlock;
}
}
//This is the added code where i try to use CGContextFillRects
//1 -> transform to a c array of CGRects
const CGRect *cRects[rectsToFill.count];
for (int i = 0; i < rectsToFill.count; ++i) {
CGRect rect = [[rectsToFill objectAtIndex:i] CGRectValue];
cRects[i] = ▭
}
size_t size = rectsToFill.count;
//2 -> trigger the method to fill all rects at once
//this method gives me the compiler error 'No matching function for call to 'CGContextFillRects''
CGContextFillRects(context, cRects, size);
}
CGContextRestoreGState(context);
}
The problem is how you convert the rects to a C array. You make pointers to the rects that are temporarily stored on the stack. There are two problems with this. First, the rects are gone with each loop iteration, so you can't do that. Second, You should pass a pointer to an array of CGRects, not an array of pointers to CGRect.
This will likely solve it:
CGRect cRects[rectsToFill.count]; // Replace your lines from this
for (int i = 0; i < rectsToFill.count; ++i) {
CGRect rect = [[rectsToFill objectAtIndex:i] CGRectValue];
cRects[i] = rect;
}
size_t size = rectsToFill.count;
CGContextFillRects(context, cRects, size); // To this
Please note the re-declaration of the cRects array and the change in the assignment.
I want to arrange my sprites at the following points using a for loop:
ccp(240.0, 160.0);
ccp(300.0, 120.0);
ccp(300.0, 200.0);
ccp(360.0, 80.0);
ccp(360.0, 160.0);
ccp(360.0, 240.0);
I am trying to get an iteration formula for these points using a for loop. I've been at it for a while. Below is the visual representation of points. Please help.
*
*
* *
*
*
Do you want something like this? (its a quick, sketch only)
Assume initial parameters:
initPoint (x0, y0)
initVector (vX,vY)
num of iteration c
index = c
while index
for(j = index, currentPoint = initPoint+(c-index)*(0,2*vY); j;j--, currentPoint += initVector)
draw currentPoint
index--
Basically the main idea is, that you start from the top right point, and shifts the context of the drawing with the initial vector as many times, as the iteration holds (to the bottom left corner) and draws the points. Then shifts the initial point down along the y axis and repeat minus one times.
Here's one way:
I didn't spend but a couple of minutes thinking about it, but it makes it easier if you start from the largest row and work your way down:
PatternTest.h
#import "cocos2d.h"
#interface PatternTest : CCLayer
#end
PatternTest.m
#implementation PatternTest
-(id) init
{
if( (self=[super init]))
{
CCNode *grid = [self generateArrowPatternWithBaseRowOfNumSprites:5 spacedApart:ccp(25.0f, 25.0f)];
[grid setPosition:ccp(50.0f,50.0f)];
[self addChild:grid];
}
return self;
}
-(CCNode *) generateArrowPatternWithBaseRowOfNumSprites:(float) numSprites spacedApart:(CGPoint) space
{
CCNode* patternNode = [CCNode node];
CGPoint tempPos = ccp(0.0f, 0.0f);
float offset = 0.0f;
while (numSprites > 0)
{
for(int x=numSprites;x>0;x--)
{
CCSprite *patternSprite = [CCSprite spriteWithFile:#"Icon.png"];
[patternSprite setScale: 0.3f];
[patternSprite setPosition: tempPos];
[patternNode addChild:patternSprite];
tempPos = ccpAdd(tempPos, ccp(0.0f,space.y));
}
tempPos = ccp(tempPos.x, 0.0f);
offset = offset + (space.y / 2.0f);
tempPos = ccpAdd(tempPos, ccp(space.x, offset));
numSprites -= 1;
}
return patternNode;
}
#end
I'm new to programming and objective-c (so i could be way off here) and i'm working my way through programming in objective c 4th edition text book and have become stuck on one of the exercises.
Can anyone tell what's wrong with the method below? In my program there is a rectangle class which has methods to set it's width, height and it's origin (from a class called XYPoint).
The containsPoint method checks to see if a rectangle's origin is within another rectangle. When i test this method it always returns 'No' even when the rectangle does contain the point.
The intersects method takes a rectangle as an argument (aRect) and uses the containsPoint method in an if statement to check if it intersects with the reciever and if it does, return a rectangle with an origin at the intersect and the correct width and height.
-(BOOL) containsPoint:(XYPoint *) aPoint
{
//create two variables to be used within the method
float upperX, upperY;
//assign them values, add the height and width to the origin values to range which the XYPoint must fall into
upperX = origin.x + height;
upperY = origin.y + width;
//if the value of aPoint's x and y points fall between the object's origin and the upperX or upperY values then the rectangle must contain the XYPoint and a message is sent to NSLog
if ((aPoint.x >= origin.x) && (aPoint.x <= upperX) && (aPoint.y >= origin.y) && (aPoint.y <= upperY) )
{
NSLog(#"Contains point");
return YES;
}
else
{
NSLog(#"Does not contain point");
return NO;
}
}
-(Rectangle *) intersects: (Rectangle *) aRect
{
//create new variables, Rectangle and XYPoint objects to use within the method
Rectangle *intersectRect = [[Rectangle alloc] init];
XYPoint *aRectOrigin = [[XYPoint alloc] init];
float wi, he; //create some variables
if ([self containsPoint:aRect.origin]) { //send the containsPoint method to self to test if the intersect
[aRectOrigin setX:aRect.origin.x andY:origin.y]; //set the origin for the new intersecting rectangle
[intersectRect setOrigin:aRectOrigin];
wi = (origin.x + width) - aRect.origin.x; //determine the width of the intersecting rectangle
he = (origin.y + height) - aRect.origin.y; //determine the height of the intersecting rectangle
[intersectRect setWidth:wi andHeight:he]; //set the rectangle's width and height
NSLog(#"The shapes intersect");
return intersectRect;
}
//if the test returned NO then send back these values
else {
[intersectRect setWidth:0. andHeight:0.];
[aRectOrigin setX:0. andY:0.];
[intersectRect setOrigin:aRectOrigin];
NSLog(#"The shapes do not intersect");
return intersectRect;
}
}
when i test with the following code
int main (int argc, char * argv [])
{
#autoreleasepool {
Rectangle *aRectangle = [[Rectangle alloc] init];
Rectangle *bRectangle = [[Rectangle alloc] init];
Rectangle *intersectRectangle = [[Rectangle alloc] init];
XYPoint *aPoint = [[XYPoint alloc] init];
XYPoint *bPoint = [[XYPoint alloc] init];
[aPoint setX:200.0 andY:420.00];
[bPoint setX:400.0 andY:300.0];
[aRectangle setWidth:250.00 andHeight:75.00];
[aRectangle setOrigin:aPoint];
[bRectangle setWidth:100.00 andHeight:180.00];
[bRectangle setOrigin:bPoint];
printf("Are the points within the rectangle's borders? ");
[aRectangle containsPoint: bPoint] ? printf("YES\n") : printf("NO\n");
intersectRectangle = [aRectangle intersects:bRectangle];
}
return 0;
}
i get the following output
Contains point
The origin is at 0.000000,0.000000, the width is 250.000000 and the height is 75.000000
Here is the code (I'm currently using "Programming in Objective-C 5th edition by Stephen Kochan) and judging by your variables and syntax, it looks like you are too.
// Use Floats for XYPoint and Rectangular exercise 8.4 on pg. 166
import "Rectangle.h"
import "XYPoint.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Rectangle *myRect = [[Rectangle alloc] init];
Rectangle *yourRect = [[Rectangle alloc] init];
XYPoint *myPoint = [[XYPoint alloc] init];
XYPoint *yourPoint = [[XYPoint alloc] init];
// For first Rectangle set w, h, origin
[myRect setWidth:250 andHeight:75];
[myPoint setX:200 andY:420];
myRect.origin = myPoint;
// Second Rectangle
[yourRect setWidth:100 andHeight:180];
[yourPoint setX:400 andY:300];
yourRect.origin = yourPoint;
// Find Points of intersection
float x1, x2, y1, y2;
x1 = MAX(myRect.origin.x, yourRect.origin.x);
x2 = MIN(myRect.origin.x + myRect.width, yourRect.origin.x + yourRect.width);
y1 = MAX(myRect.origin.y, yourRect.origin.y);
y2 = MIN(myRect.origin.y + myRect.height, yourRect.origin.y + yourRect.height);
// Make Intersecting Rectangle
Rectangle *intrRect = [[Rectangle alloc] init];
[intrRect setWidth: abs(x2 - x1) andHeight: abs(y2 - y1)];
// Print Intersecting Rectangle's infor for a check
NSLog(#"Width = %g, Height = %g, Bottom point = (%g, %g), Top point = (%g, %g)", intrRect.width, intrRect.height, x1, y1, x2, y2);
}
return 0;
}
Why not just use the included functions for determining intersection and/or containment:
if (CGRectContainsPoint(CGRect rect, CGPoint point))
{
// Contains point...
}
or
if (CGRectIntersectsRect(CGRect rectOne, CGRect rectTwo))
{
// Rects intersect...
}
or
if (CGRectContainsRect(CGRect rectOne, CGRect rectTwo))
{
// RectOne contains rectTwo...
}
You have made a number of errors, both in geometry and Objective-C, but this is how you learn!
Geometry: for two rectangles to intersect the origin of one does not need to be within the area of the other. Your two rectangles intersect but the intersection does not contain either origin. So your containsPoint: will return NO.
You only allocate objects if you create them, not if code which you call creates and returns them. So your allocation in Rectangle *intersectRectangle = [[Rectangle alloc] init]; creates an object which is then discarded by your assignment intersectRectangle = [aRectangle intersects:bRectangle];
For simple classes such as these it is usual to create init methods which set the properties, as creating an object without setting the properties is not useful. E.g. for your XYPoint class you would normally have an initialisation method - (id) initWithX:(float)x andY:(float)y;. You then use [[XYPoint alloc] initWithX:200.0 andY:420.0]] to create and initialise in one go.
As you are experimenting this is not important, but in real code for very simple and small classes which have value semantics - which means they behave much like integers and floats - it is common to use structures (struct) and functions and not objects and methods. Look up the definitions of NSRect and NSPoint as examples.
HTH
//intersect function
-(Rectangle *)intersect:(Rectangle *)secondRec
{
int intersectRectWidth;
int intersectRectHeight;
int intersectRectOriginX;
int intersectRectOriginY;
Rectangle * intersectRec =[[Rectangle alloc]init];
intersectRectOriginX = MAX(self.origin.x,secondRec.origin.x);
intersectRectOriginY = MAX((self.origin.y) , (secondRec.origin.y));
int myWidth=self.width+self.origin.x;
int secondRecWidth=secondRec.width+secondRec.origin.x;
int tempWidth=secondRecWidth - myWidth;
if (tempWidth > 0) {
intersectRectWidth=secondRec.width - tempWidth;
}
else
{
tempWidth=abs(tempWidth);
intersectRectWidth = self.width - tempWidth;
}
//height
int myHeight=self.height+self.origin.y;
int secondRecHeight=secondRec.height +secondRec.origin.y;
int tempHeight=secondRecHeight - myHeight;
if (tempHeight > 0) {
intersectRectHeight=secondRec.height - tempHeight;
}
else
{
tempHeight=abs(tempHeight);
intersectRectHeight = self.height - tempHeight;
}
intersectRec.width=intersectRectWidth;
intersectRec.height=intersectRectHeight;
XYPoint * intersectOrigin =[[XYPoint alloc]init];
[intersectOrigin setX:intersectRectOriginX andY:intersectRectOriginY];
[intersectRec setOrigin:intersectOrigin];
return intersectRec;
}
//main
XYPoint * org1=[[XYPoint alloc]init];
XYPoint * org2=[[XYPoint alloc]init];
Rectangle * firstRec=[[Rectangle alloc]init];
Rectangle * secondRec=[[Rectangle alloc]init];
Rectangle * intersectRec=[[Rectangle alloc]init];
[org1 setX:400 andY:300];
[org2 setX:200 andY:420];
[firstRec setOrigin:org1];
[secondRec setOrigin:org2];
[firstRec setWidth:100 andHeight:180];
[secondRec setWidth:250 andHeight:75];
intersectRec=[firstRec intersect:secondRec];
NSLog(#"intersect rectangle origin.x= %i origin.y=%i width=%i height=%i",intersectRec.origin.x,intersectRec.origin.y,intersectRec.width,intersectRec.height);
I have a MKMapView (obviously), that shows housing locations around the user.
I have a Radius tool that when a selection is made, the annotations should add/remove based on distance around the user.
I have it add/removing fine but for some reason the annotations won't show up until I zoom in or out.
This is the method that adds/removes the annotations based on distance. I have tried two different variations of the method.
Adds the new annotations to an array, then adds to the map by [mapView addAnnotations:NSArray].
Add the annotations as it finds them using [mapView addAnnotation:MKMapAnnotation];
1.
- (void)updateBasedDistance:(NSNumber *)distance {
//Setup increment for HUD animation loading
float hudIncrement = ( 1.0f / [[[[self appDelegate] rssParser]rssItems] count]);
//Remove all the current annotations from the map
[self._mapView removeAnnotations:self._mapView.annotations];
//Hold all the new annotations to add to map
NSMutableArray *tempAnnotations;
/*
I have an array that holds all the annotations on the map becuase
a lot of filtering/searching happens. So for memory reasons it is
more efficient to load annoations once then add/remove as needed.
*/
for (int i = 0; i < [annotations count]; i++) {
//Current annotations location
CLLocation *tempLoc = [[CLLocation alloc] initWithLatitude:[[annotations objectAtIndex:i] coordinate].latitude longitude:[[annotations objectAtIndex:i] coordinate].longitude];
//Distance of current annotaiton from user location converted to miles
CLLocationDistance miles = [self._mapView.userLocation.location distanceFromLocation:tempLoc] * 0.000621371192;
//If distance is less than user selection, add it to the map.
if (miles <= [distance floatValue]){
if (tempAnnotations == nil)
tempAnnotations = [[NSMutableArray alloc] init];
[tempAnnotations addObject:[annotations objectAtIndex:i]];
}
//For some reason, even with ARC, helps a little with memory consumption
tempLoc = nil;
//Update a progress HUD I use.
HUD.progress += hudIncrement;
}
//Add the new annotaitons to the map
if (tempAnnotations != nil)
[self._mapView addAnnotations:tempAnnotations];
}
2.
- (void)updateBasedDistance:(NSNumber *)distance {
//Setup increment for HUD animation loading
float hudIncrement = ( 1.0f / [[[[self appDelegate] rssParser]rssItems] count]);
//Remove all the current annotations from the map
[self._mapView removeAnnotations:self._mapView.annotations];
/*
I have an array that holds all the annotations on the map becuase
a lot of filtering/searching happens. So for memory reasons it is
more efficient to load annoations once then add/remove as needed.
*/
for (int i = 0; i < [annotations count]; i++) {
//Current annotations location
CLLocation *tempLoc = [[CLLocation alloc] initWithLatitude:[[annotations objectAtIndex:i] coordinate].latitude longitude:[[annotations objectAtIndex:i] coordinate].longitude];
//Distance of current annotaiton from user location converted to miles
CLLocationDistance miles = [self._mapView.userLocation.location distanceFromLocation:tempLoc] * 0.000621371192;
//If distance is less than user selection, add it to the map.
if (miles <= [distance floatValue])
[self._mapView addAnnotation:[annotations objectAtIndex:i]];
//For some reason, even with ARC, helps a little with memory consumption
tempLoc = nil;
//Update a progress HUD I use.
HUD.progress += hudIncrement;
}
}
I have also attempted at the end of the above method:
[self._mapView setNeedsDisplay];
[self._mapView setNeedsLayout];
Also, to force a refresh (saw somewhere it might work):
self._mapView.showsUserLocation = NO;
self._mapView.showsUserLocation = YES;
Any help would be very much appreciated and as always, thank you for taking the time to read.
I'm going to guess that updateBasedDistance: gets called from a background thread. Check with NSLog(#"Am I in the UI thread? %d", [NSThread isMainThread]);. If it's 0, then you should move the removeAnnotations: and addAnnotation: to a performSelectorOnMainThread: invocation, or with GCD blocks on the main thread.
I have an interface that has large numbers of controls, see image below.
Interface http://www.richardstelling.com/hosted/cocoainterface.png
What is the best way to access these, creating 288 IBOutlets in my AppController class and linking them all up seems inefficient.
I looked at forms, but they seemed to simplistic.
This is a proof-of-concept and will not ship so I'm open to any ideas. One caveat however I have to use Objective-C as the final product will be written in Objective-C/Cocoa.
NB:
The interface is static
Smaller boxed will hold integers (0-255)
You should look into NSMatrix. This is exactly what it's designed to solve.
NSTableView looks like the UI you need. The visual rendering will be a bit different but it will look more 'Mac'.
Either NSMatrix, as Rob suggests, or re-think the UI so you have fewer controls on it :-)
You could build the entire interface programmatically, with a few lines of code in a loop:
const int numRows = 11;
const int rowWidth = 400;
const int rowHeight = 20;
const int itemSpacing = 5;
const int nameFieldWidth = 120;
const int smallFieldWidth = 30;
NSMutableArray * rowList = [[NSMutableArray alloc] initWithCapacity:numRows];
int rowIndex;
NSRect rowFrame = [controlView bounds];
rowFrame.origin.y = rowFrame.size.height - rowHeight;
rowFrame.size.height = rowHeight;
NSRect itemRect
for (rowIndex = 0; rowIndex < 11; rowIndex++)
{
// create a new controller for the current row
MyRowController * rowController = [[MyRowController alloc] init];
[rowList addObject:rowController];
[rowController release];
// create and link the checkbox
itemRect = rowFrame;
itemRect.size.width = 20;
NSButton * checkBox = [[NSButton alloc] initWithFrame:itemRect];
[controlView addSubview:checkBox];
[rowController setCheckBox:checkBox];
[checkBox release];
// create and link the name field
itemRect.origin.x += itemRect.size.width + itemSpacing;
itemRect.size.width = nameFieldWidth;
NSTextField * nameField = [[NSTextField alloc] initWithFrame:itemRect];
[controlView addSubview:nameField];
[rowController setNameField:nameField];
[nameField release];
// create and link the smaller fields
itemRect.origin.x += itemRect.size.width + itemSpacing;
itemRect.size.width = smallFieldWidth;
NSTextField * smallField_1 = [[NSTextField alloc] initWithFrame:itemRect];
[controlView addSubview:smallField_1];
[rowController setSmallField_1:smallField_1];
[smallField_1 release];
//.. continue for each item in a row ..
// increment the main rectangle for the next loop
rowFrame.origin.y -= (rowFrame.size.height + itemSpacing);
}