the typical answer will be "you can convert it to nsvalue and then use [element CGPointValue];
but in my case i need to generate array of type CGPoint , as i need it in a built in function below :
static CGPathRef createClosedPathWithPoints(const CGPoint *points, size_t count) {
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddLines(path, NULL, points, count);
CGPathCloseSubpath(path);
return path;
}
so , i need to pass the exact datatype , as i can't parse it element by element , or i need a way to everytime the user make a specific action , i add it's CGPoint to array of CGPoints :((
thanks in advance
edit :
i have tried malloc and making c array but the result of the function was not desirable , i tested and made infinity for loop to that malloc array , and it's too large not just like the size i sat , and contain garbage so the result went wrong
this is the mutable array
pointToPoints = [NSMutableArray new];
[pointToPoints addObject:[NSValue valueWithCGPoint:tempPoint] ];
Here is something that will create a closed path from an NSArray of NSValue created with CGPoint using the code your provided:
BOOL isCGPoint(NSValue *value){
return value && strcmp([value objCType], #encode(CGPoint)) == 0;
}
- (CGPathRef) closedPathFromPointArray:(NSArray *)points{
CGMutablePathRef path = CGPathCreateMutable();
if(points.count){
CGPoint origin = ((NSValue *)points[0]).CGPointValue;
CGPathMoveToPoint (path, NULL, origin.x, origin.y);
// see https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CGPath/#//apple_ref/c/func/CGPathAddLines
for(NSValue *value in points){
CGPathAddLineToPoint (path, NULL, value.CGPointValue.x, value.CGPointValue.y);
}
}
CGPathCloseSubpath(path);
return path;
}
As you see, you don't really need malloc, or even creating a C array of CGPoint. This assumes you only need this array for creating the closed path.
Two extra things of note:
See the commented link for CGPathAddLines, as it describes how CGPathAddLines works internally. This gives you the hint about how to go about this.
The isCGPoint function is included so you can test if a given NSValue instance was actually created using [NSValue valueWithCGPoint:]. My previous answer checked this, but I thought it was ooverkill to check everywhere. In any case, it's included here for didactic purposes.
CGPoint *points = malloc(sizeof(CGPoint) * mutableArrayOfPoints.count);
for (int i = 0; i < mutableArrayOfPoints.count; i++) {
points[i] = [mutableArrayOfPoints[i] pointValue];
}
The above is from memory. I haven't used malloc() in ages, so you may need to adjust syntax.
Related
I have a C type pointer variable:
a_c_type *cTypePointer = [self getCTypeValue];
How can I convert cTypePointer to NSObject type & vise versa?
Should I use NSValue? What is the proper way to do so with NSValue?
You can indeed use a NSValue.
a_c_type *cTypePointer = [self getCTypeValue];
NSValue * storableunit = [NSValue valueWithBytes:cTypePointer objCType:#encode(a_c_type)];
note that the 1st parameter is a pointer (void*). the object will contain the pointed value.
to get back to C:
a_c_type element;
[value getValue:&element];
Note that you would get the actual value, not the pointer. But then, you can just
a_c_type *cTypePointer = &element
Test it :
- (void) testCVal
{
double save = 5.2;
NSValue * storageObjC = [NSValue valueWithBytes:&save objCType:#encode(double)];
double restore;
[storageObjC getValue:&restore];
XCTAssert(restore == save, #"restore should be equal to the saved value");
}
test with ptr :
typedef struct
{
NSInteger day;
NSInteger month;
NSInteger year;
} CDate;
- (void) testCVal
{
CDate save = (CDate){8, 10, 2016};
CDate* savePtr = &save;
NSValue * storageObjC = [NSValue valueWithBytes:savePtr objCType:#encode(CDate)];
CDate restore;
[storageObjC getValue:&restore];
CDate* restorePtr = &restore;
XCTAssert(restorePtr->day == savePtr->day && restorePtr->month == savePtr->month && restorePtr->year == savePtr->year, #"restore should be equal to the saved value");
}
You simply use the method valueWithPointer: to wrap a pointer value as an NSValue object, and pointerValue to extract the pointer value.
These are just like valueWithInt:/intValue et al - they wrap the primitive value. You are not wrapping what the pointer points at. Therefore it is important that you ensure that when extract the pointer that whatever it pointed at is still around, or else the pointer value will be invalid.
Finally you must cast the extract pointer value, which is returned as a void *, back to be its original type, e.g. a_c_type * in your example.
(If you want to wrap what is being pointed at consider NSData.)
HTH
I have a piece of code which orders an NSMutableArray of points, as follows:
[points sortUsingComparator:^NSComparisonResult (id firstObject, id secondObject)
{
CGPoint firstPoint = [firstObject CGPointValue];
CGPoint secondPoint = [secondObject CGPointValue];
return firstPoint.y>secondPoint.y;
}];
This works perfectly well in my first project. I have then tried to use this in another project, where I have basically copied my entire class across (for the purpose of splitting out into separate demo projects). In the second project, Xcode fails to build with the error:
Cannot initialize return object of type 'NSComparisonResult' with an
rvalue of type 'bool'.
Bizarrely, it will compile if I put the code in a different class within the new project, but never within my original class, 'Classname.mm'. The .mm is the same as in the original project, and all of the same headers and variables are included.
Both projects are compiled for iOS 7.0, on Xcode 5.0.1.
Does anyone have any idea why this would be happening in one class only in my new project?
Thanks
The block needs to return a value of type NSComparisonResult. You are not doing that.
Try:
[points sortUsingComparator:^NSComparisonResult (id firstObject, id secondObject)
{
CGPoint firstPoint = [firstObject CGPointValue];
CGPoint secondPoint = [secondObject CGPointValue];
if (firstPoint.y > secondPoint.y) {
return NSOrderedDescending;
} else if (firstPoint.y < secondPoint.y) {
return NSOrderedAscending;
} else {
return NSOrderedSame;
}
}];
I might have the "Ascending/Descending" values backward. If you get the results in the reverse order, swap those two return values.
I have to release CGMutablePathRef before returning it else the application is crashes. Where should I be releasing it?
I have tried before returning but its still crashing...
+(UIBezierPath*) smoothedBezierOutlinePathWithCGPath:(CGPathRef)path tension:(CGFloat) tension
{
CGMutablePathRef smoothedCGPath = CreateSmoothedBezierPathFromPath(path, tension);
UIBezierPath* smoothedBezierPath = [UIBezierPath bezierPathWithCGPath:smoothedCGPath];
CGPathRelease(smoothedCGPath);
return smoothedBezierPath;
}
CGMutablePathRef CreateSmoothedBezierPathFromPath( CGPathRef path, CGFloat tention)
{
//convert path to 2 arrays of CGFloats
struct dataPointer my_dataPointer; //this struct will hold the 2 indexes of the CGpath
my_dataPointer.numberOfPoints = 0;
my_dataPointer.isClosed = NO;
CGPathApply(path, &my_dataPointer, savePathToArraysApplierFunc);
//the arrays where the control points will be stored
CGFloat controlPoints_x[2*_max_points];
CGFloat controlPoints_y[2*_max_points];
calculateBezierControlPoints( controlPoints_x, controlPoints_y, my_dataPointer.indexx , my_dataPointer.indexy, my_dataPointer.isClosed, my_dataPointer.numberOfPoints, tention);
//the final CGpath constisting of original points and computed control points
CGMutablePathRef bezierPath = CGPathCreateMutable();
for (int i = 0; i<my_dataPointer.numberOfPoints + (int) my_dataPointer.isClosed; i++)
{
if(i==0)
CGPathMoveToPoint(bezierPath, nil, my_dataPointer.indexx[0], my_dataPointer.indexy[0]);
else if (i==my_dataPointer.numberOfPoints) //this happens when the path is closed
CGPathAddCurveToPoint(bezierPath, nil, controlPoints_x[i*2-2], controlPoints_y[i*2-2], controlPoints_x[i*2-1], controlPoints_y[i*2-1],my_dataPointer.indexx[0], my_dataPointer.indexy[0]);
else
CGPathAddCurveToPoint(bezierPath, nil, controlPoints_x[i*2-2], controlPoints_y[i*2-2], controlPoints_x[i*2-1], controlPoints_y[i*2-1],my_dataPointer.indexx[i], my_dataPointer.indexy[i]);
}
if(my_dataPointer.isClosed)
CGPathCloseSubpath(bezierPath);
//app crashes here before returning.
return bezierPath;
}
Where am I suppose to release it? I have tried releasing it declaring it globally too but no use. Please can anyone help me?
I am trying to store a c-float array in an NSDictionary to use later.
I was initially using an NSArray to store the C-data but NSArray is to slow for my intentions.
I am using the following code to wrap the arrays in the NSDictionary:
[self.m_morphPositions setObject:[NSValue valueWithBytes:&positionBuff objCType:#encode(float[(self.m_countVertices * 3)])] forKey:fourCC];
And retrieving the C-Float array using:
float posMorphs[(self.m_countVertices*3)];
NSValue *posValues = [self.m_morphPositions objectForKey:name];
[posValues getValue:&posMorphs];
When I retireve the array, the values are all set to 0.0 for each index which is wrong.
How can I fix this?
I also think that NSData is probably the best solution here. But just if anybody is interested: You cannot use #encode with a variable sized array, because it is a compiler directive. Therefore
#encode(float[(self.m_countVertices * 3)])
cannot work. Actually the compiler creates the encoding for a float array of size zero here, which explains why you get nothing back when reading the NSValue.
But you can create the type encoding string at runtime. For a float array,
the encoding is [<count>^f] (see Type Encodings), so the following would work:
const char *enc = [[NSString stringWithFormat:#"[%d^f]", (self.m_countVertices * 3)] UTF8String];
NSValue *val = [NSValue valueWithBytes:positionBuff objCType:enc];
NSValue is probably intented for scalar values, not arrays for them. In this case, using NSData should be much easier
NSData* data = [NSData dataWithBytes:&positionBuff length:(self.m_countVertices * 3 * sizeof(float))];
[data getBytes:posMorphs length:(self.m_countVertices * 3 * sizeof(float))];
Another solution is to allocate the array on the heap and use NSValue to store the pointer.
You can feed the bytes into an NSDictionary if they're wrapped in an NSData.
If you want to skip that, you would use an NSMapTable and NSPointerFunctionsOpaqueMemory (or MallocMemory) for the value pointer functions.
I'm not sure if it is the way you are encoding your value, but it might help to encapsulate your array into a struct.
// Put this typedef in a header
typedef struct
{
float values[3];
} PosValues;
In your code:
// store to NSValue
PosValues p1 = { { 1.0, 2.0, 3.0 } };
NSValue *val = [NSValue valueWithBytes:&p1 objCType:#encode(PosValues)];
// retrieve from NSValue
PosValues p2;
[val getValue:&p2];
NSLog(#"%f, %f, %f", p2.values[0], p2.values[1]. p2.values[2]);
The benefit to this approach is that your array is kept as an array type. Also, these structures are assignable even though raw arrays are not:
PosValues p1 = { { 1.0, 2.0, 3.0 } };
PosValues p2;
p2 = p1;
I have a few maps (tilemaps made with Tiled QT) and I would like to create a CGpoint **array based on the objects groups of those maps (I call them Waypoints).
Each maps can have a few set of waypoints that I call path.
//Create the first dimension
int nbrOfPaths = [[self.tileMap objectGroups] count];
CGPoint **pathArray = malloc(nbrOfPaths * sizeof(CGPoint *));
Then for the second dimension
//Create the second dimension
int pathCounter = 0;
while ((path = [self.tileMap objectGroupNamed:[NSString stringWithFormat:#"Path%d", pathCounter]])) {
int nbrOfWpts = 0;
while ((waypoint = [path objectNamed:[NSString stringWithFormat:#"Wpt%d", nbrOfWpts]])) {
nbrOfWpts++;
}
pathArray[pathCounter] = malloc(nbrOfWpts * sizeof(CGPoint));
pathCounter++;
}
Now I want to fill up the pathArray
//Fill the array
pathCounter = 0;
while ((path = [self.tileMap objectGroupNamed:[NSString stringWithFormat:#"Path%d", pathCounter]]))
{
int waypointCounter = 0;
//Get all the waypoints from the path
while ((waypoint = [path objectNamed:[NSString stringWithFormat:#"Wpt%d", waypointCounter]]))
{
pathArray[pathCounter][waypointCounter].x = [[waypoint valueForKey:#"x"] intValue];
pathArray[pathCounter][waypointCounter].y = [[waypoint valueForKey:#"y"] intValue];
NSLog(#"x : %f & y : %f",pathArray[pathCounter][waypointCounter].x,pathArray[pathCounter][waypointCounter].y);
waypointCounter++;
}
pathCounter++;
}
When I NSLog(#"%#",pathArray), it shows me the entire pathArray will the x and y.
HOWEVER 2 problems :
The y value is never correct (the x value is correct and my tilemap.tmx is correct too)
<object name="Wpt0" x="-18" y="304"/> <-- I get x : -18 and y :336 with NSLog
<object name="Wpt1" x="111" y="304"/> <-- I get x : 111 and y :336
<object name="Wpt2" x="112" y="207"/> <-- I get x : 112 and y :433
I get a EX_BAD_ACCESS at the end of the NSLog
EDIT
Thank you about the NSLog(%#) concerning CGPoint.
However, I get the y value with this line (in the ugly loop):
NSLog(#"x : %f & y : %f",pathArray[pathCounter][waypointCounter].x,pathArray[pathCounter][waypointCounter].y);
First of all, you can't NSLog CGPoint like that because it is not an object. %# expects an objective c object to send a description message to.
Secondly, you can use an NSValue wrapper and then use NSMutableArray as you would with any other object. Is there a reason you don't want to do that? You can add arrays inside other arrays as you are doing.
Regarding the first problem:
The y value is never correct (the x value is correct and my tilemap.tmx is correct too)
Have you noticed that if you add the y values from tile map and from NSLog they always add up to 640? Then you better check if tilemap y-coordinate is top-to-bottom as oppose to CGPoint's bottom-to-top. Then you can always do 640 - y to convert the y-coordinate between the two coordinate systems.