-
-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Export maps #78
base: main
Are you sure you want to change the base?
Changes from all commits
c37b7cf
e389a08
8f38c1c
f697800
e104efd
6b60e92
54c3878
50025a0
ef1e6ab
e1c4521
9174a0a
a7d451c
657a547
54f4a06
c0452f8
4652080
3a057e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
part of tiled; | ||
|
||
/// Basic data class holding a Color in ARGB format. | ||
/// This can be converted to dart:ui's Color using the flame_tiled package | ||
class ColorData extends ExportValue<String> { | ||
static int _sub(int hex, int index) => (hex >> index * 8) & 0x000000ff; | ||
|
||
final int red; | ||
final int green; | ||
final int blue; | ||
final int alpha; | ||
|
||
/// Parses the Color from an int using the lower 32-bits and tiled's format: 0xaarrggbb | ||
ColorData.hex(int hex) | ||
: alpha = _sub(hex, 3), | ||
red = _sub(hex, 2), | ||
green = _sub(hex, 1), | ||
blue = _sub(hex, 0); | ||
|
||
const ColorData.rgb(this.red, this.green, this.blue, [this.alpha = 255]) | ||
: assert(red >= 0 && red <= 255), | ||
assert(green >= 0 && green <= 255), | ||
assert(blue >= 0 && blue <= 255), | ||
assert(alpha >= 0 && alpha <= 255); | ||
|
||
static String _hex(int value) { | ||
return value.toRadixString(16).padLeft(2, '0'); | ||
} | ||
|
||
String get export => '#${_hex(alpha)}${_hex(red)}${_hex(green)}${_hex(blue)}'; | ||
|
||
@override | ||
String get json => export; | ||
|
||
@override | ||
String get xml => export; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
part of tiled; | ||
|
||
extension Grouping<V> on Iterable<V> { | ||
Map<K, List<V>> groupBy<K>(K Function(V value) key) { | ||
final out = <K, List<V>>{}; | ||
for (final v in this) { | ||
final k = key(v); | ||
if (!out.containsKey(k)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pick your poison; but since you have "List" as non-nullable, the first is shorter. (foo[1] ??= []).add('one');
foo.putIfAbsent(2, ()=>[]).add('two'); |
||
out[k] = [v]; | ||
} else { | ||
out[k]!.add(v); | ||
} | ||
} | ||
|
||
return out; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
part of tiled; | ||
|
||
extension Null<K, V> on Map<K, V?> { | ||
Map<K, V> nonNulls() => | ||
{for (final e in entries.where((e) => e.value is V)) e.key: e.value as V}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
part of tiled; | ||
|
||
/// Base type for all other types. This is necessary to allow for Lists | ||
abstract class ExportObject {} | ||
|
||
/// Base type for everything that returns a single exported value | ||
abstract class ExportResolver implements ExportObject { | ||
/// Creates an XmlNode representing the value | ||
XmlNode exportXml(); | ||
|
||
/// Creates an JsonObject representing this value | ||
JsonObject exportJson(); | ||
} | ||
|
||
/// Element/object for exporting with default structure | ||
class ExportElement implements ExportResolver { | ||
final String name; | ||
final Map<String, ExportValue> fields; | ||
final Map<String, ExportObject> children; | ||
final CustomProperties? properties; | ||
|
||
const ExportElement( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like for these to be named parameters. It would make the earlier calls to ExportElement easier to read, and bonus, you could default values (e.g. chilren = {}) and make the call sites look cleaner. |
||
this.name, | ||
this.fields, | ||
this.children, [ | ||
this.properties, | ||
]); | ||
|
||
@override | ||
XmlElement exportXml() => XmlElement( | ||
XmlName(name), | ||
fields.entries.map((e) => XmlAttribute(XmlName(e.key), e.value.xml)), | ||
[ | ||
...children.values.expand((e) { | ||
if (e is ExportList) { | ||
return e.map((e) => e.exportXml()); | ||
} | ||
|
||
if (e is ExportResolver) { | ||
return [e.exportXml()]; | ||
} | ||
|
||
throw 'Bad State: ExportObject switch should have been exhaustive'; | ||
}), | ||
if (properties != null && properties!.isNotEmpty) | ||
XmlElement( | ||
XmlName('properties'), | ||
[], | ||
properties!.map((e) => e.exportXml()).toList(), | ||
), | ||
], | ||
); | ||
|
||
@override | ||
JsonMap exportJson() => JsonMap({ | ||
...fields.map((key, value) => MapEntry(key, value.exportJson())), | ||
...children.map((key, e) { | ||
if (e is ExportList) { | ||
return MapEntry(key, JsonList(e.map((e) => e.exportJson()))); | ||
} | ||
|
||
if (e is ExportResolver) { | ||
return MapEntry(key, e.exportJson()); | ||
} | ||
|
||
throw 'Bad State: ExportChild switch should have been exhaustive'; | ||
}), | ||
if (properties != null) | ||
'properties': JsonList( | ||
properties!.map((e) => e.exportJson()), | ||
), | ||
}); | ||
} | ||
|
||
/// Splits export tree according to the type of export using an ExportResolver | ||
/// for each. | ||
class ExportFormatSpecific implements ExportResolver { | ||
final ExportResolver xml; | ||
final ExportResolver json; | ||
|
||
const ExportFormatSpecific({required this.xml, required this.json}); | ||
|
||
@override | ||
JsonObject exportJson() => json.exportJson(); | ||
|
||
@override | ||
XmlNode exportXml() => xml.exportXml(); | ||
} | ||
|
||
/// List of ExportResolvers. This is used to get Lists into json, while simply | ||
/// expanding in xml | ||
class ExportList extends DelegatingList<ExportResolver> | ||
implements ExportObject { | ||
ExportList(Iterable<ExportResolver> base) : super(base.toList()); | ||
|
||
ExportList.from(Iterable<Exportable> source) | ||
: super( | ||
source.map((e) => e).toList(), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not store the hex value in ColorData and have getters that returns the bit-shifted values back when needed? export then becomes
=> _hex.toRadixString(16).padLeft(8, '0')
. My assumption right now is this will increase the size of memory.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right that would be more efficient!