Joystick updates - Adds invert x/y to joystick (Thanks @alxgibsn) - Adds the invert to Flutter and C++ runtimes. - Updates Cpp core generator to latest Flutter+NNBD. - Fixes warnings and errors from latest Flutter (Dart SDK really). Diffs= a4fb3dc7d Joystick updates (#5261)
diff --git a/.rive_head b/.rive_head index 38bffa2..9c8e55f 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -8f8e06b9e56bc373e55a7f9b940d0457fe119200 +a4fb3dc7decb50f6965e21ebced098cbdc35883d
diff --git a/dev/core_generator/lib/main.dart b/dev/core_generator/lib/main.dart index a48945c..1d433d9 100644 --- a/dev/core_generator/lib/main.dart +++ b/dev/core_generator/lib/main.dart
@@ -9,7 +9,7 @@ Directory(defsPath).list(recursive: true).listen( (entity) { if (entity is File && entity.path.toLowerCase().endsWith('.json')) { - Definition(entity.path.substring(defsPath.length)); + Definition.make(entity.path.substring(defsPath.length)); } }, onDone: Definition.generate,
diff --git a/dev/core_generator/lib/src/definition.dart b/dev/core_generator/lib/src/definition.dart index 44a479b..596b969 100644 --- a/dev/core_generator/lib/src/definition.dart +++ b/dev/core_generator/lib/src/definition.dart
@@ -19,20 +19,20 @@ final String _filename; static final _formatter = CppFormatter(); - String _name; + String? _name; final List<Property> _properties = []; List<Property> get properties => _properties .where((property) => property.isRuntime) .toList(growable: false); - Definition _extensionOf; - Key _key; + Definition? _extensionOf; + Key? _key; bool _isAbstract = false; bool _editorOnly = false; bool _forRuntime = true; bool get forRuntime => _forRuntime; - factory Definition(String filename) { + static Definition? make(String filename) { var definition = definitions[filename]; if (definition != null) { return definition; @@ -40,7 +40,7 @@ var file = File(defsPath + filename); var contents = file.readAsStringSync(); - Map<String, dynamic> definitionData; + late Map<String, dynamic> definitionData; try { dynamic parsedJson = json.decode(contents); if (parsedJson is Map<String, dynamic>) { @@ -54,10 +54,11 @@ definition = Definition.fromFilename(filename, definitionData); return definition; } + Definition.fromFilename(this._filename, Map<String, dynamic> data) { dynamic extendsFilename = data['extends']; if (extendsFilename is String) { - _extensionOf = Definition(extendsFilename); + _extensionOf = Definition.make(extendsFilename); } dynamic nameValue = data['name']; if (nameValue is String) { @@ -81,8 +82,8 @@ if (properties is Map<String, dynamic>) { for (final MapEntry<String, dynamic> entry in properties.entries) { if (entry.value is Map<String, dynamic>) { - var property = - Property(this, entry.key, entry.value as Map<String, dynamic>); + var property = Property.make( + this, entry.key, entry.value as Map<String, dynamic>); if (property == null) { continue; } @@ -95,7 +96,7 @@ ? _filename.substring(defsPath.length) : _filename; - String get name => _name; + String? get name => _name; String get localCodeFilename => '${stripExtension(_filename)}_base.hpp'; String get concreteCodeFilename => 'rive/${stripExtension(_filename)}.hpp'; @@ -112,11 +113,12 @@ var includes = <String>{ defineContextExtension ? 'rive/core.hpp' - : _extensionOf.concreteCodeFilename + : _extensionOf!.concreteCodeFilename }; for (final property in properties) { - if (property.type.include != null) { - includes.add(property.type.include); + var include = property.type.include; + if (include != null) { + includes.add(include); } includes.add('rive/core/field_types/' + property.type.snakeRuntimeCoreName + @@ -141,7 +143,7 @@ code.writeln('protected:'); code.writeln('typedef $superTypeName Super;'); code.writeln('public:'); - code.writeln('static const uint16_t typeKey = ${_key.intValue};\n'); + code.writeln('static const uint16_t typeKey = ${_key!.intValue};\n'); code.write(comment( 'Helper to quickly determine if a core object extends another ' @@ -163,7 +165,7 @@ if (properties.isNotEmpty) { for (final property in properties) { code.writeln('static const uint16_t ${property.name}PropertyKey = ' - '${property.key.intValue};'); + '${property.key!.intValue};'); } if (properties.any((prop) => !prop.isEncoded)) { code.writeln('private:'); @@ -244,7 +246,7 @@ } } if (_extensionOf != null) { - code.writeln('${_extensionOf.name}::' + code.writeln('${_extensionOf!.name}::' 'copy(object); '); } code.writeln('}'); @@ -269,7 +271,7 @@ code.writeln('}'); } if (_extensionOf != null) { - code.writeln('return ${_extensionOf.name}::' + code.writeln('return ${_extensionOf!.name}::' 'deserialize(propertyKey, reader); }'); } else { code.writeln('return false; }'); @@ -306,7 +308,7 @@ concreteCode.writeln('}'); var formattedCode = - await _formatter.formatAndGuard(_name, concreteCode.toString()); + await _formatter.formatAndGuard(_name!, concreteCode.toString()); concreteFile.writeAsStringSync(formattedCode, flush: true); } if (!_isAbstract) { @@ -341,34 +343,34 @@ Map<int, Property> properties = {}; for (final definition in definitions.values) { if (definition._key?.intValue != null) { - var other = ids[definition._key.intValue]; + var other = ids[definition._key!.intValue]; if (other != null) { color('Duplicate type ids for $definition and $other.', front: Styles.RED); runGenerator = false; } else { - ids[definition._key.intValue] = definition; + ids[definition._key!.intValue!] = definition; } } for (final property in definition._properties) { - if (property.key.isMissing) { + if (property.key!.isMissing) { continue; } - var other = properties[property.key.intValue]; + var other = properties[property.key!.intValue]; if (other != null) { color( '''Duplicate field ids for ${property.definition}.$property ''' '''and ${other.definition}.$other.''', front: Styles.RED); runGenerator = false; - } else if (property.key.intValue < minPropertyId) { + } else if (property.key!.intValue! < minPropertyId) { color( '${property.definition}.$property: ids less than ' '$minPropertyId are reserved.', front: Styles.RED); runGenerator = false; } else { - properties[property.key.intValue] = property; + properties[property.key!.intValue!] = property; } } } @@ -377,16 +379,14 @@ int nextFieldId = minPropertyId - 1; int nextId = 0; for (final definition in definitions.values) { - if (definition._key != null && - definition._key.intValue != null && - definition._key.intValue > nextId) { - nextId = definition._key.intValue; + var intValue = definition._key?.intValue; + if (intValue != null && intValue > nextId) { + nextId = intValue; } for (final field in definition._properties) { - if (field != null && - field.key.intValue != null && - field.key.intValue > nextFieldId) { - nextFieldId = field.key.intValue; + var intValue = field.key?.intValue; + if (intValue != null && intValue > nextFieldId) { + nextFieldId = intValue; } } } @@ -440,10 +440,10 @@ for (final definition in runtimeDefinitions) { for (final property in definition.properties) { usedFieldTypes[property.type] ??= []; - usedFieldTypes[property.type].add(property); + usedFieldTypes[property.type]!.add(property); if (!property.isEncoded) { getSetFieldTypes[property.type] ??= []; - getSetFieldTypes[property.type].add(property); + getSetFieldTypes[property.type]!.add(property); } } } @@ -453,12 +453,14 @@ 'int propertyKey, ${fieldType.cppName} value){'); ctxCode.writeln('switch (propertyKey) {'); var properties = getSetFieldTypes[fieldType]; - for (final property in properties) { - ctxCode.writeln('case ${property.definition.name}Base' - '::${property.name}PropertyKey:'); - ctxCode.writeln('object->as<${property.definition.name}Base>()->' - '${property.name}(value);'); - ctxCode.writeln('break;'); + if (properties != null) { + for (final property in properties) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${property.name}PropertyKey:'); + ctxCode.writeln('object->as<${property.definition.name}Base>()->' + '${property.name}(value);'); + ctxCode.writeln('break;'); + } } ctxCode.writeln('}}'); } @@ -468,11 +470,14 @@ 'Core* object, int propertyKey){'); ctxCode.writeln('switch (propertyKey) {'); var properties = getSetFieldTypes[fieldType]; - for (final property in properties) { - ctxCode.writeln('case ${property.definition.name}Base' - '::${property.name}PropertyKey:'); - ctxCode.writeln('return object->as<${property.definition.name}Base>()->' - '${property.name}();'); + if (properties != null) { + for (final property in properties) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${property.name}PropertyKey:'); + ctxCode + .writeln('return object->as<${property.definition.name}Base>()->' + '${property.name}();'); + } } ctxCode.writeln('}'); ctxCode.writeln('return ${fieldType.defaultValue ?? 'nullptr'};'); @@ -484,9 +489,11 @@ for (final fieldType in usedFieldTypes.keys) { var properties = usedFieldTypes[fieldType]; - for (final property in properties) { - ctxCode.writeln('case ${property.definition.name}Base' - '::${property.name}PropertyKey:'); + if (properties != null) { + for (final property in properties) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${property.name}PropertyKey:'); + } } ctxCode.writeln('return Core${fieldType.capitalizedName}Type::id;'); } @@ -496,10 +503,9 @@ ctxCode.writeln('};}'); var output = generatedHppPath; - var folder = - output != null && output.isNotEmpty && output[output.length - 1] == '/' - ? output.substring(0, output.length - 1) - : output; + var folder = output.isNotEmpty && output[output.length - 1] == '/' + ? output.substring(0, output.length - 1) + : output; var file = File('$folder/core_registry.hpp'); file.createSync(recursive: true);
diff --git a/dev/core_generator/lib/src/field_type.dart b/dev/core_generator/lib/src/field_type.dart index 486bd92..60f3025 100644 --- a/dev/core_generator/lib/src/field_type.dart +++ b/dev/core_generator/lib/src/field_type.dart
@@ -8,26 +8,25 @@ abstract class FieldType { final String name; - String _cppName; - final String include; - String get cppName => _cppName; - String get cppGetterName => _cppName; + String? _cppName; + final String? include; + String? get cppName => _cppName; + String? get cppGetterName => _cppName; - String _runtimeCoreType; + final String _runtimeCoreType; String get runtimeCoreType => _runtimeCoreType; FieldType( this.name, - String runtimeCoreType, { - String cppName, + this._runtimeCoreType, { + String? cppName, this.include, }) { _cppName = cppName ?? name; - _runtimeCoreType = runtimeCoreType; _types[name] = this; } - static FieldType find(dynamic key) { + static FieldType? find(dynamic key) { if (key is! String) { return null; } @@ -40,10 +39,10 @@ } String equalityCheck(String varAName, String varBName) { - return "$varAName == $varBName"; + return '$varAName == $varBName'; } - String get defaultValue => null; + String? get defaultValue => null; String get uncapitalizedName => '${name[0].toLowerCase()}${name.substring(1)}' .replaceAll('<', '') @@ -61,5 +60,5 @@ .replaceAllMapped(RegExp('(.+?)([A-Z])'), (Match m) => '${m[1]}_${m[2]}') .toLowerCase(); - String convertCpp(String value) => value; + String? convertCpp(String value) => value; }
diff --git a/dev/core_generator/lib/src/field_types/bytes_field_type.dart b/dev/core_generator/lib/src/field_types/bytes_field_type.dart index 163772b..3ff7f55 100644 --- a/dev/core_generator/lib/src/field_types/bytes_field_type.dart +++ b/dev/core_generator/lib/src/field_types/bytes_field_type.dart
@@ -16,7 +16,7 @@ String get cppGetterName => 'Span<const uint8_t>'; @override - String convertCpp(String value) { + String? convertCpp(String value) { return null; } }
diff --git a/dev/core_generator/lib/src/field_types/double_field_type.dart b/dev/core_generator/lib/src/field_types/double_field_type.dart index 69e4f49..1f565bb 100644 --- a/dev/core_generator/lib/src/field_types/double_field_type.dart +++ b/dev/core_generator/lib/src/field_types/double_field_type.dart
@@ -7,7 +7,7 @@ String get defaultValue => '0.0f'; @override - String convertCpp(String value) { + String? convertCpp(String value) { var result = value; if (result.isNotEmpty) { if (result[result.length - 1] != 'f') {
diff --git a/dev/core_generator/lib/src/field_types/initialize.dart b/dev/core_generator/lib/src/field_types/initialize.dart index 01c0b2a..5d37260 100644 --- a/dev/core_generator/lib/src/field_types/initialize.dart +++ b/dev/core_generator/lib/src/field_types/initialize.dart
@@ -3,7 +3,7 @@ import 'package:core_generator/src/field_type.dart'; import 'package:core_generator/src/field_types/bytes_field_type.dart'; -List<FieldType> fields; +late List<FieldType> fields; void initializeFields() { fields = [
diff --git a/dev/core_generator/lib/src/field_types/string_field_type.dart b/dev/core_generator/lib/src/field_types/string_field_type.dart index db4842f..3fb6d9f 100644 --- a/dev/core_generator/lib/src/field_types/string_field_type.dart +++ b/dev/core_generator/lib/src/field_types/string_field_type.dart
@@ -11,7 +11,7 @@ String get cppGetterName => 'const std::string&'; @override - String convertCpp(String value) { + String? convertCpp(String value) { var result = value; if (result.length > 1) { if (result[0] == '\'') {
diff --git a/dev/core_generator/lib/src/field_types/uint_field_type.dart b/dev/core_generator/lib/src/field_types/uint_field_type.dart index daa2457..df9b751 100644 --- a/dev/core_generator/lib/src/field_types/uint_field_type.dart +++ b/dev/core_generator/lib/src/field_types/uint_field_type.dart
@@ -13,5 +13,6 @@ // We do this to fix up CoreContext.invalidProperyKey @override - String convertCpp(String value) => value.replaceAll('CoreContext.', 'Core::'); + String? convertCpp(String value) => + value.replaceAll('CoreContext.', 'Core::'); }
diff --git a/dev/core_generator/lib/src/key.dart b/dev/core_generator/lib/src/key.dart index a455656..8e1a8d0 100644 --- a/dev/core_generator/lib/src/key.dart +++ b/dev/core_generator/lib/src/key.dart
@@ -2,14 +2,14 @@ import 'property.dart'; class Key { - final String stringValue; - final int intValue; + final String? stringValue; + final int? intValue; bool get isMissing => intValue == null; Key(this.stringValue, this.intValue); Key.forDefinition(Definition def) - : stringValue = def.name.toLowerCase(), + : stringValue = def.name?.toLowerCase(), intValue = null; Key.forProperty(Property field) : stringValue = field.name.toLowerCase(), @@ -17,7 +17,7 @@ Key withIntValue(int id) => Key(stringValue, id); - factory Key.fromJSON(dynamic data) { + static Key? fromJSON(dynamic data) { if (data is! Map<String, dynamic>) { return null; }
diff --git a/dev/core_generator/lib/src/property.dart b/dev/core_generator/lib/src/property.dart index 76b3be8..e13ae67 100644 --- a/dev/core_generator/lib/src/property.dart +++ b/dev/core_generator/lib/src/property.dart
@@ -7,22 +7,23 @@ final String name; final FieldType type; final Definition definition; - String initialValue; - String initialValueRuntime; + String? initialValue; + String? initialValueRuntime; bool isVirtual = false; bool animates = false; - String group; - Key key; - String description; + String? group; + Key? key; + String? description; bool isNullable = false; bool isRuntime = true; bool isCoop = true; bool isSetOverride = false; bool isGetOverride = false; bool isEncoded = false; - FieldType typeRuntime; + FieldType? typeRuntime; - factory Property(Definition type, String name, Map<String, dynamic> data) { + static Property? make( + Definition type, String name, Map<String, dynamic> data) { if (data['runtime'] is bool && data['runtime'] == false) { return null; } @@ -34,11 +35,10 @@ color('Invalid field type ${data['type']} for $name.', front: Styles.RED); return null; } - return Property.make(type, name, fieldType, data); + return Property(type, name, fieldType, data); } - Property.make( - this.definition, this.name, this.type, Map<String, dynamic> data) { + Property(this.definition, this.name, this.type, Map<String, dynamic> data) { dynamic encodedValue = data['encoded']; if (encodedValue is bool) { isEncoded = encodedValue; @@ -99,9 +99,7 @@ FieldType getExportType() => typeRuntime ?? type; @override - String toString() { - return '$name(${key.intValue})'; - } + String toString() => '$name(${key?.intValue})'; String get capitalizedName => '${name[0].toUpperCase()}${name.substring(1)}' .replaceAll('<', '')
diff --git a/dev/core_generator/pubspec.yaml b/dev/core_generator/pubspec.yaml index 6e7eb7c..5227dc5 100644 --- a/dev/core_generator/pubspec.yaml +++ b/dev/core_generator/pubspec.yaml
@@ -1,7 +1,7 @@ name: core_generator environment: - sdk: ">=2.6.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: - colorize: ^2.0.0 + colorize: ^3.0.0
diff --git a/dev/defs/joystick.json b/dev/defs/joystick.json index 4ef5e38..17137f0 100644 --- a/dev/defs/joystick.json +++ b/dev/defs/joystick.json
@@ -32,8 +32,7 @@ "key": { "int": 303, "string": "posx" - }, - "runtime": false + } }, "posY": { "type": "double", @@ -41,8 +40,7 @@ "key": { "int": 304, "string": "posy" - }, - "runtime": false + } }, "originX": { "type": "double", @@ -51,8 +49,7 @@ "int": 307, "string": "originx" }, - "description": "Origin x in normalized coordinates (0.5 = center, 0 = left, 1 = right).", - "runtime": false + "description": "Origin x in normalized coordinates (0.5 = center, 0 = left, 1 = right)." }, "originY": { "type": "double", @@ -61,8 +58,7 @@ "int": 308, "string": "originy" }, - "description": "Origin y in normalized coordinates (0.5 = center, 0 = top, 1 = bottom).", - "runtime": false + "description": "Origin y in normalized coordinates (0.5 = center, 0 = top, 1 = bottom)." }, "width": { "type": "double", @@ -70,8 +66,7 @@ "key": { "int": 305, "string": "width" - }, - "runtime": false + } }, "height": { "type": "double", @@ -79,8 +74,7 @@ "key": { "int": 306, "string": "height" - }, - "runtime": false + } }, "xId": { "type": "Id", @@ -103,6 +97,25 @@ "string": "y_id" }, "description": "Identifier used to track the animation used for the y axis of the joystick." + }, + "joystickFlags": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 312, + "string": "joystickflags" + } + }, + "handleSourceId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 313, + "string": "handlesourceid" + }, + "description": "Identifier used to track the custom handle source of the joystick." } } } \ No newline at end of file
diff --git a/include/rive/animation/blend_state_1d.hpp b/include/rive/animation/blend_state_1d.hpp index ebe394a..8b3092d 100644 --- a/include/rive/animation/blend_state_1d.hpp +++ b/include/rive/animation/blend_state_1d.hpp
@@ -7,10 +7,7 @@ class BlendState1D : public BlendState1DBase { public: - // -1 (4294967295) is our flag value for input not set. It means it wasn't set at edit - // time. - const uint32_t noInputSpecified = -1; - bool hasValidInputId() const { return inputId() != noInputSpecified; } + bool hasValidInputId() const { return inputId() != Core::emptyId; } StatusCode import(ImportStack& importStack) override;
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 39e9034..b53430c 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -43,6 +43,7 @@ std::vector<DrawTarget*> m_DrawTargets; std::vector<NestedArtboard*> m_NestedArtboards; std::vector<Joystick*> m_Joysticks; + bool m_JoysticksApplyBeforeUpdate = true; unsigned int m_DirtDepth = 0; std::unique_ptr<RenderPath> m_BackgroundPath;
diff --git a/include/rive/core.hpp b/include/rive/core.hpp index 0b5567f..b397d49 100644 --- a/include/rive/core.hpp +++ b/include/rive/core.hpp
@@ -12,6 +12,7 @@ class Core { public: + const uint32_t emptyId = -1; static const int invalidPropertyKey = 0; virtual ~Core() {} virtual uint16_t coreType() const = 0;
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index 3dd4042..5ba4c82 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -517,6 +517,12 @@ case JoystickBase::yIdPropertyKey: object->as<JoystickBase>()->yId(value); break; + case JoystickBase::joystickFlagsPropertyKey: + object->as<JoystickBase>()->joystickFlags(value); + break; + case JoystickBase::handleSourceIdPropertyKey: + object->as<JoystickBase>()->handleSourceId(value); + break; case WeightBase::valuesPropertyKey: object->as<WeightBase>()->values(value); break; @@ -781,6 +787,24 @@ case JoystickBase::yPropertyKey: object->as<JoystickBase>()->y(value); break; + case JoystickBase::posXPropertyKey: + object->as<JoystickBase>()->posX(value); + break; + case JoystickBase::posYPropertyKey: + object->as<JoystickBase>()->posY(value); + break; + case JoystickBase::originXPropertyKey: + object->as<JoystickBase>()->originX(value); + break; + case JoystickBase::originYPropertyKey: + object->as<JoystickBase>()->originY(value); + break; + case JoystickBase::widthPropertyKey: + object->as<JoystickBase>()->width(value); + break; + case JoystickBase::heightPropertyKey: + object->as<JoystickBase>()->height(value); + break; case BoneBase::lengthPropertyKey: object->as<BoneBase>()->length(value); break; @@ -1059,6 +1083,10 @@ return object->as<JoystickBase>()->xId(); case JoystickBase::yIdPropertyKey: return object->as<JoystickBase>()->yId(); + case JoystickBase::joystickFlagsPropertyKey: + return object->as<JoystickBase>()->joystickFlags(); + case JoystickBase::handleSourceIdPropertyKey: + return object->as<JoystickBase>()->handleSourceId(); case WeightBase::valuesPropertyKey: return object->as<WeightBase>()->values(); case WeightBase::indicesPropertyKey: @@ -1238,6 +1266,18 @@ return object->as<JoystickBase>()->x(); case JoystickBase::yPropertyKey: return object->as<JoystickBase>()->y(); + case JoystickBase::posXPropertyKey: + return object->as<JoystickBase>()->posX(); + case JoystickBase::posYPropertyKey: + return object->as<JoystickBase>()->posY(); + case JoystickBase::originXPropertyKey: + return object->as<JoystickBase>()->originX(); + case JoystickBase::originYPropertyKey: + return object->as<JoystickBase>()->originY(); + case JoystickBase::widthPropertyKey: + return object->as<JoystickBase>()->width(); + case JoystickBase::heightPropertyKey: + return object->as<JoystickBase>()->height(); case BoneBase::lengthPropertyKey: return object->as<BoneBase>()->length(); case RootBoneBase::xPropertyKey: @@ -1408,6 +1448,8 @@ case ArtboardBase::defaultStateMachineIdPropertyKey: case JoystickBase::xIdPropertyKey: case JoystickBase::yIdPropertyKey: + case JoystickBase::joystickFlagsPropertyKey: + case JoystickBase::handleSourceIdPropertyKey: case WeightBase::valuesPropertyKey: case WeightBase::indicesPropertyKey: case TendonBase::boneIdPropertyKey: @@ -1495,6 +1537,12 @@ case ArtboardBase::originYPropertyKey: case JoystickBase::xPropertyKey: case JoystickBase::yPropertyKey: + case JoystickBase::posXPropertyKey: + case JoystickBase::posYPropertyKey: + case JoystickBase::originXPropertyKey: + case JoystickBase::originYPropertyKey: + case JoystickBase::widthPropertyKey: + case JoystickBase::heightPropertyKey: case BoneBase::lengthPropertyKey: case RootBoneBase::xPropertyKey: case RootBoneBase::yPropertyKey:
diff --git a/include/rive/generated/joystick_base.hpp b/include/rive/generated/joystick_base.hpp index 401188e..d32b1a4 100644 --- a/include/rive/generated/joystick_base.hpp +++ b/include/rive/generated/joystick_base.hpp
@@ -31,14 +31,30 @@ static const uint16_t xPropertyKey = 299; static const uint16_t yPropertyKey = 300; + static const uint16_t posXPropertyKey = 303; + static const uint16_t posYPropertyKey = 304; + static const uint16_t originXPropertyKey = 307; + static const uint16_t originYPropertyKey = 308; + static const uint16_t widthPropertyKey = 305; + static const uint16_t heightPropertyKey = 306; static const uint16_t xIdPropertyKey = 301; static const uint16_t yIdPropertyKey = 302; + static const uint16_t joystickFlagsPropertyKey = 312; + static const uint16_t handleSourceIdPropertyKey = 313; private: float m_X = 0.0f; float m_Y = 0.0f; + float m_PosX = 0.0f; + float m_PosY = 0.0f; + float m_OriginX = 0.5f; + float m_OriginY = 0.5f; + float m_Width = 100.0f; + float m_Height = 100.0f; uint32_t m_XId = -1; uint32_t m_YId = -1; + uint32_t m_JoystickFlags = 0; + uint32_t m_HandleSourceId = -1; public: inline float x() const { return m_X; } @@ -63,6 +79,72 @@ yChanged(); } + inline float posX() const { return m_PosX; } + void posX(float value) + { + if (m_PosX == value) + { + return; + } + m_PosX = value; + posXChanged(); + } + + inline float posY() const { return m_PosY; } + void posY(float value) + { + if (m_PosY == value) + { + return; + } + m_PosY = value; + posYChanged(); + } + + inline float originX() const { return m_OriginX; } + void originX(float value) + { + if (m_OriginX == value) + { + return; + } + m_OriginX = value; + originXChanged(); + } + + inline float originY() const { return m_OriginY; } + void originY(float value) + { + if (m_OriginY == value) + { + return; + } + m_OriginY = value; + originYChanged(); + } + + inline float width() const { return m_Width; } + void width(float value) + { + if (m_Width == value) + { + return; + } + m_Width = value; + widthChanged(); + } + + inline float height() const { return m_Height; } + void height(float value) + { + if (m_Height == value) + { + return; + } + m_Height = value; + heightChanged(); + } + inline uint32_t xId() const { return m_XId; } void xId(uint32_t value) { @@ -85,13 +167,43 @@ yIdChanged(); } + inline uint32_t joystickFlags() const { return m_JoystickFlags; } + void joystickFlags(uint32_t value) + { + if (m_JoystickFlags == value) + { + return; + } + m_JoystickFlags = value; + joystickFlagsChanged(); + } + + inline uint32_t handleSourceId() const { return m_HandleSourceId; } + void handleSourceId(uint32_t value) + { + if (m_HandleSourceId == value) + { + return; + } + m_HandleSourceId = value; + handleSourceIdChanged(); + } + Core* clone() const override; void copy(const JoystickBase& object) { m_X = object.m_X; m_Y = object.m_Y; + m_PosX = object.m_PosX; + m_PosY = object.m_PosY; + m_OriginX = object.m_OriginX; + m_OriginY = object.m_OriginY; + m_Width = object.m_Width; + m_Height = object.m_Height; m_XId = object.m_XId; m_YId = object.m_YId; + m_JoystickFlags = object.m_JoystickFlags; + m_HandleSourceId = object.m_HandleSourceId; Component::copy(object); } @@ -105,12 +217,36 @@ case yPropertyKey: m_Y = CoreDoubleType::deserialize(reader); return true; + case posXPropertyKey: + m_PosX = CoreDoubleType::deserialize(reader); + return true; + case posYPropertyKey: + m_PosY = CoreDoubleType::deserialize(reader); + return true; + case originXPropertyKey: + m_OriginX = CoreDoubleType::deserialize(reader); + return true; + case originYPropertyKey: + m_OriginY = CoreDoubleType::deserialize(reader); + return true; + case widthPropertyKey: + m_Width = CoreDoubleType::deserialize(reader); + return true; + case heightPropertyKey: + m_Height = CoreDoubleType::deserialize(reader); + return true; case xIdPropertyKey: m_XId = CoreUintType::deserialize(reader); return true; case yIdPropertyKey: m_YId = CoreUintType::deserialize(reader); return true; + case joystickFlagsPropertyKey: + m_JoystickFlags = CoreUintType::deserialize(reader); + return true; + case handleSourceIdPropertyKey: + m_HandleSourceId = CoreUintType::deserialize(reader); + return true; } return Component::deserialize(propertyKey, reader); } @@ -118,8 +254,16 @@ protected: virtual void xChanged() {} virtual void yChanged() {} + virtual void posXChanged() {} + virtual void posYChanged() {} + virtual void originXChanged() {} + virtual void originYChanged() {} + virtual void widthChanged() {} + virtual void heightChanged() {} virtual void xIdChanged() {} virtual void yIdChanged() {} + virtual void joystickFlagsChanged() {} + virtual void handleSourceIdChanged() {} }; } // namespace rive
diff --git a/include/rive/joystick.hpp b/include/rive/joystick.hpp index d076a14..e75556e 100644 --- a/include/rive/joystick.hpp +++ b/include/rive/joystick.hpp
@@ -1,20 +1,39 @@ #ifndef _RIVE_JOYSTICK_HPP_ #define _RIVE_JOYSTICK_HPP_ #include "rive/generated/joystick_base.hpp" +#include "rive/joystick_flags.hpp" +#include "rive/math/mat2d.hpp" #include <stdio.h> + namespace rive { class Artboard; class LinearAnimation; +class TransformComponent; class Joystick : public JoystickBase { public: + void update(ComponentDirt value) override; void apply(Artboard* artboard) const; StatusCode onAddedClean(CoreContext* context) override; + StatusCode onAddedDirty(CoreContext* context) override; + + bool isJoystickFlagged(JoystickFlags flag) const + { + return (((JoystickFlags)joystickFlags()) & flag) == flag; + } + + bool canApplyBeforeUpdate() const { return m_handleSource == nullptr; } + +protected: + void buildDependencies() override; private: + Mat2D m_worldTransform; + Mat2D m_inverseWorldTransform; LinearAnimation* m_xAnimation = nullptr; LinearAnimation* m_yAnimation = nullptr; + TransformComponent* m_handleSource = nullptr; }; } // namespace rive
diff --git a/include/rive/joystick_flags.hpp b/include/rive/joystick_flags.hpp new file mode 100644 index 0000000..d3e73cd --- /dev/null +++ b/include/rive/joystick_flags.hpp
@@ -0,0 +1,64 @@ +#ifndef _RIVE_JOYSTICK_FLAGS_HPP_ +#define _RIVE_JOYSTICK_FLAGS_HPP_ +namespace rive +{ +enum class JoystickFlags : unsigned char +{ + /// Whether to invert the application of the x axis. + invertX = 1 << 0, + + /// Whether to invert the application of the y axis. + invertY = 1 << 1, + + /// Whether this Joystick works in world space. + worldSpace = 1 << 2 +}; + +inline constexpr JoystickFlags operator&(JoystickFlags lhs, JoystickFlags rhs) +{ + return static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) & + static_cast<std::underlying_type<JoystickFlags>::type>(rhs)); +} + +inline constexpr JoystickFlags operator^(JoystickFlags lhs, JoystickFlags rhs) +{ + return static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) ^ + static_cast<std::underlying_type<JoystickFlags>::type>(rhs)); +} + +inline constexpr JoystickFlags operator|(JoystickFlags lhs, JoystickFlags rhs) +{ + return static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) | + static_cast<std::underlying_type<JoystickFlags>::type>(rhs)); +} + +inline constexpr JoystickFlags operator~(JoystickFlags rhs) +{ + return static_cast<JoystickFlags>(~static_cast<std::underlying_type<JoystickFlags>::type>(rhs)); +} + +inline JoystickFlags& operator|=(JoystickFlags& lhs, JoystickFlags rhs) +{ + lhs = static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) | + static_cast<std::underlying_type<JoystickFlags>::type>(rhs)); + + return lhs; +} + +inline JoystickFlags& operator&=(JoystickFlags& lhs, JoystickFlags rhs) +{ + lhs = static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) & + static_cast<std::underlying_type<JoystickFlags>::type>(rhs)); + + return lhs; +} + +inline JoystickFlags& operator^=(JoystickFlags& lhs, JoystickFlags rhs) +{ + lhs = static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) ^ + static_cast<std::underlying_type<JoystickFlags>::type>(rhs)); + + return lhs; +} +} // namespace rive +#endif \ No newline at end of file
diff --git a/include/rive/math/aabb.hpp b/include/rive/math/aabb.hpp index 81c1c6a..5d0ef5d 100644 --- a/include/rive/math/aabb.hpp +++ b/include/rive/math/aabb.hpp
@@ -90,6 +90,12 @@ static void join(AABB& out, const AABB& a, const AABB& b); void expand(const AABB& other) { join(*this, *this, other); } + + Vec2D factorFrom(Vec2D point) const + { + return Vec2D((point.x - left()) * 2.0f / width() - 1.0f, + (point.y - top()) * 2.0f / height() - 1.0f); + } }; } // namespace rive
diff --git a/src/artboard.cpp b/src/artboard.cpp index ed104ee..2c5b233 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp
@@ -150,8 +150,15 @@ break; case JoystickBase::typeKey: - m_Joysticks.push_back(object->as<Joystick>()); + { + Joystick* joystick = object->as<Joystick>(); + if (!joystick->canApplyBeforeUpdate()) + { + m_JoysticksApplyBeforeUpdate = false; + } + m_Joysticks.push_back(joystick); break; + } } } @@ -465,12 +472,23 @@ bool Artboard::advance(double elapsedSeconds) { - for (auto joystick : m_Joysticks) + if (m_JoysticksApplyBeforeUpdate) { - joystick->apply(this); + for (auto joystick : m_Joysticks) + { + joystick->apply(this); + } } bool didUpdate = updateComponents(); + if (!m_JoysticksApplyBeforeUpdate) + { + for (auto joystick : m_Joysticks) + { + joystick->apply(this); + } + updateComponents(); + } for (auto nestedArtboard : m_NestedArtboards) { if (nestedArtboard->advance((float)elapsedSeconds))
diff --git a/src/joystick.cpp b/src/joystick.cpp index 5a61281..334face 100644 --- a/src/joystick.cpp +++ b/src/joystick.cpp
@@ -1,23 +1,94 @@ #include "rive/joystick.hpp" #include "rive/artboard.hpp" +#include "rive/transform_component.hpp" using namespace rive; +StatusCode Joystick::onAddedDirty(CoreContext* context) +{ + StatusCode status = Super::onAddedDirty(context); + if (status != StatusCode::Ok) + { + return status; + } + if (handleSourceId() != Core::emptyId) + { + auto coreObject = context->resolve(handleSourceId()); + if (coreObject == nullptr || !coreObject->is<TransformComponent>()) + { + return StatusCode::MissingObject; + } + m_handleSource = static_cast<TransformComponent*>(coreObject); + } + + return StatusCode::Ok; +} + StatusCode Joystick::onAddedClean(CoreContext* context) { m_xAnimation = artboard()->animation(xId()); m_yAnimation = artboard()->animation(yId()); + return StatusCode::Ok; } +void Joystick::buildDependencies() +{ + // We only need to update if we're in world space (and that is only required + // at runtime if we have a custom handle source). + if (m_handleSource != nullptr && parent() != nullptr) + { + parent()->addDependent(this); + m_handleSource->addDependent(this); + } +} + +void Joystick::update(ComponentDirt value) +{ + if (m_handleSource == nullptr) + { + return; + } + if (hasDirt(value, ComponentDirt::WorldTransform | ComponentDirt::Transform)) + { + Mat2D world = Mat2D::fromTranslate(posX(), posY()); + if (parent() != nullptr && parent()->is<WorldTransformComponent>()) + { + world = parent()->as<WorldTransformComponent>()->worldTransform() * world; + } + + if (m_worldTransform != world) + { + m_worldTransform = world; + m_inverseWorldTransform = world.invertOrIdentity(); + } + + auto pos = m_inverseWorldTransform * m_handleSource->worldTranslation(); + + auto localBounds = AABB(-width() * originX(), + -height() * originY(), + -width() * originX() + width(), + -height() * originY() + height()); + + auto local = localBounds.factorFrom(pos); + x(local.x); + y(local.y); + } +} + void Joystick::apply(Artboard* artboard) const { if (m_xAnimation != nullptr) { - m_xAnimation->apply(artboard, (x() + 1.0f) / 2.0f * m_xAnimation->durationSeconds()); + m_xAnimation->apply(artboard, + ((isJoystickFlagged(JoystickFlags::invertX) ? -x() : x()) + 1.0f) / + 2.0f * m_xAnimation->durationSeconds()); } if (m_yAnimation != nullptr) { - m_yAnimation->apply(artboard, (y() + 1.0f) / 2.0f * m_yAnimation->durationSeconds()); + + m_yAnimation->apply(artboard, + ((isJoystickFlagged(JoystickFlags::invertY) ? -y() : y()) + 1.0f) / + 2.0f * m_yAnimation->durationSeconds()); } }