Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOJoystickProfile.m
Go to the documentation of this file.
1/*
2
3OOJoystickProfile.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 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#define SPLINE_POINT_MIN_SPACING 0.02
32
33@interface OOJoystickSplineSegment: NSObject <NSCopying>
34{
35@private
36 double start;
37 double end;
38 double a[4];
40
41- (id) init;
42
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
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
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
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
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
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
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
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
67- (id) copyWithZone: (NSZone *) zone;
68- (double) start;
69- (double) end;
70- (double) value: (double) t;
71- (double) gradient: (double) t;
72
73@end
74
75@interface OOJoystickSplineAxisProfile (Private)
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
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 {
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{
455 NSUInteger i;
456 NSPoint left, right, next;
457 double gradientleft, gradientright;
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;
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;
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
#define STICK_MAX_DEADZONE
#define STICK_DEADZONE
#define STICKPROFILE_MAX_POWER
#define SPLINE_POINT_MIN_SPACING
return self
unsigned count
return nil
float y
float x
id segmentWithData:right:(NSPoint left,[right] NSPoint right)
id segmentWithData:right:gradientleft:(NSPoint left,[right] NSPoint right,[gradientleft] double gradientleft)
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque