Skip to content

Instantly share code, notes, and snippets.

@renatoathaydes
Created December 4, 2021 10:25
Show Gist options
  • Save renatoathaydes/15bb8bb74b8f6f11e073ebc7d51c0464 to your computer and use it in GitHub Desktop.
Save renatoathaydes/15bb8bb74b8f6f11e073ebc7d51c0464 to your computer and use it in GitHub Desktop.
Skyline problem implementation in Flutter. Based on https://briangordon.github.io/2014/08/the-skyline-problem.html
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() => runApp(MyApp());
class Rectangle {
// only x-coordinate as all "buildings" start on the same y-axis point.
final double x;
final double width;
final double height;
final Color color;
const Rectangle(this.x, this.width, this.height, this.color);
}
const horizonY = 300.0;
const rectangles = [
Rectangle(10, 40, 100, Color(0xFFf14925)),
Rectangle(180, 100, 70, Color(0xFFFF7F50)),
Rectangle(60, 150, 50, Color(0xFFFF00FF)),
Rectangle(110, 50, 120, Color(0xFF008000)),
Rectangle(80, 50, 80, Color(0xFF6495ED)),
];
class RectangleOnOff {
final Rectangle rect;
final bool on;
const RectangleOnOff(this.rect, this.on);
}
Map<double, RectangleOnOff> findSkyline(List<Rectangle> rectangles) {
final result = <double, RectangleOnOff>{};
// at each change point, remember which rectangles are "on" or "off"...
// assume no two buildings can start/end at the exact same x point for simplification.
for (final rect in rectangles) {
result[rect.x] = RectangleOnOff(rect, true);
result[rect.x + rect.width] = RectangleOnOff(rect, false);
}
return result;
}
double findMaxHeight(List<Rectangle> rectangles) {
return rectangles.map((r) => r.height).fold(0.0, (a, b) => math.max(a, b));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Skyline Problem',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('The Skyline Problem'),
),
body: ListView(children: const <Widget>[
SizedBox(
width: 800,
height: horizonY,
child: CustomPaint(
painter: OpenPainter(rectangles),
),
),
]),
);
}
}
class OpenPainter extends CustomPainter {
final List<Rectangle> rects;
const OpenPainter(this.rects);
@override
void paint(Canvas canvas, Size size) {
// draw the buildings
for (final rect in rects) {
final paint = Paint()
..color = rect.color
..style = PaintingStyle.fill;
canvas.drawRect(
Offset(rect.x, horizonY - rect.height) &
Size(rect.width, rect.height),
paint);
}
// draw the skyline
var skyline = findSkyline(rects);
final paint = Paint()
..color = const Color(0xFF000000)
..strokeWidth = 2.0;
final changePoints = skyline.keys.toList();
changePoints.sort();
// start from the horizon at x=0
var currentPoint = const Offset(0.0, horizonY);
// remember buildings which are on
final buildings = <Rectangle>[];
for (final p in changePoints) {
final change = skyline[p]!;
if (change.on) {
buildings.add(change.rect);
} else {
buildings.remove(change.rect);
}
final nextPoint = Offset(p, horizonY - findMaxHeight(buildings));
// first we need to draw the horizontal line (same height as before)
canvas.drawLine(currentPoint, Offset(p, currentPoint.dy), paint);
// then, the vertical line changing the current height
canvas.drawLine(Offset(p, currentPoint.dy), nextPoint, paint);
currentPoint = nextPoint;
}
}
@override
bool shouldRepaint(OpenPainter oldDelegate) => false;
@override
bool shouldRebuildSemantics(OpenPainter oldDelegate) => false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment