Line data Source code
1 0 : /* 2 : 3 : OOJoystickProfile.m 4 : 5 : Oolite 6 : Copyright (C) 2004-2013 Giles C Williams and contributors 7 : 8 : This program is free software; you can redistribute it and/or 9 : modify it under the terms of the GNU General Public License 10 : as published by the Free Software Foundation; either version 2 11 : of the License, or (at your option) any later version. 12 : 13 : This program is distributed in the hope that it will be useful, 14 : but WITHOUT ANY WARRANTY; without even the implied warranty of 15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 : GNU General Public License for more details. 17 : 18 : You should have received a copy of the GNU General Public License 19 : along with this program; if not, write to the Free Software 20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 : MA 02110-1301, USA. 22 : 23 : */ 24 : 25 : #import "OOJoystickManager.h" 26 : #import "OOJoystickProfile.h" 27 : #import "OOMaths.h" 28 : #import "OOLoggingExtended.h" 29 : #import "Universe.h" 30 : 31 0 : #define SPLINE_POINT_MIN_SPACING 0.02 32 : 33 0 : @interface OOJoystickSplineSegment: NSObject <NSCopying> 34 : { 35 : @private 36 : double start; 37 0 : double end; 38 0 : double a[4]; 39 0 : } 40 : 41 : - (id) init; 42 0 : 43 : // Linear spline from left point to right point. Returns nil if right.x - left.x <= 0.0. 44 : - (id) initWithData: (NSPoint) left right: (NSPoint) right; 45 0 : 46 : // Quadratic spline from left point to right point, with gradient specified at left. returns nil if right.x - left.x <= 0.0. 47 : - (id) initWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft; 48 0 : 49 : // Quadratic spline from left point to right point, with gradient specified at right. returns nil if right.x - left.x <= 0.0. 50 : - (id) initWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright; 51 0 : 52 : // Cubic spline from left point to right point, with gradients specified at end points. returns nil if right.x - left.x <= 0.0. 53 : - (id) initWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright; 54 0 : 55 : // Linear spline from left point to right point. Returns nil if right.x - left.x <= 0.0. 56 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right; 57 0 : 58 : // Quadratic spline from left point to right point, with gradient specified at left. returns nil if right.x - left.x <= 0.0. 59 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft; 60 0 : 61 : // Quadratic spline from left point to right point, with gradient specified at right. returns nil if right.x - left.x <= 0.0. 62 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright; 63 0 : 64 : // Cubic spline from left point to right point, with gradients specified at end points. returns nil if right.x - left.x <= 0.0. 65 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright; 66 0 : 67 : - (id) copyWithZone: (NSZone *) zone; 68 0 : - (double) start; 69 0 : - (double) end; 70 0 : - (double) value: (double) t; 71 0 : - (double) gradient: (double) t; 72 0 : 73 : @end 74 : 75 : @interface OOJoystickSplineAxisProfile (Private) 76 : 77 : // Create the segments from the control points. If there's a problem, e.g. control points not in order or overlapping, 78 : // leave segments as they are and return NO. Otherwise return YES. 79 : - (BOOL) makeSegments; 80 0 : 81 : @end 82 : 83 : 84 : @implementation OOJoystickAxisProfile 85 : 86 : - (id) init 87 : { 88 : if ((self = [super init])) 89 : { 90 : deadzone = STICK_DEADZONE; 91 : } 92 : return self; 93 : } 94 : 95 : - (id) copyWithZone: (NSZone *) zone 96 : { 97 : OOJoystickAxisProfile *copy = [[[self class] alloc] init]; 98 : return copy; 99 : } 100 : 101 : 102 : - (double) rawValue: (double) x 103 : { 104 : return x; 105 : } 106 : 107 : - (double) value: (double) x 108 : { 109 : if (fabs(x) < deadzone) 110 : { 111 : return 0.0; 112 : } 113 : return x < 0 ? -[self rawValue: (-x-deadzone)/(1.0-deadzone)] : [self rawValue: (x-deadzone)/(1.0-deadzone)]; 114 : } 115 : 116 : - (double) deadzone 117 : { 118 : return deadzone; 119 : } 120 : 121 : - (void) setDeadzone: (double) newValue 122 : { 123 : deadzone = OOClamp_0_max_d(newValue, STICK_MAX_DEADZONE); 124 : } 125 : 126 : @end 127 : 128 : @implementation OOJoystickStandardAxisProfile 129 : 130 : - (id) init 131 : { 132 : if ((self = [super init])) 133 : { 134 : power = 1.0; 135 : parameter = 1.0; 136 : } 137 : return self; 138 : } 139 : 140 : - (id) copyWithZone: (NSZone *) zone 141 : { 142 : OOJoystickStandardAxisProfile *copy = [[[self class] alloc] init]; 143 : copy->power = power; 144 : copy->parameter = parameter; 145 : return copy; 146 : } 147 : 148 : - (void) setPower: (double) newValue 149 : { 150 : if (newValue < 1.0) 151 : { 152 : power = 1.0; 153 : } 154 : else if (newValue > STICKPROFILE_MAX_POWER) 155 : { 156 : power = STICKPROFILE_MAX_POWER; 157 : } 158 : else 159 : { 160 : power = newValue; 161 : } 162 : return; 163 : } 164 : 165 : - (double) power 166 : { 167 : return power; 168 : } 169 : 170 : 171 : - (void) setParameter: (double) newValue 172 : { 173 : parameter = OOClamp_0_1_d(newValue); 174 : return; 175 : } 176 : 177 : - (double) parameter 178 : { 179 : return parameter; 180 : } 181 : 182 : 183 : - (double) rawValue: (double) x 184 : { 185 : if (x < 0) 186 : { 187 : return -OOClamp_0_1_d(parameter * pow(-x,power)-(parameter - 1.0)*(-x)); 188 : } 189 : return OOClamp_0_1_d(parameter * pow(x,power)-(parameter - 1.0)*(x)); 190 : } 191 : 192 : @end 193 : 194 : @implementation OOJoystickSplineSegment 195 : 196 : - (id) init 197 : { 198 : if ((self = [super init])) 199 : { 200 : start = 0.0; 201 : end = 1.0; 202 : a[0] = 0.0; 203 : a[1] = 1.0; 204 : a[2] = 0.0; 205 : a[3] = 0.0; 206 : } 207 : return self; 208 : } 209 : 210 : - (id) copyWithZone: (NSZone *) zone 211 : { 212 : OOJoystickSplineSegment *copy = [[OOJoystickSplineSegment allocWithZone: zone] init]; 213 : copy->start = start; 214 : copy->end = end; 215 : copy->a[0] = a[0]; 216 : copy->a[1] = a[1]; 217 : copy->a[2] = a[2]; 218 : copy->a[3] = a[3]; 219 : return copy; 220 : } 221 : 222 : - (id) initWithData: (NSPoint) left right: (NSPoint) right 223 : { 224 : double dx = right.x - left.x; 225 : if (dx <= 0.0) 226 : { 227 : return nil; 228 : } 229 : if ((self = [super init])) 230 : { 231 : start = left.x; 232 : end = right.x; 233 : a[1] = (right.y - left.y)/dx; 234 : a[0] = left.y-a[1]*left.x; 235 : a[2] = 0.0; 236 : a[3] = 0.0; 237 : } 238 : return self; 239 : } 240 : 241 : - (id) initWithData:(NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft 242 : { 243 : double dx = right.x - left.x; 244 : if (dx <= 0.0) 245 : { 246 : return nil; 247 : } 248 : if ((self = [super init])) 249 : { 250 : start = left.x; 251 : end = right.x; 252 : a[0] = left.y*right.x*(right.x - 2*left.x)/(dx*dx) + right.y*left.x*left.x/(dx*dx) - gradientleft*left.x*right.x/dx; 253 : a[1] = 2*left.x*(left.y-right.y)/(dx*dx) + gradientleft*(left.x+right.x)/dx; 254 : a[2] = (right.y-left.y)/(dx*dx) - gradientleft/dx; 255 : } 256 : return self; 257 : } 258 : 259 : - (id) initWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright 260 : { 261 : double dx = right.x - left.x; 262 : if (dx <= 0.0) 263 : { 264 : return nil; 265 : } 266 : if ((self = [super init])) 267 : { 268 : start = left.x; 269 : end = right.x; 270 : a[0] = (left.y*right.x*right.x + right.y*left.x*(left.x-2*right.x))/(dx*dx) + gradientright*left.x*right.x/dx; 271 : a[1] = 2*right.x*(right.y-left.y)/(dx*dx) - gradientright*(left.x+right.x)/dx; 272 : a[2] = (left.y-right.y)/(dx*dx) + gradientright/dx; 273 : } 274 : return self; 275 : } 276 : 277 : - (id) initWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright 278 : { 279 : double dx = right.x - left.x; 280 : if (dx <= 0.0) 281 : { 282 : return nil; 283 : } 284 : if ((self = [super init])) 285 : { 286 : start = left.x; 287 : end = right.x; 288 : a[0] = (left.y*right.x*right.x*(right.x-3*left.x) - right.y*left.x*left.x*(left.x-3*right.x))/(dx*dx*dx) - (gradientleft*right.x + gradientright*left.x)*left.x*right.x/(dx*dx); 289 : a[1] = 6*left.x*right.x*(left.y-right.y)/(dx*dx*dx) + (gradientleft*right.x*(right.x+2*left.x) + gradientright*left.x*(left.x+2*right.x))/(dx*dx); 290 : a[2] = 3*(left.x+right.x)*(right.y-left.y)/(dx*dx*dx) - (gradientleft*(2*right.x+left.x)+gradientright*(2*left.x+right.x))/(dx*dx); 291 : a[3] = 2*(left.y-right.y)/(dx*dx*dx) + (gradientleft+gradientright)/(dx*dx); 292 : } 293 : return self; 294 : } 295 : 296 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right 297 : { 298 : OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right]; 299 : return [segment autorelease]; 300 : } 301 : 302 : 303 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft 304 : { 305 : OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right gradientleft:gradientleft]; 306 : return [segment autorelease]; 307 : } 308 : 309 : 310 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright 311 : { 312 : OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right gradientright:gradientright]; 313 : return [segment autorelease]; 314 : } 315 : 316 : 317 : + (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright 318 : { 319 : OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right gradientleft:gradientleft gradientright:gradientright]; 320 : return [segment autorelease]; 321 : } 322 : 323 : - (double) start 324 : { 325 : return start; 326 : } 327 : 328 : 329 : - (double) end 330 : { 331 : return end; 332 : } 333 : 334 : 335 : - (double) value: (double) x 336 : { 337 : return a[0] + (a[1] + (a[2] + a[3]*x)*x)*x; 338 : } 339 : 340 : - (double) gradient: (double) x 341 : { 342 : return a[1]+(2*a[2] + 3*a[3]*x)*x; 343 : } 344 : 345 : @end 346 : 347 : 348 : @implementation OOJoystickSplineAxisProfile 349 : 350 : - (id) init 351 : { 352 : if ((self = [super init])) 353 : { 354 : controlPoints = [[NSMutableArray alloc] initWithCapacity: 2]; 355 : segments = nil; 356 : [self makeSegments]; 357 : } 358 : return self; 359 : } 360 : 361 : - (void) dealloc 362 : { 363 : [controlPoints release]; 364 : [segments release]; 365 : [super dealloc]; 366 : return; 367 : } 368 : 369 : - (id) copyWithZone: (NSZone *) zone 370 : { 371 : OOJoystickSplineAxisProfile *copy = [[[self class] alloc] init]; 372 : copy->controlPoints = [controlPoints copyWithZone: zone]; 373 : copy->segments = [segments copyWithZone: zone]; 374 : return copy; 375 : } 376 : 377 : 378 : - (int) addControl: (NSPoint) point 379 : { 380 : NSPoint left, right; 381 : NSUInteger i; 382 : 383 : if (point.x <= SPLINE_POINT_MIN_SPACING || point.x >= 1 - SPLINE_POINT_MIN_SPACING ) 384 : { 385 : return -1; 386 : } 387 : 388 : left.x = 0.0; 389 : left.y = 0.0; 390 : for (i = 0; i <= [controlPoints count]; i++ ) 391 : { 392 : if (i < [controlPoints count]) 393 : { 394 : right = [[controlPoints objectAtIndex: i] pointValue]; 395 : } 396 : else 397 : { 398 : right = NSMakePoint(1.0,1.0); 399 : } 400 : if ((point.x - left.x) < SPLINE_POINT_MIN_SPACING) 401 : { 402 : if (i == 0) 403 : { 404 : return -1; 405 : } 406 : [controlPoints replaceObjectAtIndex: i - 1 withObject: [NSValue valueWithPoint: point]]; 407 : [self makeSegments]; 408 : return i - 1; 409 : } 410 : if ((right.x - point.x) >= SPLINE_POINT_MIN_SPACING) 411 : { 412 : [controlPoints insertObject: [NSValue valueWithPoint: point] atIndex: i]; 413 : [self makeSegments]; 414 : return i; 415 : } 416 : left = right; 417 : } 418 : return -1; 419 : } 420 : 421 : - (NSPoint) pointAtIndex: (NSInteger) index 422 : { 423 : NSPoint point; 424 : if (index < 0) 425 : { 426 : point.x = 0.0; 427 : point.y = 0.0; 428 : } 429 : else if (index >= (NSInteger)[controlPoints count]) 430 : { 431 : point.x = 1.0; 432 : point.y = 1.0; 433 : } 434 : else 435 : { 436 : point = [[controlPoints objectAtIndex: index] pointValue]; 437 : } 438 : return point; 439 : } 440 : 441 : - (int) countPoints 442 : { 443 : return [controlPoints count]; 444 : } 445 : 446 : 447 : - (NSArray *) controlPoints 448 : { 449 : return [NSArray arrayWithArray: controlPoints]; 450 : } 451 : 452 : // Calculate segments from control points 453 : - (BOOL) makeSegments 454 0 : { 455 : NSUInteger i; 456 : NSPoint left, right, next; 457 : double gradientleft, gradientright; 458 : OOJoystickSplineSegment* segment; 459 : BOOL first_segment = YES; 460 : NSMutableArray *new_segments = [NSMutableArray arrayWithCapacity: ([controlPoints count] + 1)]; 461 : 462 : left.x = 0.0; 463 : left.y = 0.0; 464 : if ([controlPoints count] == 0) 465 : { 466 : right.x = 1.0; 467 : right.y = 1.0; 468 : segment = [OOJoystickSplineSegment segmentWithData: left right: right]; 469 : [new_segments addObject:segment]; 470 : } 471 : else 472 : { 473 : gradientleft = 1.0; 474 : right = [[controlPoints objectAtIndex: 0] pointValue]; 475 : for (i = 0; i < [controlPoints count]; i++) 476 : { 477 : next = [self pointAtIndex: i + 1]; 478 : if (next.x - left.x > 0.0) 479 : { 480 : // we make the gradient at right equal to the gradient of a straight line between the neighcouring points 481 : gradientright = (next.y - left.y)/(next.x - left.x); 482 : if (first_segment) 483 : { 484 : segment = [OOJoystickSplineSegment segmentWithData: left right: right gradientright: gradientright]; 485 : } 486 : else 487 : { 488 : segment = [OOJoystickSplineSegment segmentWithData: left right: right gradientleft: gradientleft gradientright: gradientright]; 489 : } 490 : if (segment == nil) 491 : { 492 : return NO; 493 : } 494 : else 495 : { 496 : [new_segments addObject: segment]; 497 : gradientleft = gradientright; 498 : first_segment = NO; 499 : left = right; 500 : } 501 : } 502 : right = next; 503 : } 504 : right.x = 1.0; 505 : right.y = 1.0; 506 : segment = [OOJoystickSplineSegment segmentWithData: left right: right gradientleft: gradientleft]; 507 : if (segment == nil) 508 : { 509 : return NO; 510 : } 511 : [new_segments addObject: segment]; 512 : } 513 : [segments release]; 514 : segments = [[NSArray arrayWithArray: new_segments] retain]; 515 : return YES; 516 : } 517 : 518 : - (void) removeControl: (NSInteger) index 519 : { 520 : if (index >= 0 && index < (NSInteger)[controlPoints count]) 521 : { 522 : [controlPoints removeObjectAtIndex: index]; 523 : [self makeSegments]; 524 : } 525 : return; 526 : } 527 : 528 : - (void) clearControlPoints 529 : { 530 : [controlPoints removeAllObjects]; 531 : [self makeSegments]; 532 : } 533 : 534 : - (void) moveControl: (NSInteger) index point: (NSPoint) point 535 : { 536 : NSPoint left, right; 537 : 538 : point.x = OOClamp_0_1_d(point.x); 539 : point.y = OOClamp_0_1_d(point.y); 540 : if (index < 0 || index >= (NSInteger)[controlPoints count]) 541 : { 542 : return; 543 : } 544 : if (index == 0) 545 : { 546 : left.x = 0.0; 547 : right.x = 0.0; 548 : } 549 : else 550 : { 551 : left = [[controlPoints objectAtIndex: (index-1)] pointValue]; 552 : } 553 : if (index == (NSInteger)[controlPoints count] - 1) 554 : { 555 : right.x = 1.0; 556 : right.y = 1.0; 557 : } 558 : else 559 : { 560 : right = [[controlPoints objectAtIndex: (index+1)] pointValue]; 561 : } 562 : // preserve order of control points - if we attempt to move this control point beyond 563 : // either of its neighbours, move it back inside. Also keep neighbours a distance of at least SPLINE_POINT_MIN_SPACING apart 564 : if (point.x - left.x < SPLINE_POINT_MIN_SPACING) 565 : { 566 : point.x = left.x + SPLINE_POINT_MIN_SPACING; 567 : if (right.x - point.x < SPLINE_POINT_MIN_SPACING) 568 : { 569 : point.x = (left.x + right.x)/2; 570 : } 571 : } 572 : else if (right.x - point.x < SPLINE_POINT_MIN_SPACING) 573 : { 574 : point.x = right.x - SPLINE_POINT_MIN_SPACING; 575 : if (point.x - left.x < SPLINE_POINT_MIN_SPACING) 576 : { 577 : point.x = (left.x + right.x)/2; 578 : } 579 : } 580 : [controlPoints replaceObjectAtIndex: index withObject: [NSValue valueWithPoint: point]]; 581 : [self makeSegments]; 582 : return; 583 : } 584 : 585 : - (double) rawValue: (double) x 586 : { 587 : NSUInteger i; 588 : OOJoystickSplineSegment *segment; 589 : double sign; 590 : 591 : if (x < 0) 592 : { 593 : sign = -1.0; 594 : x = -x; 595 : } 596 : else 597 : { 598 : sign = 1.0; 599 : } 600 : for (i = 0; i < [segments count]; i++) 601 : { 602 : segment = [segments objectAtIndex: i]; 603 : if ([segment end] > x) 604 : { 605 : return sign * OOClamp_0_1_d([segment value:x]); 606 : } 607 : } 608 : return 1.0; 609 : } 610 : 611 : - (double) gradient: (double) x 612 : { 613 : NSUInteger i; 614 : OOJoystickSplineSegment *segment; 615 : for (i = 0; i < [segments count]; i++) 616 : { 617 : segment = [segments objectAtIndex: i]; 618 : if ([segment end] > x) 619 : { 620 : return [segment gradient:x]; 621 : } 622 : } 623 : return 1.0; 624 : } 625 : 626 : 627 : @end 628 :