patternrustMinor
Simple ASCII art in Rust
Viewed 0 times
rustartsimpleascii
Problem
I stumbled my way through some Rust yesterday and today to make a simple ASCII art API. It can draw lines, circles, and a canvas.
```
use std::collections::HashMap;
#[derive(PartialEq, PartialOrd, Debug)]
struct Point(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Dimension(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Rectangle(Point, Point);
#[derive(PartialEq, PartialOrd, Debug, Hash, Eq)]
struct Coordinate(u32, u32);
#[derive(Clone, Copy, Debug)]
enum Shape {
Canvas,
Circle,
HorizontalLine,
VerticalLine,
DiagonalLineLeftToRight,
DiagonalLineRightToLeft,
}
fn canvas_index_to_coords(i: u32, num: u32) -> Coordinate {
if i , b: HashMap) -> HashMap {
let mut combined = HashMap::new();
for (key, val) in a {
combined.insert(key, val);
}
for (key, val) in b {
combined.insert(key, val);
}
combined
}
fn canvas(size: Dimension) -> HashMap {
let mut canvas_coords = HashMap::new();
for i in 0..(size.0 * size.1) {
canvas_coords.insert(canvas_index_to_coords(i, size.0), Shape::Canvas);
}
canvas_coords
}
fn circle(radius: u32, point: Point) -> HashMap {
let x0 = point.0;
let y0 = point.1;
let mut x = radius;
let mut y = 0;
let mut err: i32 = 0;
let mut coords = HashMap::new();
while x >= y {
coords.insert(Coordinate(x0 + x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 - y), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + x, y0 - y), Shape::Circle);
y += 1;
err += 1 + 2 * y as i32;
if 2 * (err - x as i32) + 1 > 0
{
```
use std::collections::HashMap;
#[derive(PartialEq, PartialOrd, Debug)]
struct Point(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Dimension(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Rectangle(Point, Point);
#[derive(PartialEq, PartialOrd, Debug, Hash, Eq)]
struct Coordinate(u32, u32);
#[derive(Clone, Copy, Debug)]
enum Shape {
Canvas,
Circle,
HorizontalLine,
VerticalLine,
DiagonalLineLeftToRight,
DiagonalLineRightToLeft,
}
fn canvas_index_to_coords(i: u32, num: u32) -> Coordinate {
if i , b: HashMap) -> HashMap {
let mut combined = HashMap::new();
for (key, val) in a {
combined.insert(key, val);
}
for (key, val) in b {
combined.insert(key, val);
}
combined
}
fn canvas(size: Dimension) -> HashMap {
let mut canvas_coords = HashMap::new();
for i in 0..(size.0 * size.1) {
canvas_coords.insert(canvas_index_to_coords(i, size.0), Shape::Canvas);
}
canvas_coords
}
fn circle(radius: u32, point: Point) -> HashMap {
let x0 = point.0;
let y0 = point.1;
let mut x = radius;
let mut y = 0;
let mut err: i32 = 0;
let mut coords = HashMap::new();
while x >= y {
coords.insert(Coordinate(x0 + x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 - y), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + x, y0 - y), Shape::Circle);
y += 1;
err += 1 + 2 * y as i32;
if 2 * (err - x as i32) + 1 > 0
{
Solution
canvas_index_to_coordswould sound better as a factory method onCoordinate, i.e.Coordinate::from_canvas_index.
- In
combine, you can turn theHashMaps into iterators,chainthe two iterators together andcollectthat into a newHashMap.collectdefers toFromIter::from_iter;HashMapimplementsFromIterand uses the iterator'ssize_hintto reserve enough memory for the reported minimum number of items at once, whereas repeated calls toinsertmay need to reallocate a few times (which may mean copying theHashMap's items every time). (Note: we don't need to callinto_iteronb, aschainwill do it for us. However, you could still do it if you like the visual symmetry; it works because iterators implementIntoIterator.)
- In
canvas, you can usemapon the range iterator to turn it into an iterator of key-value pairs, then collect that into aHashMap.
- In
circleandline_shape, you separately assign tuple struct fields to local variables. You can use a tuple struct pattern in aletstatement to destructure the tuple struct and assign all fields to local variables at once. (This wouldn't work inlinebecause of the casts.) The patterns could also be used in the parameter list, but I find that they're too long here.
- In
line_shape, you repeat thex0 != x1condition. I would reorder the conditions to avoid that.
- In
draw, you can constructvecby getting an iterator from theHashMapand collecting it into aVec. This works becauseHashMap's iterators iterate on key-value tuples, which is exactly what you're putting in your vector!
- In
draw, you want to order byyin descending order. However, the way you do it will panic when overflow checks are enabled of theycoordinate is equal tostd::i32::MIN. A safe alternative is to just perform a bitwise not on the value (this is written!yin Rust); we don't even need to cast toi32!
- In
draw, you can combine the two calls tosort_by_keyinto one: make the closure return a tuple. This works because tuples implementOrd(for up to 12-tuples). So instead of sorting byx, then by!y, we can simply sort by(!y, x).
- When
matching on a reference, it's typical to use the dereferencing operator in the match expression instead of repeating a reference pattern on all arms.
- In
draw, each arm repeats the call towritewith only the character value differing between each arm. I'd make a method onShapethat maps a shape to a character, then use that method to determine the character to write.
```
use std::collections::HashMap;
#[derive(PartialEq, PartialOrd, Debug)]
struct Point(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Dimension(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Rectangle(Point, Point);
#[derive(PartialEq, PartialOrd, Debug, Hash, Eq)]
struct Coordinate(u32, u32);
#[derive(Clone, Copy, Debug)]
enum Shape {
Canvas,
Circle,
HorizontalLine,
VerticalLine,
DiagonalLineLeftToRight,
DiagonalLineRightToLeft,
}
impl Coordinate {
fn from_canvas_index(i: u32, num: u32) -> Coordinate {
if i char {
match *self {
Shape::Canvas => ' ',
Shape::Circle => 'o',
Shape::HorizontalLine => '-',
Shape::VerticalLine => '|',
Shape::DiagonalLineLeftToRight => '\\',
Shape::DiagonalLineRightToLeft => '/',
}
}
}
fn write(coords: &Coordinate, chr: char, num: u32) {
if coords.0 == num - 1 {
println!("{}", chr);
} else {
print!("{} ", chr);
}
}
fn combine(a: HashMap, b: HashMap) -> HashMap {
a.into_iter().chain(b).collect()
}
fn canvas(size: Dimension) -> HashMap {
(0..(size.0 * size.1))
.map(|i| (Coordinate::from_canvas_index(i, size.0), Shape::Canvas))
.collect()
}
fn circle(radius: u32, point: Point) -> HashMap {
let Point(x0, y0) = point;
let mut x = radius;
let mut y = 0;
let mut err: i32 = 0;
let mut coords = HashMap::new();
while x >= y {
coords.insert(Coordinate(x0 + x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 - y), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + x, y0 - y), Shape::Circle);
y += 1;
err += 1 + 2 * y as i32;
if 2 * (err - x as i32) + 1 > 0 {
x -= 1;
err += 1 - 2 * x as i32;
}
}
coords
}
fn line_shape(rectangle: Rectangle) -> Shape {
let Rectangle(Point(x0, y0), Point(x1, y1)) = rectangle;
if y0 == y1 {
Shape::HorizontalLine
} else if x0 == x1 {
Shape::Vertica
Code Snippets
use std::collections::HashMap;
#[derive(PartialEq, PartialOrd, Debug)]
struct Point(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Dimension(u32, u32);
#[derive(PartialEq, PartialOrd, Debug)]
struct Rectangle(Point, Point);
#[derive(PartialEq, PartialOrd, Debug, Hash, Eq)]
struct Coordinate(u32, u32);
#[derive(Clone, Copy, Debug)]
enum Shape {
Canvas,
Circle,
HorizontalLine,
VerticalLine,
DiagonalLineLeftToRight,
DiagonalLineRightToLeft,
}
impl Coordinate {
fn from_canvas_index(i: u32, num: u32) -> Coordinate {
if i < num {
Coordinate(i, 0)
} else {
Coordinate(i % num, i / num)
}
}
}
impl Shape {
fn to_char(&self) -> char {
match *self {
Shape::Canvas => ' ',
Shape::Circle => 'o',
Shape::HorizontalLine => '-',
Shape::VerticalLine => '|',
Shape::DiagonalLineLeftToRight => '\\',
Shape::DiagonalLineRightToLeft => '/',
}
}
}
fn write(coords: &Coordinate, chr: char, num: u32) {
if coords.0 == num - 1 {
println!("{}", chr);
} else {
print!("{} ", chr);
}
}
fn combine(a: HashMap<Coordinate, Shape>, b: HashMap<Coordinate, Shape>) -> HashMap<Coordinate, Shape> {
a.into_iter().chain(b).collect()
}
fn canvas(size: Dimension) -> HashMap<Coordinate, Shape> {
(0..(size.0 * size.1))
.map(|i| (Coordinate::from_canvas_index(i, size.0), Shape::Canvas))
.collect()
}
fn circle(radius: u32, point: Point) -> HashMap<Coordinate, Shape> {
let Point(x0, y0) = point;
let mut x = radius;
let mut y = 0;
let mut err: i32 = 0;
let mut coords = HashMap::new();
while x >= y {
coords.insert(Coordinate(x0 + x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 + x), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 + y), Shape::Circle);
coords.insert(Coordinate(x0 - x, y0 - y), Shape::Circle);
coords.insert(Coordinate(x0 - y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + y, y0 - x), Shape::Circle);
coords.insert(Coordinate(x0 + x, y0 - y), Shape::Circle);
y += 1;
err += 1 + 2 * y as i32;
if 2 * (err - x as i32) + 1 > 0 {
x -= 1;
err += 1 - 2 * x as i32;
}
}
coords
}
fn line_shape(rectangle: Rectangle) -> Shape {
let Rectangle(Point(x0, y0), Point(x1, y1)) = rectangle;
if y0 == y1 {
Shape::HorizontalLine
} else if x0 == x1 {
Shape::VerticalLine
} else if y0 > y1 {
Shape::DiagonalLineLeftToRight
} else {
Shape::DiagonalLineRightToLeft
}
}
fn line(rectangle: Rectangle) -> HashMap<Coordinate, Shape> {
let x0 = (rectangle.0).0 as i32;
let y0 = (rectangle.0).1 as i32;
let x1 = (rectangle.1).0 as i32;
let y1 = (rectangle.1).1 as i32;Context
StackExchange Code Review Q#145402, answer score: 4
Revisions (0)
No revisions yet.