Merge pull request #77 from kanthoney/joystick_configurator

Joystick configurator
This commit is contained in:
AnotherCommander 2014-02-01 06:05:57 -08:00
commit 1aa6022982
14 changed files with 1694 additions and 88 deletions

View File

@ -175,6 +175,7 @@ OOLITE_ENTITY_FILES = \
PlayerEntityScriptMethods.m \
PlayerEntitySound.m \
PlayerEntityStickMapper.m \
PlayerEntityStickProfile.m \
ProxyPlayerEntity.m \
OOBreakPatternEntity.m \
ShipEntity.m \
@ -385,6 +386,7 @@ OOLITE_MISC_FILES = \
GameController.m \
GameController+SDLFullScreen.m \
OOJoystickManager.m \
OOJoystickProfile.m \
OOSDLJoystickManager.m \
main.m \
MyOpenGLView.m \

View File

@ -37,6 +37,7 @@ MA 02110-1301, USA.
@class GuiDisplayGen, OOTrumble, MyOpenGLView, HeadUpDisplay, ShipEntity;
@class OOSound, OOSoundSource, OOSoundReferencePoint;
@class OOJoystickManager, OOTexture, OOLaserShotEntity;
@class StickProfileScreen;
#define ALLOW_CUSTOM_VIEWS_WHILE_PAUSED 1
#define SCRIPT_TIMER_INTERVAL 10.0
@ -612,6 +613,8 @@ typedef enum
ShipEntity *demoShip; // Used while docked to maintain demo ship rotation.
OOLaserShotEntity *lastShot; // used to correctly position laser shots on first frame of firing
StickProfileScreen *stickProfileScreen;
}
+ (PlayerEntity *) sharedPlayer;

View File

@ -80,6 +80,7 @@ MA 02110-1301, USA.
#import "OOJoystickManager.h"
#import "PlayerEntityStickMapper.h"
#import "PlayerEntityStickProfile.h"
#define PLAYER_DEFAULT_NAME @"Jameson"
@ -1740,6 +1741,8 @@ static GLfloat sBaseMass = 0.0;
demoShip = nil;
[[OOMusicController sharedController] justStop];
[stickProfileScreen release];
stickProfileScreen = [[StickProfileScreen alloc] init];
return YES;
}
@ -2626,6 +2629,7 @@ static GLfloat sBaseMass = 0.0;
case GUI_SCREEN_SAVE:
case GUI_SCREEN_SAVE_OVERWRITE:
case GUI_SCREEN_STICKMAPPER:
case GUI_SCREEN_STICKPROFILE:
case GUI_SCREEN_MISSION:
case GUI_SCREEN_REPORT:
return;

View File

@ -29,6 +29,7 @@ MA 02110-1301, USA.
#import "PlayerEntitySound.h"
#import "PlayerEntityLoadSave.h"
#import "PlayerEntityStickMapper.h"
#import "PlayerEntityStickProfile.h"
#import "ShipEntityAI.h"
#import "StationEntity.h"
@ -1428,7 +1429,7 @@ static NSTimeInterval time_last_frame;
[self pollCustomViewControls]; // allow custom views during pause
#endif
if (gui_screen == GUI_SCREEN_OPTIONS || gui_screen == GUI_SCREEN_GAMEOPTIONS || gui_screen == GUI_SCREEN_STICKMAPPER)
if (gui_screen == GUI_SCREEN_OPTIONS || gui_screen == GUI_SCREEN_GAMEOPTIONS || gui_screen == GUI_SCREEN_STICKMAPPER || gui_screen == GUI_SCREEN_STICKPROFILE )
{
if ([UNIVERSE pauseMessageVisible]) [[UNIVERSE messageGUI] leaveLastLine];
else [[UNIVERSE messageGUI] clear];
@ -1903,7 +1904,7 @@ static NSTimeInterval time_last_frame;
if (from_function < 0) from_function = 0;
[self setGuiToStickMapperScreen:from_function];
if ([[UNIVERSE gui] selectedRow] < 0)
if ([[UNIVERSE gui] selectedRow] < GUI_ROW_FUNCSTART)
{
[[UNIVERSE gui] setSelectedRow: GUI_ROW_FUNCSTART];
}
@ -1914,6 +1915,10 @@ static NSTimeInterval time_last_frame;
}
}
break;
case GUI_SCREEN_STICKPROFILE:
[self stickProfileInputHandler: gui view: gameView];
break;
case GUI_SCREEN_GAMEOPTIONS:
[self handleGameOptionsScreenKeys];

View File

@ -32,10 +32,11 @@ MA 02110-1301, USA.
#define MAX_ROWS_FUNCTIONS 12
#define GUI_ROW_STICKNAME 1
#define GUI_ROW_HEADING 3
#define GUI_ROW_FUNCSTART 4
#define GUI_ROW_STICKPROFILE 2
#define GUI_ROW_HEADING 4
#define GUI_ROW_FUNCSTART 5
#define GUI_ROW_FUNCEND (GUI_ROW_FUNCSTART + MAX_ROWS_FUNCTIONS - 1)
#define GUI_ROW_INSTRUCT 17
#define GUI_ROW_INSTRUCT 18
// Dictionary keys
#define KEY_GUIDESC @"guiDesc"

View File

@ -1,6 +1,6 @@
/*
PlayerEntityStickMapper.h
PlayerEntityStickMapper.m
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
@ -24,11 +24,11 @@ MA 02110-1301, USA.
#import "PlayerEntityStickMapper.h"
#import "PlayerEntityControls.h"
#import "PlayerEntityStickProfile.h"
#import "OOJoystickManager.h"
#import "OOTexture.h"
#import "OOCollectionExtractors.h"
@interface PlayerEntity (StickMapperInternal)
- (void) removeFunction:(int)selFunctionIdx;
@ -68,13 +68,15 @@ MA 02110-1301, USA.
nil]
forRow:i + GUI_ROW_STICKNAME];
}
[gui setArray: [NSArray arrayWithObjects: @"Edit Axis Profiles", nil] forRow: GUI_ROW_STICKPROFILE];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE];
[self displayFunctionList:gui skip:skip];
[gui setArray:[NSArray arrayWithObject:@"Select a function and press Enter to modify or 'u' to unset."]
forRow:GUI_ROW_INSTRUCT];
[gui setSelectedRow: selFunctionIdx + GUI_ROW_FUNCSTART];
[gui setSelectedRow: GUI_ROW_STICKPROFILE];
[[UNIVERSE gameView] supressKeysUntilKeyUp];
[gui setForegroundTextureKey:[self status] == STATUS_DOCKED ? @"docked_overlay" : @"paused_overlay"];
[gui setBackgroundTextureKey:@"settings"];
@ -85,7 +87,7 @@ MA 02110-1301, USA.
view:(MyOpenGLView *)gameView
{
OOJoystickManager *stickHandler = [OOJoystickManager sharedStickHandler];
// Don't do anything if the user is supposed to be selecting
// a function - other than look for Escape.
if(waitingForStickCallback)
@ -105,6 +107,12 @@ MA 02110-1301, USA.
[self handleGUIUpDownArrowKeys];
if ([gui selectedRow] == GUI_ROW_STICKPROFILE && [gameView isDown: 13])
{
[self setGuiToStickProfileScreen: gui];
return;
}
NSString* key = [gui keyForRow: [gui selectedRow]];
if ([key hasPrefix:@"Index:"])
selFunctionIdx=[[[key componentsSeparatedByString:@":"] objectAtIndex: 1] intValue];
@ -360,7 +368,7 @@ MA 02110-1301, USA.
i++;
}
[gui setSelectableRange: NSMakeRange(GUI_ROW_FUNCSTART, i + start_row - GUI_ROW_FUNCSTART)];
[gui setSelectableRange: NSMakeRange(GUI_ROW_STICKPROFILE, i + start_row - GUI_ROW_STICKPROFILE)];
}
}
@ -594,7 +602,7 @@ MA 02110-1301, USA.
[guiDict setObject: [NSNumber numberWithInt: butfn]
forKey: KEY_BUTTONFN];
return guiDict;
}
}
@end

View File

@ -0,0 +1,62 @@
/*
PlayerEntityStickProfile.h
GUI for managing joystick profile settings
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#import "PlayerEntity.h"
#import "GuiDisplayGen.h"
#import "MyOpenGLView.h"
#import "OOJoystickProfile.h"
#import "Universe.h"
@interface PlayerEntity (StickProfile)
- (void) setGuiToStickProfileScreen: (GuiDisplayGen *) gui;
- (void) stickProfileInputHandler: (GuiDisplayGen *) gui view: (MyOpenGLView *) gameView;
- (void) stickProfileGraphAxisProfile: (GLfloat) alpha screenAt: (Vector) screenAt screenSize: (NSSize) screenSize;
@end
@interface StickProfileScreen: NSObject
{
@private
OOJoystickManager *stickHandler;
int current_axis;
OOJoystickAxisProfile *profiles[3][2];
GuiDisplayGen *gui;
NSRect graphRect;
int selected_control_point;
int dragged_control_point;
int double_click_control_point;
}
- (id) init;
- (void) dealloc;
- (void) startGui: (GuiDisplayGen *) gui_display_gen;
- (void) mouseDown: (NSPoint) position;
- (void) mouseUp;
- (void) deleteSelected;
@end

View File

@ -0,0 +1,663 @@
/*
PlayerEntityStickProfile.m
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#import "PlayerEntity.h"
#import "PlayerEntityStickProfile.h"
#import "PlayerEntityStickMapper.h"
#import "OOJoystickManager.h"
#import "OOJoystickProfile.h"
#import "PlayerEntityControls.h"
#import "PlayerEntitySound.h"
#import "OOOpenGL.h"
#import "OOMacroOpenGL.h"
#define GUI_ROW_STICKPROFILE_BACK 18
#define GUI_ROW_STICKPROFILE_AXIS 1
#define GUI_ROW_STICKPROFILE_DEADZONE 2
#define GUI_ROW_STICKPROFILE_PROFILE_TYPE 3
#define GUI_ROW_STICKPROFILE_POWER 4
#define GUI_ROW_STICKPROFILE_PARAM 5
static BOOL stickProfileArrow_pressed;
@interface StickProfileScreen (StickProfileInternal)
- (void) showScreen;
- (void) nextAxis;
- (NSString *) currentAxis;
- (void) previousAxis;
- (void) increaseDeadzone;
- (void) decreaseDeadzone;
- (void) nextProfileType;
- (void) previousProfileType;
- (void) IncreasePower;
- (BOOL) currentProfileIsSpline;
- (void) DecreasePower;
- (void) IncreaseParam;
- (void) DecreaseParam;
- (void) saveSettings;
- (void) graphProfile: (GLfloat) alpha at: (Vector) at size: (NSSize) size;
- (void) startEdit;
- (NSString *) profileType;
@end
@implementation PlayerEntity (StickProfile)
- (void) setGuiToStickProfileScreen: (GuiDisplayGen *) gui
{
gui_screen = GUI_SCREEN_STICKPROFILE;
[stickProfileScreen startGui: gui];
return;
}
- (void) stickProfileInputHandler: (GuiDisplayGen *) gui
view: (MyOpenGLView *) gameView
{
if ([gameView isDown: gvMouseLeftButton])
{
NSPoint mouse_position = NSMakePoint(
[gameView virtualJoystickPosition].x * [gui size].width,
[gameView virtualJoystickPosition].y * [gui size].height );
[stickProfileScreen mouseDown: mouse_position];
}
else
{
[stickProfileScreen mouseUp];
}
if ([gameView isDown: gvDeleteKey])
{
[stickProfileScreen deleteSelected];
}
[self handleGUIUpDownArrowKeys];
if ([gameView isDown:13] && [gui selectedRow] == GUI_ROW_STICKPROFILE_BACK)
{
[stickProfileScreen saveSettings];
[self setGuiToStickMapperScreen: 0];
}
switch ([gui selectedRow])
{
case GUI_ROW_STICKPROFILE_AXIS:
if ([gameView isDown:key_gui_arrow_left])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_right])
{
[stickProfileScreen previousAxis];
stickProfileArrow_pressed = YES;
}
}
else if ([gameView isDown: key_gui_arrow_right])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_left])
{
[stickProfileScreen nextAxis];
stickProfileArrow_pressed = YES;
}
}
else
{
stickProfileArrow_pressed = NO;
}
break;
case GUI_ROW_STICKPROFILE_DEADZONE:
if ([gameView isDown:key_gui_arrow_left])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_right])
{
[stickProfileScreen decreaseDeadzone];
stickProfileArrow_pressed = YES;
}
}
else if ([gameView isDown: key_gui_arrow_right])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_left])
{
[stickProfileScreen increaseDeadzone];
stickProfileArrow_pressed = YES;
}
}
else
{
stickProfileArrow_pressed = NO;
}
break;
case GUI_ROW_STICKPROFILE_PROFILE_TYPE:
if ([gameView isDown:key_gui_arrow_left])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_right])
{
[stickProfileScreen previousProfileType];
stickProfileArrow_pressed = YES;
}
}
else if ([gameView isDown: key_gui_arrow_right])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_left])
{
[stickProfileScreen nextProfileType];
stickProfileArrow_pressed = YES;
}
}
else
{
stickProfileArrow_pressed = NO;
}
break;
}
if (![stickProfileScreen currentProfileIsSpline])
{
if ([gui selectedRow] == GUI_ROW_STICKPROFILE_POWER)
{
if ([gameView isDown:key_gui_arrow_left])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_right])
{
[stickProfileScreen DecreasePower];
stickProfileArrow_pressed = YES;
}
}
else if ([gameView isDown: key_gui_arrow_right])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_left])
{
[stickProfileScreen IncreasePower];
stickProfileArrow_pressed = YES;
}
}
else
{
stickProfileArrow_pressed = NO;
}
}
else if ([gui selectedRow] == GUI_ROW_STICKPROFILE_PARAM)
{
if ([gameView isDown:key_gui_arrow_left])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_right])
{
[stickProfileScreen DecreaseParam];
stickProfileArrow_pressed = YES;
}
}
else if ([gameView isDown: key_gui_arrow_right])
{
if (!stickProfileArrow_pressed && ![gameView isDown: key_gui_arrow_left])
{
[stickProfileScreen IncreaseParam];
stickProfileArrow_pressed = YES;
}
}
else
{
stickProfileArrow_pressed = NO;
}
}
}
return;
}
- (void) stickProfileGraphAxisProfile: (GLfloat) alpha screenAt: (Vector) screenAt screenSize: (NSSize) screenSize
{
[stickProfileScreen graphProfile: alpha at: make_vector(screenAt.x - screenSize.width/2.0, screenAt.y - 40, screenAt.z) size: NSMakeSize(screenSize.width,150)];
return;
}
@end
@implementation StickProfileScreen
- (id) init
{
int i, j;
if ((self = [super init]))
{
stickHandler = [OOJoystickManager sharedStickHandler];
current_axis = AXIS_ROLL;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 2; j++)
{
profiles[i][j] = nil;
}
}
}
return self;
}
- (void) dealloc
{
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 2; j++)
{
[profiles[i][j] release];
}
}
[super dealloc];
}
- (void) startGui: (GuiDisplayGen *) gui_display_gen
{
gui = gui_display_gen;
[self startEdit];
[gui clear];
[gui setTitle: [NSString stringWithFormat: @"Joystick Profile"]];
[self showScreen];
[gui setSelectedRow: GUI_ROW_STICKPROFILE_AXIS];
return;
}
- (void) mouseDown: (NSPoint) position
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickSplineAxisProfile *spline_profile;
NSPoint spline_position;
if (![profile isKindOfClass: [OOJoystickSplineAxisProfile class]])
{
return;
}
spline_profile = (OOJoystickSplineAxisProfile *)profile;
spline_position.x = (position.x - graphRect.origin.x - 10) / (graphRect.size.width - 20);
spline_position.y = (-position.y - graphRect.origin.y - 10) / (graphRect.size.height - 20);
if (spline_position.x >= 0.0 && spline_position.x <= 1.0 && spline_position.y >= 0.0 && spline_position.y <= 1.0)
{
if (dragged_control_point < 0)
{
selected_control_point = [spline_profile addControl: spline_position];
dragged_control_point = selected_control_point;
double_click_control_point = -1;
}
else
{
[spline_profile moveControl: dragged_control_point point: spline_position];
}
[stickHandler saveStickSettings];
}
return;
}
- (void) mouseUp
{
if (selected_control_point >= 0)
{
double_click_control_point = selected_control_point;
}
dragged_control_point = -1;
return;
}
- (void) deleteSelected
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickSplineAxisProfile *spline_profile;
if ([profile isKindOfClass: [OOJoystickSplineAxisProfile class]] && selected_control_point >= 0)
{
spline_profile = (OOJoystickSplineAxisProfile *)profile;
[spline_profile removeControl: selected_control_point];
selected_control_point = -1;
dragged_control_point = -1;
[stickHandler saveStickSettings];
}
return;
}
@end
@implementation StickProfileScreen (StickProfileInternal)
- (void) nextAxis
{
if (current_axis == AXIS_ROLL)
current_axis = AXIS_PITCH;
else if (current_axis == AXIS_PITCH)
current_axis = AXIS_YAW;
[self showScreen];
return;
}
- (void) previousAxis
{
if (current_axis == AXIS_PITCH)
current_axis = AXIS_ROLL;
else if (current_axis == AXIS_YAW)
current_axis = AXIS_PITCH;
[self showScreen];
return;
}
- (NSString *) currentAxis
{
switch (current_axis)
{
case AXIS_ROLL:
return @"Roll";
case AXIS_PITCH:
return @"Pitch";
case AXIS_YAW:
return @"Yaw";
}
return @"";
}
- (void) increaseDeadzone
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
if (profile)
{
[profile setDeadzone: [profile deadzone] + STICK_MAX_DEADZONE / 20];
}
[self showScreen];
return;
}
- (void) decreaseDeadzone
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
if (profile)
{
[profile setDeadzone: [profile deadzone] - STICK_MAX_DEADZONE / 20];
}
[self showScreen];
return;
}
- (void) nextProfileType
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
if ([profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
{
[profiles[current_axis][0] release];
profiles[current_axis][0] = [profile retain];
if (!profiles[current_axis][1])
{
profiles[current_axis][1] = [[OOJoystickSplineAxisProfile alloc] init];
}
[stickHandler setProfile: profiles[current_axis][1] forAxis: current_axis];
[stickHandler saveStickSettings];
}
[self showScreen];
return;
}
- (void) previousProfileType
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
if ([profile isKindOfClass: [OOJoystickSplineAxisProfile class]])
{
[profiles[current_axis][1] release];
profiles[current_axis][1] = [profile retain];
if (!profiles[current_axis][0])
{
profiles[current_axis][0] = [[OOJoystickStandardAxisProfile alloc] init];
}
[stickHandler setProfile: profiles[current_axis][0] forAxis: current_axis];
[stickHandler saveStickSettings];
}
[self showScreen];
return;
}
- (BOOL) currentProfileIsSpline
{
if ([[stickHandler getProfileForAxis: current_axis] isKindOfClass: [OOJoystickSplineAxisProfile class]])
{
return YES;
}
return NO;
}
- (void) IncreasePower
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickStandardAxisProfile *standard_profile;
if (profile && [profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
{
standard_profile = (OOJoystickStandardAxisProfile *) profile;
[standard_profile setPower: [standard_profile power] + STICKPROFILE_MAX_POWER / 20];
[stickHandler saveStickSettings];
}
[self showScreen];
return;
}
- (void) DecreasePower
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickStandardAxisProfile *standard_profile;
if (profile && [profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
{
standard_profile = (OOJoystickStandardAxisProfile *) profile;
[standard_profile setPower: [standard_profile power] - STICKPROFILE_MAX_POWER / 20];
[stickHandler saveStickSettings];
}
[self showScreen];
return;
}
- (void) IncreaseParam
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickStandardAxisProfile *standard_profile;
if (profile && [profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
{
standard_profile = (OOJoystickStandardAxisProfile *) profile;
[standard_profile setParameter: [standard_profile parameter] + 0.05];
[stickHandler saveStickSettings];
}
[self showScreen];
return;
}
- (void) DecreaseParam
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickStandardAxisProfile *standard_profile;
if (profile && [profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
{
standard_profile = (OOJoystickStandardAxisProfile *) profile;
[standard_profile setParameter: [standard_profile parameter] - 0.05];
[stickHandler saveStickSettings];
}
[self showScreen];
return;
}
- (void) graphProfile: (GLfloat) alpha at: (Vector) at size: (NSSize) size
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickSplineAxisProfile *spline_profile;
int i;
NSPoint point;
NSArray *control_points;
if (!profile) return;
graphRect = NSMakeRect(at.x, at.y, size.width, size.height);
OO_ENTER_OPENGL();
OOGL(glColor4f(0.2,0.2,0.5,alpha));
OOGLBEGIN(GL_QUADS);
glVertex3f(at.x,at.y,at.z);
glVertex3f(at.x + size.width,at.y,at.z);
glVertex3f(at.x + size.width,at.y + size.height,at.z);
glVertex3f(at.x,at.y + size.height,at.z);
OOGLEND();
OOGL(glColor4f(0.9,0.9,0.9,alpha));
OOGL(GLScaledLineWidth(2.0f));
OOGLBEGIN(GL_LINE_STRIP);
for (i = 0; i <= size.width - 20; i++)
{
glVertex3f(at.x+i+10,at.y+10+(size.height-20)*[profile rawValue:((float)i)/(size.width-20)],at.z);
}
OOGLEND();
OOGL(glColor4f(0.5,0.0,0.5,alpha));
GLDrawFilledOval(at.x+10,at.y+10,at.z,NSMakeSize(4,4),20);
GLDrawFilledOval(at.x+size.width-10,at.y+size.height-10,at.z,NSMakeSize(4,4),20);
if ([profile isKindOfClass: [OOJoystickSplineAxisProfile class]])
{
spline_profile = (OOJoystickSplineAxisProfile *)profile;
control_points = [spline_profile controlPoints];
for (i = 0; i < [control_points count]; i++)
{
if (i == selected_control_point)
{
OOGL(glColor4f(1.0,0.0,0.0,alpha));
}
else
{
OOGL(glColor4f(0.0,1.0,0.0,alpha));
}
point = [[control_points objectAtIndex: i] pointValue];
GLDrawFilledOval(at.x+10+point.x*(size.width - 20),at.y+10+point.y*(size.height-20),at.z,NSMakeSize(4,4),20);
}
}
return;
}
- (void) startEdit
{
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 2; j++)
{
[profiles[i][j] release];
profiles[i][j] = nil;
}
}
current_axis = AXIS_ROLL;
selected_control_point = -1;
dragged_control_point = -1;
double_click_control_point = -1;
return;
}
- (void) saveSettings
{
[stickHandler saveStickSettings];
return;
}
- (void) showScreen
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
OOJoystickStandardAxisProfile *standard_profile;
NSString *v1 = @"||||||||||||||||||||";
NSString *v2 = @"....................";
int bars;
double value;
double power;
OOGUITabStop tabStop[GUI_MAX_COLUMNS];
tabStop[0] = 50;
tabStop[1] = 140;
[gui setTabStops:tabStop];
[gui setText: @"Back" forRow: GUI_ROW_STICKPROFILE_BACK];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE_BACK];
[gui setArray: [NSArray arrayWithObjects: @"Axis:", [self currentAxis], nil ] forRow: GUI_ROW_STICKPROFILE_AXIS];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE_AXIS];
value = [profile deadzone];
bars = 20 * value / STICK_MAX_DEADZONE;
[gui setArray: [NSArray arrayWithObjects: @"Deadzone:",
[NSString stringWithFormat:
@"%@%@ (%0.4f)",
[v1 substringToIndex: bars],
[v2 substringToIndex: 20 - bars],
value,
nil],
nil] forRow: GUI_ROW_STICKPROFILE_DEADZONE];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE_DEADZONE];
[gui setArray: [NSArray arrayWithObjects: @"Profile Type:", [self profileType], nil ] forRow: GUI_ROW_STICKPROFILE_PROFILE_TYPE];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE_PROFILE_TYPE];
if ([profile isKindOfClass:[OOJoystickStandardAxisProfile class]])
{
standard_profile = (OOJoystickStandardAxisProfile*) profile;
power = [standard_profile power];
bars = 20*power / STICKPROFILE_MAX_POWER;
if (bars < 0) bars = 0;
if (bars > 20) bars = 20;
[gui setArray: [NSArray arrayWithObjects: @"Power:",
[NSString stringWithFormat: @"%@%@ (%.1f) ", [v1 substringToIndex: bars], [v2 substringToIndex: 20 - bars], power],
nil] forRow: GUI_ROW_STICKPROFILE_POWER];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE_POWER];
value = [standard_profile parameter];
bars = 20*value;
if (bars < 0) bars = 0;
if (bars > 20) bars = 20;
[gui setArray: [NSArray arrayWithObjects: @"Parameter:",
[NSString stringWithFormat: @"%@%@ (%0.2f) ", [v1 substringToIndex: bars], [v2 substringToIndex: 20 - bars], value],
nil] forRow: GUI_ROW_STICKPROFILE_PARAM];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE_PARAM];
[gui setColor:[OOColor yellowColor] forRow: GUI_ROW_STICKPROFILE_PARAM];
}
else
{
[gui setText: @"" forRow: GUI_ROW_STICKPROFILE_POWER];
[gui setKey: GUI_KEY_SKIP forRow: GUI_ROW_STICKPROFILE_POWER];
[gui setText: @"Click and Drag to set control points. Select and <Del> to delete points." forRow: GUI_ROW_STICKPROFILE_PARAM];
[gui setKey: GUI_KEY_SKIP forRow: GUI_ROW_STICKPROFILE_PARAM];
[gui setColor:[OOColor magentaColor] forRow: GUI_ROW_STICKPROFILE_PARAM];
}
[gui setText: @"Back" forRow: GUI_ROW_STICKPROFILE_BACK];
[gui setKey: GUI_KEY_OK forRow: GUI_ROW_STICKPROFILE_BACK];
[gui setSelectableRange: NSMakeRange(1, GUI_ROW_STICKPROFILE_BACK)];
[[UNIVERSE gameView] supressKeysUntilKeyUp];
[gui setForegroundTextureKey:[PLAYER status] == STATUS_DOCKED ? @"docked_overlay" : @"paused_overlay"];
[gui setBackgroundTextureKey: @"settings"];
return;
}
- (NSString *) profileType
{
OOJoystickAxisProfile *profile = [stickHandler getProfileForAxis: current_axis];
if ([profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
{
return @"Standard";
}
if ([profile isKindOfClass: [OOJoystickSplineAxisProfile class]])
{
return @"Spline";
}
return @"Standard";
}
@end

View File

@ -36,7 +36,7 @@ MA 02110-1301, USA.
#import "OOCollectionExtractors.h"
#import "OOTexture.h"
#import "OOJavaScriptEngine.h"
#import "PlayerEntityStickProfile.h"
OOINLINE BOOL RowInRange(OOGUIRow row, NSRange range)
{
@ -1252,6 +1252,10 @@ static OOTextureSprite *NewTextureSpriteWithDescriptor(NSDictionary *descriptor)
{
[self drawEquipmentList:[player equipmentList] z:z];
}
if ([player guiScreen] == GUI_SCREEN_STICKPROFILE)
{
[player stickProfileGraphAxisProfile: alpha screenAt: make_vector(x,y,z) screenSize: size_in_pixels];
}
}
if (fade_sign)

View File

@ -112,6 +112,9 @@ enum {
#define STICK_DEADZONE 0.05
#endif
#define STICK_MAX_DEADZONE (STICK_DEADZONE * 2)
// Kind of stick device (these are bits - if any more are added,
// the next one is 4 and so on).
#define HW_AXIS 1
@ -128,9 +131,9 @@ enum {
#define STICK_NUMBER @"stickNum" // Stick number 0 to 4
#define STICK_AXBUT @"stickAxBt" // Axis or button number
#define STICK_FUNCTION @"stickFunc" // Function of axis/button
#define STICK_DEADZONE_SETTING @"JoystickAxesDeadzone" // Deadzone setting double 0.0 to 1.0.
#define STICK_PRECISION_SETTING @"JoystickPrecision" // Precision mode
#define STICK_NONLINEAR_PARAMETER @"JoystickNonlinear" // Nonlinear parameter double from 0.0 to 1.0
#define STICK_ROLL_AXIS_PROFILE_SETTING @"RollAxisProfile" // Joystick Profiles
#define STICK_PITCH_AXIS_PROFILE_SETTING @"PitchAxisProfile" // Joystick Profiles
#define STICK_YAW_AXIS_PROFILE_SETTING @"YawAxisProfile" // Joystick Profiles
// shortcut to make code more readable when using enum as key for
// an NSDictionary
#define ENUMKEY(x) [NSString stringWithFormat: @"%d", x]
@ -219,6 +222,8 @@ typedef struct
#endif //OOLITE_SDL
#import "OOJoystickProfile.h"
@interface OOJoystickManager: NSObject
{
@private
@ -229,6 +234,9 @@ typedef struct
BOOL butstate[BUTTON_end];
uint8_t hatstate[MAX_STICKS][MAX_HATS];
BOOL precisionMode;
OOJoystickAxisProfile *roll_profile;
OOJoystickAxisProfile *pitch_profile;
OOJoystickAxisProfile *yaw_profile;
// Handle callbacks - the object, selector to call
// the desired function, and the hardware (axis or button etc.)
@ -236,12 +244,7 @@ typedef struct
SEL cbSelector;
char cbHardware;
BOOL invertPitch;
double deadzone;
// parameter for nonlinear settings. This is a double between 0.0 and 1.0. 1.0 - nonlinear_parameter is the
// gradient of the transform function at zero. 0.0 means the gradient is 1.0, which makes the stick linear.
// Higher values reduce the stick response for more accuracy at the centre.
double nonlinear_parameter;
}
+ (id) sharedStickHandler;
@ -273,8 +276,11 @@ typedef struct
- (double) getAxisState:(int)function;
- (double) getSensitivity;
// Transform raw axis state into actual axis state
- (double) axisTransform: (double)axisvalue;
// Axis profile handling
- (void) setProfile: (OOJoystickAxisProfile *) profile forAxis:(int) axis;
- (OOJoystickAxisProfile *) getProfileForAxis: (int) axis;
- (void) saveProfileForAxis: (int) axis;
- (void) loadProfileForAxis: (int) axis;
// This one just returns a pointer to the entire state array to
// allow for multiple lookups with only one objc_sendMsg

View File

@ -86,8 +86,8 @@ static id sSharedStickHandler = nil;
// Make some sensible mappings. This also ensures unassigned
// axes and buttons are set to unassigned (STICK_NOFUNCTION).
[self loadStickSettings];
invertPitch = NO;
precisionMode = NO;
}
return self;
}
@ -127,9 +127,32 @@ static id sSharedStickHandler = nil;
switch (function)
{
case AXIS_ROLL:
if (precisionMode)
{
return [roll_profile value:axstate[function]] / STICK_PRECISIONFAC;
}
else
{
return [roll_profile value:axstate[function]];
}
case AXIS_PITCH:
if (precisionMode)
{
return [pitch_profile value:axstate[function]] / STICK_PRECISIONFAC;
}
else
{
return [pitch_profile value:axstate[function]];
}
case AXIS_YAW:
return [self axisTransform:axstate[function]];
if (precisionMode)
{
return [yaw_profile value:axstate[function]] / STICK_PRECISIONFAC;
}
else
{
return [yaw_profile value:axstate[function]];
}
default:
return axstate[function];
}
@ -141,52 +164,160 @@ static id sSharedStickHandler = nil;
return precisionMode ? STICK_PRECISIONFAC : 1.0;
}
- (double) deadZone
- (void) setProfile: (OOJoystickAxisProfile *) profile forAxis: (int) axis
{
return deadzone;
switch (axis)
{
case AXIS_ROLL:
[roll_profile release];
roll_profile = [profile retain];
break;
case AXIS_PITCH:
[pitch_profile release];
pitch_profile = [profile retain];
break;
case AXIS_YAW:
[yaw_profile release];
yaw_profile = [profile retain];
break;
}
return;
}
- (OOJoystickAxisProfile *) getProfileForAxis: (int) axis
{
switch (axis)
{
case AXIS_ROLL:
return roll_profile;
case AXIS_PITCH:
return pitch_profile;
case AXIS_YAW:
return yaw_profile;
}
return nil;
}
- (void) setDeadZone: (double)newValue
- (void) saveProfileForAxis: (int) axis
{
deadzone = newValue;
}
// axisTransform is an increasing function that maps the interval [-1,1] onto [-1,1] such that -1 -> -1, 0 -> 0 and 1 -> 1
// By using a function with a shallow gradient at the origin, we can make the stick less sensitive at the centre (and hence
// easier to make fine adjustments). The functions I've used below are ax^n+bx where a+b=1 and n in an odd power.
- (double) axisTransform: (double)axisvalue
{
if (fabs(axisvalue) < deadzone) return 0.0;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
OOJoystickAxisProfile *profile;
OOJoystickStandardAxisProfile *standard_profile;
OOJoystickSplineAxisProfile *spline_profile;
NSArray *controlPoints;
NSMutableArray *points;
NSPoint point;
int i;
// since we're mucking around with nonlinear stuff, we may as well throw in a smooth transition
// from deadzone to non-deadzone
if (axisvalue < 0.0) axisvalue = -(-axisvalue - deadzone)/(1 - deadzone);
else axisvalue = (axisvalue - deadzone)/(1 - deadzone);
// apply non-linearity
axisvalue = axisvalue*((1.0-nonlinear_parameter) + nonlinear_parameter*axisvalue*axisvalue);
// apply precision mode if needed
if (precisionMode)
profile = [self getProfileForAxis: axis];
if (!profile) return;
[dict setObject: [NSNumber numberWithDouble: [profile deadzone]] forKey: @"Deadzone"];
if ([profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
{
axisvalue /= STICK_PRECISIONFAC;
standard_profile = (OOJoystickStandardAxisProfile *) profile;
[dict setObject: @"Standard" forKey: @"Type"];
[dict setObject: [NSNumber numberWithDouble: [standard_profile power]] forKey: @"Power"];
[dict setObject: [NSNumber numberWithDouble: [standard_profile parameter]] forKey: @"Parameter"];
}
return axisvalue;
/*
// these original settings caused problems for test pilots due to
// expectation that precisionmode would also reduce full-axis turn
// rate.
if (precisionMode)
else if ([profile isKindOfClass: [OOJoystickSplineAxisProfile class]])
{
return axisvalue*((1.0-nonlinear_parameter) + nonlinear_parameter*axisvalue*axisvalue);
spline_profile = (OOJoystickSplineAxisProfile *) profile;
[dict setObject: @"Spline" forKey: @"Type"];
controlPoints = [NSArray arrayWithArray: [spline_profile controlPoints]];
points = [[NSMutableArray alloc] initWithCapacity: [controlPoints count]];
for (i = 0; i < [controlPoints count]; i++)
{
point = [[controlPoints objectAtIndex: i] pointValue];
[points addObject: [NSArray arrayWithObjects:
[NSNumber numberWithFloat: point.x],
[NSNumber numberWithFloat: point.y],
nil ]];
}
[dict setObject: points forKey: @"ControlPoints"];
}
return axisvalue; */
else
{
[dict setObject: @"Standard" forKey: @"Type"];
}
if (axis == AXIS_ROLL)
{
[defaults setObject: dict forKey: STICK_ROLL_AXIS_PROFILE_SETTING];
}
else if (axis == AXIS_PITCH)
{
[defaults setObject: dict forKey: STICK_PITCH_AXIS_PROFILE_SETTING];
}
else if (axis == AXIS_YAW)
{
[defaults setObject: dict forKey: STICK_YAW_AXIS_PROFILE_SETTING];
}
return;
}
- (void) loadProfileForAxis: (int) axis
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *dict;
OOJoystickStandardAxisProfile *standard_profile;
OOJoystickSplineAxisProfile *spline_profile;
if (axis == AXIS_ROLL)
{
dict = [defaults objectForKey: STICK_ROLL_AXIS_PROFILE_SETTING];
}
else if (axis == AXIS_PITCH)
{
dict = [defaults objectForKey: STICK_PITCH_AXIS_PROFILE_SETTING];
}
else if (axis == AXIS_YAW)
{
dict = [defaults objectForKey: STICK_YAW_AXIS_PROFILE_SETTING];
}
else
{
return;
}
NSString *type = [dict objectForKey: @"Type"];
if ([type isEqualToString: @"Standard"])
{
standard_profile = [[OOJoystickStandardAxisProfile alloc] init];
[standard_profile setDeadzone: [[dict objectForKey: @"Deadzone"] doubleValue]];
[standard_profile setPower: [[dict objectForKey: @"Power"] doubleValue]];
[standard_profile setParameter: [[dict objectForKey: @"Parameter"] doubleValue]];
[self setProfile: [standard_profile autorelease] forAxis: axis];
}
else if([type isEqualToString: @"Spline"])
{
spline_profile = [[OOJoystickSplineAxisProfile alloc] init];
[spline_profile setDeadzone: [[dict objectForKey: @"Deadzone"] doubleValue]];
NSArray *points = [dict objectForKey: @"ControlPoints"], *pointArray;
NSPoint point;
int i;
for (i = 0; i < [points count]; i++)
{
pointArray = [points objectAtIndex: i];
if ([pointArray count] >= 2)
{
point = NSMakePoint([[pointArray objectAtIndex: 0] floatValue], [[pointArray objectAtIndex: 1] floatValue]);
[spline_profile addControl: point];
}
}
[self setProfile: [spline_profile autorelease] forAxis: axis];
}
else
{
[self setProfile: [[[OOJoystickStandardAxisProfile alloc] init] autorelease] forAxis: axis];
}
}
- (NSArray *)listSticks
{
NSUInteger i, stickCount = [self joystickCount];
@ -423,7 +554,7 @@ static id sSharedStickHandler = nil;
if(cbObject && (cbHardware & HW_AXIS))
{
// ...then check if axis moved more than AXCBTHRESH - (fix for BUG #17482)
if(axisvalue > AXCBTHRESH)
if(axisvalue > AXCBTHRESH)
{
NSDictionary *fnDict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool: YES], STICK_ISAXIS,
@ -516,7 +647,6 @@ static id sSharedStickHandler = nil;
bs = YES;
if(function == BUTTON_PRECISION)
precisionMode = !precisionMode;
[[NSUserDefaults standardUserDefaults] setBool: precisionMode forKey: STICK_PRECISION_SETTING];
}
if (function >= 0)
@ -527,24 +657,24 @@ static id sSharedStickHandler = nil;
}
- (void) decodeHatEvent:(JoyHatEvent *)evt
{
// HACK: handle this as a set of buttons
int i;
JoyButtonEvent btn;
- (void) decodeHatEvent:(JoyHatEvent *)evt
{
// HACK: handle this as a set of buttons
int i;
JoyButtonEvent btn;
btn.which = evt->which;
btn.which = evt->which;
for (i = 0; i < 4; ++i)
{
if ((evt->value ^ hatstate[evt->which][evt->hat]) & (1 << i))
{
btn.type = (evt->value & (1 << i)) ? JOYBUTTONDOWN : JOYBUTTONUP;
btn.button = MAX_REAL_BUTTONS + i + evt->which * 4;
btn.state = (evt->value & (1 << i)) ? JOYBUTTON_PRESSED : JOYBUTTON_RELEASED;
[self decodeButtonEvent:&btn];
}
}
for (i = 0; i < 4; ++i)
{
if ((evt->value ^ hatstate[evt->which][evt->hat]) & (1 << i))
{
btn.type = (evt->value & (1 << i)) ? JOYBUTTONDOWN : JOYBUTTONUP;
btn.button = MAX_REAL_BUTTONS + i + evt->which * 4;
btn.state = (evt->value & (1 << i)) ? JOYBUTTON_PRESSED : JOYBUTTON_RELEASED;
[self decodeButtonEvent:&btn];
}
}
hatstate[evt->which][evt->hat] = evt->value;
}
@ -559,13 +689,14 @@ static id sSharedStickHandler = nil;
- (void) saveStickSettings
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[self axisFunctions]
forKey:AXIS_SETTINGS];
[defaults setObject:[self buttonFunctions]
forKey:BUTTON_SETTINGS];
[defaults setFloat: deadzone forKey: STICK_DEADZONE_SETTING];
[defaults setFloat: nonlinear_parameter forKey: STICK_NONLINEAR_PARAMETER];
[defaults setBool: !!precisionMode forKey: STICK_PRECISION_SETTING];
[self saveProfileForAxis: AXIS_ROLL];
[self saveProfileForAxis: AXIS_PITCH];
[self saveProfileForAxis: AXIS_YAW];
[defaults synchronize];
}
@ -573,7 +704,7 @@ static id sSharedStickHandler = nil;
- (void) loadStickSettings
{
unsigned i;
[self clearMappings];
[self clearMappings];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *axisSettings = [defaults objectForKey: AXIS_SETTINGS];
NSDictionary *buttonSettings = [defaults objectForKey: BUTTON_SETTINGS];
@ -602,13 +733,9 @@ static id sSharedStickHandler = nil;
// Nothing to load - set useful defaults
[self setDefaultMapping];
}
deadzone = [defaults oo_doubleForKey:STICK_DEADZONE_SETTING defaultValue:STICK_DEADZONE];
if (deadzone < 0 || deadzone > 1)
{
deadzone = STICK_DEADZONE;
}
nonlinear_parameter = OOClamp_0_1_d( [defaults oo_doubleForKey: STICK_NONLINEAR_PARAMETER defaultValue: 1.0] );
precisionMode = [defaults oo_boolForKey: STICK_PRECISION_SETTING defaultValue:NO];
[self loadProfileForAxis: AXIS_ROLL];
[self loadProfileForAxis: AXIS_PITCH];
[self loadProfileForAxis: AXIS_YAW];
}
// These get overidden by subclasses

View File

@ -0,0 +1,92 @@
/*
OOJoystickProfile.h
JoystickProfile maintains settings such as deadzone and the mapping
from joystick movement to response.
JoystickSpline manages the mapping of the physical joystick movements
to the joystick response. It holds a series of control points, with
the points (0,0) and (1,1) being assumed. It then interpolates
splines between the set of control points - the segment between (0,0)
and the first control point is linear, the remaining segments
quadratic with the gradients matching at the control point.
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#define STICKPROFILE_TYPE_STANDARD 1
#define STICKPROFILE_TYPE_SPLINE 2
#define STICKPROFILE_MAX_POWER 10.0
@interface OOJoystickAxisProfile : NSObject <NSCopying>
{
@private
double deadzone;
}
- (id) init;
- (id) copyWithZone: (NSZone *) zone;
- (double) rawValue: (double) x;
- (double) value: (double) x;
- (double) deadzone;
- (void) setDeadzone: (double) newValue;
@end
@interface OOJoystickStandardAxisProfile: OOJoystickAxisProfile
{
@private
double power;
double parameter;
}
- (id) init;
- (id) copyWithZone: (NSZone *) zone;
- (void) setPower: (double) newValue;
- (double) power;
- (void) setParameter: (double) newValue;
- (double) parameter;
- (double) rawValue: (double) x;
@end
@interface OOJoystickSplineAxisProfile: OOJoystickAxisProfile
{
@private
NSMutableArray *controlPoints;
NSArray *segments;
}
- (id) init;
- (void) dealloc;
- (id) copyWithZone: (NSZone *) zone;
- (int) addControl: (NSPoint) point;
- (NSPoint) pointAtIndex: (int) index;
- (int) countPoints;
- (void) removeControl: (int) index;
- (void) clearControlPoints;
- (void) moveControl: (int) index point: (NSPoint) point;
- (double) rawValue: (double) x;
- (double) gradient: (double) x;
- (NSArray *) controlPoints;
@end

View File

@ -0,0 +1,628 @@
/*
OOJoystickProfile.m
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#import "OOJoystickManager.h"
#import "OOJoystickProfile.h"
#import "OOMaths.h"
#import "OOLoggingExtended.h"
#import "Universe.h"
#define SPLINE_POINT_MIN_SPACING 0.02
@interface OOJoystickSplineSegment: NSObject <NSCopying>
{
@private
double start;
double end;
double a[4];
}
- (id) init;
// Linear spline from left point to right point. Returns nil if right.x - left.x <= 0.0.
- (id) initWithData: (NSPoint) left right: (NSPoint) right;
// Quadratic spline from left point to right point, with gradient specified at left. returns nil if right.x - left.x <= 0.0.
- (id) initWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft;
// Quadratic spline from left point to right point, with gradient specified at right. returns nil if right.x - left.x <= 0.0.
- (id) initWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright;
// Cubic spline from left point to right point, with gradients specified at end points. returns nil if right.x - left.x <= 0.0.
- (id) initWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright;
// Linear spline from left point to right point. Returns nil if right.x - left.x <= 0.0.
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right;
// Quadratic spline from left point to right point, with gradient specified at left. returns nil if right.x - left.x <= 0.0.
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft;
// Quadratic spline from left point to right point, with gradient specified at right. returns nil if right.x - left.x <= 0.0.
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright;
// Cubic spline from left point to right point, with gradients specified at end points. returns nil if right.x - left.x <= 0.0.
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright;
- (id) copyWithZone: (NSZone *) zone;
- (double) start;
- (double) end;
- (double) value: (double) t;
- (double) gradient: (double) t;
@end
@interface OOJoystickSplineAxisProfile (Private)
// Create the segments from the control points. If there's a problem, e.g. control points not in order or overlapping,
// leave segments as they are and return NO. Otherwise return YES.
- (BOOL) makeSegments;
@end
@implementation OOJoystickAxisProfile
- (id) init
{
if ((self = [super init]))
{
deadzone = STICK_DEADZONE;
}
return self;
}
- (id) copyWithZone: (NSZone *) zone
{
OOJoystickAxisProfile *copy = [[[self class] alloc] init];
return copy;
}
- (double) rawValue: (double) x
{
return x;
}
- (double) value: (double) x
{
if (fabs(x) < deadzone)
{
return 0.0;
}
return x < 0 ? -[self rawValue: (-x-deadzone)/(1.0-deadzone)] : [self rawValue: (x-deadzone)/(1.0-deadzone)];
}
- (double) deadzone
{
return deadzone;
}
- (void) setDeadzone: (double) newValue
{
deadzone = OOClamp_0_1_d(newValue);
}
@end
@implementation OOJoystickStandardAxisProfile
- (id) init
{
if ((self = [super init]))
{
power = 1.0;
parameter = 1.0;
}
return self;
}
- (id) copyWithZone: (NSZone *) zone
{
OOJoystickStandardAxisProfile *copy = [[[self class] alloc] init];
copy->power = power;
copy->parameter = parameter;
return copy;
}
- (void) setPower: (double) newValue
{
if (newValue < 1.0)
{
power = 1.0;
}
else if (newValue > STICKPROFILE_MAX_POWER)
{
power = STICKPROFILE_MAX_POWER;
}
else
{
power = newValue;
}
return;
}
- (double) power
{
return power;
}
- (void) setParameter: (double) newValue
{
parameter = OOClamp_0_1_d(newValue);
return;
}
- (double) parameter
{
return parameter;
}
- (double) rawValue: (double) x
{
if (x < 0)
{
return -OOClamp_0_1_d(parameter * pow(-x,power)-(parameter - 1.0)*(-x));
}
return OOClamp_0_1_d(parameter * pow(x,power)-(parameter - 1.0)*(x));
}
@end
@implementation OOJoystickSplineSegment
- (id) init
{
if ((self = [super init]))
{
start = 0.0;
end = 1.0;
a[0] = 0.0;
a[1] = 1.0;
a[2] = 0.0;
a[3] = 0.0;
}
return self;
}
- (id) copyWithZone: (NSZone *) zone
{
OOJoystickSplineSegment *copy = [[OOJoystickSplineSegment allocWithZone: zone] init];
copy->start = start;
copy->end = end;
copy->a[0] = a[0];
copy->a[1] = a[1];
copy->a[2] = a[2];
copy->a[3] = a[3];
return copy;
}
- (id) initWithData: (NSPoint) left right: (NSPoint) right
{
double dx = right.x - left.x;
if (dx <= 0.0)
{
return nil;
}
if ((self = [super init]))
{
start = left.x;
end = right.x;
a[1] = (right.y - left.y)/dx;
a[0] = left.y-a[1]*left.x;
a[2] = 0.0;
a[3] = 0.0;
}
return self;
}
- (id) initWithData:(NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft
{
double dx = right.x - left.x;
if (dx <= 0.0)
{
return nil;
}
if ((self = [super init]))
{
start = left.x;
end = right.x;
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;
a[1] = 2*left.x*(left.y-right.y)/(dx*dx) + gradientleft*(left.x+right.x)/dx;
a[2] = (right.y-left.y)/(dx*dx) - gradientleft/dx;
}
return self;
}
- (id) initWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright
{
double dx = right.x - left.x;
if (dx <= 0.0)
{
return nil;
}
if ((self = [super init]))
{
start = left.x;
end = right.x;
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;
a[1] = 2*right.x*(right.y-left.y)/(dx*dx) - gradientright*(left.x+right.x)/dx;
a[2] = (left.y-right.y)/(dx*dx) + gradientright/dx;
}
return self;
}
- (id) initWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright
{
double dx = right.x - left.x;
if (dx <= 0.0)
{
return nil;
}
if ((self = [super init]))
{
start = left.x;
end = right.x;
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);
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);
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);
a[3] = 2*(left.y-right.y)/(dx*dx*dx) + (gradientleft+gradientright)/(dx*dx);
}
return self;
}
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right
{
OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right];
return [segment autorelease];
}
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft
{
OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right gradientleft:gradientleft];
return [segment autorelease];
}
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientright: (double) gradientright
{
OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right gradientright:gradientright];
return [segment autorelease];
}
+ (id) segmentWithData: (NSPoint) left right: (NSPoint) right gradientleft: (double) gradientleft gradientright: (double) gradientright
{
OOJoystickSplineSegment *segment = [[OOJoystickSplineSegment alloc] initWithData: left right:right gradientleft:gradientleft gradientright:gradientright];
return [segment autorelease];
}
- (double) start
{
return start;
}
- (double) end
{
return end;
}
- (double) value: (double) x
{
return a[0] + (a[1] + (a[2] + a[3]*x)*x)*x;
}
- (double) gradient: (double) x
{
return a[1]+(2*a[2] + 3*a[3]*x)*x;
}
@end
@implementation OOJoystickSplineAxisProfile
- (id) init
{
if ((self = [super init]))
{
controlPoints = [[NSMutableArray alloc] initWithCapacity: 2];
segments = nil;
[self makeSegments];
}
return self;
}
- (void) dealloc
{
[controlPoints release];
[segments release];
[super dealloc];
return;
}
- (id) copyWithZone: (NSZone *) zone
{
OOJoystickSplineAxisProfile *copy = [[[self class] alloc] init];
copy->controlPoints = [controlPoints copyWithZone: zone];
copy->segments = [segments copyWithZone: zone];
return copy;
}
- (int) addControl: (NSPoint) point
{
NSPoint left, right;
int i;
if (point.x <= SPLINE_POINT_MIN_SPACING || point.x >= 1 - SPLINE_POINT_MIN_SPACING )
{
return -1;
}
left.x = 0.0;
left.y = 0.0;
for (i = 0; i <= [controlPoints count]; i++ )
{
if (i < [controlPoints count])
{
right = [[controlPoints objectAtIndex: i] pointValue];
}
else
{
right = NSMakePoint(1.0,1.0);
}
if ((point.x - left.x) < SPLINE_POINT_MIN_SPACING)
{
if (i == 0)
{
return -1;
}
[controlPoints replaceObjectAtIndex: i - 1 withObject: [NSValue valueWithPoint: point]];
[self makeSegments];
return i - 1;
}
if ((right.x - point.x) >= SPLINE_POINT_MIN_SPACING)
{
[controlPoints insertObject: [NSValue valueWithPoint: point] atIndex: i];
[self makeSegments];
return i;
}
left = right;
}
return -1;
}
- (NSPoint) pointAtIndex: (int) index
{
NSPoint point;
if (index < 0)
{
point.x = 0.0;
point.y = 0.0;
}
else if (index >= [controlPoints count])
{
point.x = 1.0;
point.y = 1.0;
}
else
{
point = [[controlPoints objectAtIndex: index] pointValue];
}
return point;
}
- (int) countPoints
{
return [controlPoints count];
}
- (NSArray *) controlPoints
{
return [NSArray arrayWithArray: controlPoints];
}
// Calculate segments from control points
- (BOOL) makeSegments
{
int i;
NSPoint left, right, next;
double gradientleft, gradientright;
OOJoystickSplineSegment* segment;
BOOL first_segment = YES;
NSMutableArray *new_segments = [NSMutableArray arrayWithCapacity: ([controlPoints count] + 1)];
left.x = 0.0;
left.y = 0.0;
if ([controlPoints count] == 0)
{
right.x = 1.0;
right.y = 1.0;
segment = [OOJoystickSplineSegment segmentWithData: left right: right];
[new_segments addObject:segment];
}
else
{
gradientleft = 1.0;
right = [[controlPoints objectAtIndex: 0] pointValue];
for (i = 0; i < [controlPoints count]; i++)
{
next = [self pointAtIndex: i + 1];
if (next.x - left.x > 0.0)
{
// we make the gradient at right equal to the gradient of a straight line between the neighcouring points
gradientright = (next.y - left.y)/(next.x - left.x);
if (first_segment)
{
segment = [OOJoystickSplineSegment segmentWithData: left right: right gradientright: gradientright];
}
else
{
segment = [OOJoystickSplineSegment segmentWithData: left right: right gradientleft: gradientleft gradientright: gradientright];
}
if (segment == nil)
{
return NO;
}
else
{
[new_segments addObject: segment];
gradientleft = gradientright;
first_segment = NO;
left = right;
}
}
right = next;
}
right.x = 1.0;
right.y = 1.0;
segment = [OOJoystickSplineSegment segmentWithData: left right: right gradientleft: gradientleft];
if (segment == nil)
{
return NO;
}
[new_segments addObject: segment];
}
[segments release];
segments = [[NSArray arrayWithArray: new_segments] retain];
return YES;
}
- (void) removeControl: (int) index
{
if (index >= 0 && index < [controlPoints count])
{
[controlPoints removeObjectAtIndex: index];
[self makeSegments];
}
return;
}
- (void) clearControlPoints
{
[controlPoints removeAllObjects];
[self makeSegments];
}
- (void) moveControl: (int) index point: (NSPoint) point
{
NSPoint left, right;
point.x = OOClamp_0_1_d(point.x);
point.y = OOClamp_0_1_d(point.y);
if (index < 0 || index >= [controlPoints count])
{
return;
}
if (index == 0)
{
left.x = 0.0;
right.x = 0.0;
}
else
{
left = [[controlPoints objectAtIndex: (index-1)] pointValue];
}
if (index == [controlPoints count] - 1)
{
right.x = 1.0;
right.y = 1.0;
}
else
{
right = [[controlPoints objectAtIndex: (index+1)] pointValue];
}
// preserve order of control points - if we attempt to move this control point beyond
// either of its neighbours, move it back inside. Also keep neighbours a distance of at least SPLINE_POINT_MIN_SPACING apart
if (point.x - left.x < SPLINE_POINT_MIN_SPACING)
{
point.x = left.x + SPLINE_POINT_MIN_SPACING;
if (right.x - point.x < SPLINE_POINT_MIN_SPACING)
{
point.x = (left.x + right.x)/2;
}
}
else if (right.x - point.x < SPLINE_POINT_MIN_SPACING)
{
point.x = right.x - SPLINE_POINT_MIN_SPACING;
if (point.x - left.x < SPLINE_POINT_MIN_SPACING)
{
point.x = (left.x + right.x)/2;
}
}
[controlPoints replaceObjectAtIndex: index withObject: [NSValue valueWithPoint: point]];
[self makeSegments];
return;
}
- (double) rawValue: (double) x
{
int i;
OOJoystickSplineSegment *segment;
double sign;
if (x < 0)
{
sign = -1.0;
x = -x;
}
else
{
sign = 1.0;
}
for (i = 0; i < [segments count]; i++)
{
segment = [segments objectAtIndex: i];
if ([segment end] > x)
{
return sign * OOClamp_0_1_d([segment value:x]);
}
}
return 1.0;
}
- (double) gradient: (double) x
{
int i;
OOJoystickSplineSegment *segment;
for (i = 0; i < [segments count]; i++)
{
segment = [segments objectAtIndex: i];
if ([segment end] > x)
{
return [segment gradient:x];
}
}
return 1.0;
}
@end

View File

@ -18,6 +18,7 @@ ENTRY(GUI_SCREEN_LOAD, -1)
ENTRY(GUI_SCREEN_SAVE, -1)
ENTRY(GUI_SCREEN_SAVE_OVERWRITE, -1)
ENTRY(GUI_SCREEN_STICKMAPPER, -1)
ENTRY(GUI_SCREEN_STICKPROFILE, -1)
ENTRY(GUI_SCREEN_MISSION, -1)
ENTRY(GUI_SCREEN_REPORT, -1)
ENTRY(GUI_SCREEN_INTERFACES, -1)