Difference between revisions of "Camera View, Rotate, Trim and Pad a Map in Dart"

From RogueBasin
Jump to navigation Jump to search
(initial)
 
(added generic types)
Line 23: Line 23:
     map.trim().split('\n').map((row) => row.split('')).toList();
     map.trim().split('\n').map((row) => row.split('')).toList();


List<List<dynamic>> result = Transform.rotateRight(mapList);
List<List<String>> result = Transform.rotateRight<String>(mapList);


String mapString = result.map((e) => e.join('')).join('\n');
String mapString = result.map((e) => e.join('')).join('\n');
Line 59: Line 59:
String getWall() => '#';
String getWall() => '#';


List<List<dynamic>> result = Transform.pad(mapList, 2, getWall);
List<List<String>> result = Transform.pad<String>(mapList, 2, getWall);


String mapString = result.map((e) => e.join('')).join('\n');
String mapString = result.map((e) => e.join('')).join('\n');
Line 101: Line 101:
bool isOuterWall(dynamic str) => str == '#';
bool isOuterWall(dynamic str) => str == '#';


List<List<dynamic>> result = Transform.trim(mapListPadded, isOuterWall);
List<List<String>> result = Transform.trim<String>(mapListPadded, isOuterWall);


String mapString = result.map((e) => e.join('')).join('\n');
String mapString = result.map((e) => e.join('')).join('\n');
Line 134: Line 134:
     map.trim().split('\n').map((row) => row.split('')).toList();
     map.trim().split('\n').map((row) => row.split('')).toList();


List<List<dynamic>> result = Transform.copy(mapList, 0, 0, 5, 5);
List<List<String>> result = Transform.copy<String>(mapList, 0, 0, 5, 5);


String mapString = result.map((e) => e.join('')).join('\n');
String mapString = result.map((e) => e.join('')).join('\n');
Line 151: Line 151:


<syntaxhighlight lang="dart" line>
<syntaxhighlight lang="dart" line>
import 'dart:math';
/// Allows for rotating, padding and trimming a list-of-lists.
class Transform {
class Transform {
   // -----------------------------------------------------------------------
   // -----------------------------------------------------------------------
Line 157: Line 160:


   /// Rotates to the right.
   /// Rotates to the right.
   static List<List<dynamic>> rotateRight(List<List<dynamic>> target) {
   static List<List<T>> rotateRight<T>(List<List<T>> target) {
     List<List<dynamic>> rotatedList = [];
     List<List<T>> rotatedList = [];
     final width = target.first.length;
     final width = target.first.length;
     final height = target.length - 1;
     final height = target.length - 1;


     for (int column = 0; column < width; column++) {
     for (int column = 0; column < width; column++) {
       final List<dynamic> lineNew = [];
       final List<T> lineNew = [];


       for (int row = height; row >= 0; row--) {
       for (int row = height; row >= 0; row--) {
Line 180: Line 183:


   /// Pads with WALL tiles returned by [getWall].
   /// Pads with WALL tiles returned by [getWall].
   static List<List<dynamic>> pad(
   static List<List<T>> pad<T>(
       List<List<dynamic>> mapShape, int padding, dynamic Function() getWall) {
       List<List<T>> mapShape, int padding, T Function() getWall) {
     List<List<dynamic>> padded = _padTop(mapShape, padding, getWall);
     List<List<T>> padded = _padTop(mapShape, padding, getWall);
 
     padded = rotateRight(padded);
     padded = rotateRight(padded);
     padded = _padTop(padded, padding, getWall);
     padded = _padTop(padded, padding, getWall);
Line 195: Line 199:


   /// Pads a WALL at the top
   /// Pads a WALL at the top
   static List<List<dynamic>> _padTop(
   static List<List<T>> _padTop<T>(
       List<List<dynamic>> other, int padding, dynamic Function() getWall) {
       List<List<T>> other, int padding, T Function() getWall) {
     final width = other.first.length;
     final width = other.first.length;


     final List<List<dynamic>> mapNew = [];
     final List<List<T>> mapNew = [];
     mapNew.addAll(List<List<dynamic>>.generate(padding, (index) {
     mapNew.addAll(List<List<T>>.generate(padding, (index) {
       return List<dynamic>.generate(width, (index) => getWall.call());
       return List<T>.generate(width, (index) => getWall.call());
     }));
     }));
     mapNew.addAll(other);
     mapNew.addAll(other);
Line 213: Line 217:


   /// Trims all outer WALLS down to a single outer wall.
   /// Trims all outer WALLS down to a single outer wall.
   static List<List<dynamic>> trim(
   static List<List<T>> trim<T>(
       List<List<dynamic>> mapShape, bool Function(dynamic e) isOuterWall) {
       List<List<T>> mapShape, bool Function(T e) isOuterWall) {
     List<List<dynamic>> trimmed = _trimTopInternal(mapShape, isOuterWall);
     List<List<T>> trimmed = _trimTopInternal(mapShape, isOuterWall);
     trimmed = rotateRight(trimmed);
     trimmed = rotateRight(trimmed);
     trimmed = _trimTopInternal(trimmed, isOuterWall);
     trimmed = _trimTopInternal(trimmed, isOuterWall);
Line 228: Line 232:


   /// Trims WALL tiles from the top of the other elements, leaves ony one WALL.
   /// Trims WALL tiles from the top of the other elements, leaves ony one WALL.
   static List<List<dynamic>> _trimTopInternal(
   static List<List<T>> _trimTopInternal<T>(
       List<List<dynamic>> other, bool Function(dynamic e) isOuterWall) {
       List<List<T>> other, bool Function(T e) isOuterWall) {
     final List<List<dynamic>> rows = [];
     final List<List<T>> rows = [];


     int trimTop = 0;
     int trimTop = 0;
     for (int row = 0; row < other.length; row++) {
     for (int row = 0; row < other.length; row++) {
       final List<dynamic> line = other.elementAt(row);
       final List<T> line = other.elementAt(row);


       final isOnlyWallsInternal =
       final isOnlyWallsInternal =
           line.where((dynamic e) => isOuterWall.call(e)).length == line.length;
           line.where((T e) => isOuterWall.call(e)).length == line.length;


       if (isOnlyWallsInternal == false) {
       if (isOnlyWallsInternal == false) {
Line 256: Line 260:
   ///
   ///
   /// `x1` and `y1` are inclusive.
   /// `x1` and `y1` are inclusive.
   static List<List<dynamic>> copy(
   static List<List<T>> copy<T>(
       List<List<dynamic>> target, int x0, int y0, int x1, int y1) {
       List<List<T>> target, int x0, int y0, int x1, int y1) {
     assert(x0 >= 0 && x1 < target.first.length);
     assert(x0 >= 0 && x1 < target.first.length);
     assert(y0 >= 0 && y1 < target.length);
     assert(y0 >= 0 && y1 < target.length);


     final List<List<dynamic>> copy = [];
     final List<List<T>> copy = [];


     for (int y = y0; y <= y1; y++) {
     for (int y = y0; y <= y1; y++) {
       final List<dynamic> copyRow = [];
       final List<T> copyRow = [];
       for (int x = x0; x <= x1; x++) {
       for (int x = x0; x <= x1; x++) {
         copyRow.add(target[y][x]);
         copyRow.add(target[y][x]);
Line 272: Line 276:


     return copy;
     return copy;
  }
  // -----------------------------------------------------------------------
  // CENTER TILE
  // -----------------------------------------------------------------------
  /// Returns the center tile of shapes with uneven `width` and `height`.
  ///
  /// Undefined for other shapes.
  ///
  /// Will throw if width or height are 1.
  static (Point<int> position, T tile) getCenterTile<T>(
      int x, int y, List<List<T>> target) {
    final int targetHeight = target.length;
    final int targetWidth = target.first.length;
    if (targetHeight == 1 || targetWidth == 1) {
      throw Exception('Not implemented');
    }
    final y0 = (targetHeight / 2).ceil() - 1;
    final x0 = (targetWidth / 2).ceil() - 1;
    final tile = target[y0][x0];
    return (Point<int>(x + x0, y + y0), tile);
  }
  // -----------------------------------------------------------------------
  // FILTER
  // -----------------------------------------------------------------------
  static List<(Point<int> position, T tile)> filter<T>(
      List<List<T>> target, bool Function(T tile) doFilter) {
    final List<(Point<int> position, T tile)> ret = [];
    final int height = target.length;
    final int width = target.first.length;
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        if (doFilter.call(target[y][x])) {
          ret.add((Point<int>(x, y), target[y][x]));
        }
      }
    }
    return ret;
   }
   }
}
}
</syntaxhighlight>
</syntaxhighlight>

Revision as of 09:41, 19 October 2023

Dart Implementation of Map Transformations

The code below is versatile in that it is map engine agnostic.

It will trim, pad, copy and rotate about any list-of-lists.

Usage

Rotate Right

final map = '''
##########
###....###
###....###
###....###
###....###
###....###
######.###
######▒▒▒#''';

List<List<String>> mapList =
    map.trim().split('\n').map((row) => row.split('')).toList();

List<List<String>> result = Transform.rotateRight<String>(mapList);

String mapString = result.map((e) => e.join('')).join('\n');

print(mapString);

// ########
// ########
// ########
// ##.....#
// ##.....#
// ##.....#
// ▒......#
// ▒#######
// ▒#######
// ########

Pad

final map = '''
##########
###....###
###....###
###....###
###....###
###....###
######.###
######▒▒▒#''';

List<List<String>> mapList =
    map.trim().split('\n').map((row) => row.split('')).toList();

String getWall() => '#';

List<List<String>> result = Transform.pad<String>(mapList, 2, getWall);

String mapString = result.map((e) => e.join('')).join('\n');

print(mapString);

// ##############
// ##############
// ##############
// #####....#####
// #####....#####
// #####....#####
// #####....#####
// #####....#####
// ########.#####
// ########▒▒▒###
// ##############
// ##############

Trim

final map = '''
##############
##############
##############
#####....#####
#####....#####
#####....#####
#####....#####
#####....#####
########.#####
########▒▒▒###
##############
##############''';

List<List<String>> mapList =
    map.trim().split('\n').map((row) => row.split('')).toList();

bool isOuterWall(dynamic str) => str == '#';

List<List<String>> result = Transform.trim<String>(mapListPadded, isOuterWall);

String mapString = result.map((e) => e.join('')).join('\n');

print(mapString);

// ########
// #....###
// #....###
// #....###
// #....###
// #....###
// ####.###
// ####▒▒▒#
// ########

Copy

final map = '''
##########
###....###
###....###
###....###
###....###
###....###
######.###
######▒▒▒#''';

List<List<String>> mapList =
    map.trim().split('\n').map((row) => row.split('')).toList();

List<List<String>> result = Transform.copy<String>(mapList, 0, 0, 5, 5);

String mapString = result.map((e) => e.join('')).join('\n');

print(mapString);

// ######
// ###...
// ###...
// ###...
// ###...
// ###...

Dart Implementation

import 'dart:math';

/// Allows for rotating, padding and trimming a list-of-lists.
class Transform {
  // -----------------------------------------------------------------------
  // ROTATE
  // -----------------------------------------------------------------------

  /// Rotates to the right.
  static List<List<T>> rotateRight<T>(List<List<T>> target) {
    List<List<T>> rotatedList = [];
    final width = target.first.length;
    final height = target.length - 1;

    for (int column = 0; column < width; column++) {
      final List<T> lineNew = [];

      for (int row = height; row >= 0; row--) {
        lineNew.add(target[row][column]);
      }

      rotatedList.add(lineNew);
    }

    return rotatedList;
  }

  // -----------------------------------------------------------------------
  // PAD
  // -----------------------------------------------------------------------

  /// Pads with WALL tiles returned by [getWall].
  static List<List<T>> pad<T>(
      List<List<T>> mapShape, int padding, T Function() getWall) {
    List<List<T>> padded = _padTop(mapShape, padding, getWall);

    padded = rotateRight(padded);
    padded = _padTop(padded, padding, getWall);
    padded = rotateRight(padded);
    padded = _padTop(padded, padding, getWall);
    padded = rotateRight(padded);
    padded = _padTop(padded, padding, getWall);
    padded = rotateRight(padded);

    return padded;
  }

  /// Pads a WALL at the top
  static List<List<T>> _padTop<T>(
      List<List<T>> other, int padding, T Function() getWall) {
    final width = other.first.length;

    final List<List<T>> mapNew = [];
    mapNew.addAll(List<List<T>>.generate(padding, (index) {
      return List<T>.generate(width, (index) => getWall.call());
    }));
    mapNew.addAll(other);

    return mapNew;
  }

  // -----------------------------------------------------------------------
  // TRIM
  // -----------------------------------------------------------------------

  /// Trims all outer WALLS down to a single outer wall.
  static List<List<T>> trim<T>(
      List<List<T>> mapShape, bool Function(T e) isOuterWall) {
    List<List<T>> trimmed = _trimTopInternal(mapShape, isOuterWall);
    trimmed = rotateRight(trimmed);
    trimmed = _trimTopInternal(trimmed, isOuterWall);
    trimmed = rotateRight(trimmed);
    trimmed = _trimTopInternal(trimmed, isOuterWall);
    trimmed = rotateRight(trimmed);
    trimmed = _trimTopInternal(trimmed, isOuterWall);
    trimmed = rotateRight(trimmed);

    return trimmed;
  }

  /// Trims WALL tiles from the top of the other elements, leaves ony one WALL.
  static List<List<T>> _trimTopInternal<T>(
      List<List<T>> other, bool Function(T e) isOuterWall) {
    final List<List<T>> rows = [];

    int trimTop = 0;
    for (int row = 0; row < other.length; row++) {
      final List<T> line = other.elementAt(row);

      final isOnlyWallsInternal =
          line.where((T e) => isOuterWall.call(e)).length == line.length;

      if (isOnlyWallsInternal == false) {
        break;
      }
      trimTop = row;
    }

    rows.addAll(other.getRange(trimTop, other.length));
    return rows;
  }

// -----------------------------------------------------------------------
// COPY
// -----------------------------------------------------------------------

  /// Copies the given coordinates.
  ///
  /// `x1` and `y1` are inclusive.
  static List<List<T>> copy<T>(
      List<List<T>> target, int x0, int y0, int x1, int y1) {
    assert(x0 >= 0 && x1 < target.first.length);
    assert(y0 >= 0 && y1 < target.length);

    final List<List<T>> copy = [];

    for (int y = y0; y <= y1; y++) {
      final List<T> copyRow = [];
      for (int x = x0; x <= x1; x++) {
        copyRow.add(target[y][x]);
      }
      copy.add(copyRow);
    }

    return copy;
  }

  // -----------------------------------------------------------------------
  // CENTER TILE
  // -----------------------------------------------------------------------

  /// Returns the center tile of shapes with uneven `width` and `height`.
  ///
  /// Undefined for other shapes.
  ///
  /// Will throw if width or height are 1.
  static (Point<int> position, T tile) getCenterTile<T>(
      int x, int y, List<List<T>> target) {
    final int targetHeight = target.length;
    final int targetWidth = target.first.length;

    if (targetHeight == 1 || targetWidth == 1) {
      throw Exception('Not implemented');
    }

    final y0 = (targetHeight / 2).ceil() - 1;
    final x0 = (targetWidth / 2).ceil() - 1;
    final tile = target[y0][x0];
    return (Point<int>(x + x0, y + y0), tile);
  }

  // -----------------------------------------------------------------------
  // FILTER
  // -----------------------------------------------------------------------

  static List<(Point<int> position, T tile)> filter<T>(
      List<List<T>> target, bool Function(T tile) doFilter) {
    final List<(Point<int> position, T tile)> ret = [];

    final int height = target.length;
    final int width = target.first.length;

    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        if (doFilter.call(target[y][x])) {
          ret.add((Point<int>(x, y), target[y][x]));
        }
      }
    }
    return ret;
  }
}