Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Benjamin Lee
ball-gfx-hal
Commits
376c1e2f
Commit
376c1e2f
authored
Jan 28, 2019
by
Benjamin Lee
💬
Browse files
Added animations to identify each round winner.
parent
a9ec875a
Changes
8
Hide whitespace changes
Inline
Side-by-side
Cargo.lock
View file @
376c1e2f
...
...
@@ -113,8 +113,8 @@ dependencies = [
"colored 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"easer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"enum-kinds 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx-backend-vulkan 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx-hal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
...
...
@@ -364,18 +364,17 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "e
ith
er"
version = "
1.5.0
"
name = "e
as
er"
version = "
0.2.1
"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "e
num-kinds
"
version = "
0.4.1
"
name = "e
ither
"
version = "
1.5.0
"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
...
...
@@ -966,14 +965,6 @@ name = "pkg-config"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.26"
...
...
@@ -982,14 +973,6 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.11"
...
...
@@ -1326,16 +1309,6 @@ dependencies = [
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.14.9"
...
...
@@ -1723,8 +1696,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e"
"checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a"
"checksum downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "18df8ce4470c189d18aa926022da57544f31e154631eb4cfe796aea97051fe6c"
"checksum easer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde460cecb776d6524d53ca7acf960561686a620d3791c0dc56c3ada406191a"
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
"checksum enum-kinds 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f21c374dea848c19071b1504ca5ad03c9ad0d03d2e509e68f6623b8fcac4b5"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
...
...
@@ -1792,9 +1765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
"checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978"
"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5"
...
...
@@ -1834,7 +1805,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3"
"checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04"
"checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5"
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
...
...
Cargo.toml
View file @
376c1e2f
...
...
@@ -36,9 +36,9 @@ ord_subset = "3.1.1"
nalgebra
=
{
version
=
"0.16.13"
,
features
=
["serde-serialize"]
}
either
=
"1.5.0"
crossbeam
=
"0.6.0"
enum-kinds
=
"0.4.1"
take_mut
=
"0.2.2"
itertools
=
"0.8.0"
easer
=
"0.2.1"
[build-dependencies]
shaderc
=
"0.3.13"
...
...
src/game/client.rs
View file @
376c1e2f
...
...
@@ -37,9 +37,11 @@ pub struct Players<'a, S> {
}
pub
struct
Game
{
players
:
HashMap
<
PlayerId
,
StaticPlayerState
>
,
pub
players
:
HashMap
<
PlayerId
,
StaticPlayerState
>
,
pub
last_round
:
Option
<
RoundState
>
,
pub
round
:
RoundState
,
pub
round_duration
:
f32
,
snapshots
:
VecDeque
<
(
Snapshot
,
Instant
)
>
,
round
:
RoundState
,
settings
:
GameSettings
,
settings_handle
:
Arc
<
SettingsHandle
>
,
cursor
:
Arc
<
Mutex
<
Point2
<
f32
>>>
,
...
...
@@ -159,6 +161,8 @@ impl Game {
pub
fn
new
(
players
:
HashMap
<
PlayerId
,
StaticPlayerState
>
,
snapshot
:
Snapshot
,
round
:
RoundState
,
round_duration
:
f32
,
settings
:
GameSettings
,
player_id
:
PlayerId
,
cursor
:
Point2
<
f32
>
,
...
...
@@ -176,7 +180,9 @@ impl Game {
snapshots
,
cursor
:
cursor
.clone
(),
events
:
events_rx
,
round
:
RoundState
::
default
(),
round
,
last_round
:
None
,
round_duration
,
player_id
,
settings
,
settings_handle
:
Arc
::
clone
(
&
settings_handle
),
...
...
@@ -209,6 +215,8 @@ impl Game {
match
event
{
Event
::
RoundState
(
round
)
=>
{
info!
(
"transitioning to round state {:?}"
,
round
);
self
.last_round
=
Some
(
self
.round
);
self
.round_duration
=
0.0
;
self
.round
=
round
;
},
Event
::
Settings
(
settings
)
=>
{
...
...
@@ -240,8 +248,8 @@ impl Game {
/// Steps client prediction forward in time.
pub
fn
tick
(
&
mut
self
,
dt
:
f32
)
{
self
.round_duration
+=
dt
;
self
.handle_events
();
self
.round
.tick
(
dt
);
}
/// Updates the cursor position for this client player.
...
...
src/game/mod.rs
View file @
376c1e2f
use
crate
::
graphics
::
Circle
;
use
enum_kinds
::
EnumKind
;
use
nalgebra
::{
self
,
Point2
,
Vector2
};
use
palette
::
LinSrgb
;
use
serde
::{
Deserialize
,
Serialize
};
...
...
@@ -17,17 +16,18 @@ pub use self::snapshot::*;
pub
type
PlayerId
=
u16
;
/// Finite state machine for the round state.
#[derive(Debug,
Copy,
Clone,
Serialize,
Deserialize,
EnumKind)]
#[enum_kind(RoundStateKind)]
#[derive(Debug,
Copy,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq)]
pub
enum
RoundState
{
/// Less than two players, so nothing happens.
Lobby
,
/// More than two players, waiting for a round to start.
Waiting
(
f32
)
,
Waiting
,
/// More than one player left alive.
Round
,
/// One or less players alive, waiting for the round to end.
PostRound
(
f32
),
/// One or less players left alive, waiting to end the round.
RoundEnd
,
/// Winner declared, starting the next round.
Winner
(
Option
<
PlayerId
>
),
}
#[derive(Copy,
Clone,
Debug,
PartialEq,
Serialize,
Deserialize)]
...
...
@@ -212,47 +212,14 @@ impl Default for RoundState {
}
impl
RoundState
{
/// Waiting state, for the default initial time.
pub
fn
waiting
()
->
RoundState
{
RoundState
::
Waiting
(
3.0
)
// 3 seconds
}
/// Post-round state, for the default initial time.
pub
fn
post_round
()
->
RoundState
{
RoundState
::
PostRound
(
3.0
)
// 3 seconds
}
/// Whether a round is actually running in this state.
pub
fn
running
(
self
)
->
bool
{
match
self
{
RoundState
::
Round
|
RoundState
::
PostRound
(
_
)
=>
true
,
RoundState
::
Round
=>
true
,
RoundState
::
RoundEnd
=>
true
,
_
=>
false
,
}
}
/// Tick any timers on this state, returning a possible transition.
///
/// The reason that this doesn't just transition itself is that
/// sometimes you want to ignore the transition. For example, the
/// client should just wait for a message from the server.
pub
fn
tick
(
&
mut
self
,
dt
:
f32
)
->
Option
<
RoundState
>
{
match
self
{
RoundState
::
Waiting
(
ref
mut
time
)
=>
{
*
time
-=
dt
;
if
*
time
<
0.0
{
return
Some
(
RoundState
::
Round
);
}
},
RoundState
::
PostRound
(
ref
mut
time
)
=>
{
*
time
-=
dt
;
if
*
time
<
0.0
{
return
Some
(
RoundState
::
waiting
());
}
},
_
=>
(),
}
None
}
}
/// Steps over a given time interval in chunks of at most a specified duration.
...
...
src/game/server.rs
View file @
376c1e2f
...
...
@@ -57,6 +57,7 @@ pub struct Player {
pub
struct
Game
{
pub
players
:
HashMap
<
PlayerId
,
Player
>
,
pub
round
:
RoundState
,
pub
round_duration
:
f32
,
pub
settings
:
GameSettings
,
next_id
:
PlayerId
,
}
...
...
@@ -125,11 +126,48 @@ impl Game {
}
}
fn
switch_round
(
&
mut
self
,
round
:
RoundState
)
{
self
.round
=
round
;
self
.round_duration
=
0.0
;
}
/// Steps the whole game world forward in time.
pub
fn
tick
(
&
mut
self
,
dt
:
f32
)
->
impl
Iterator
<
Item
=
Event
>
{
let
transition
=
self
.round
.tick
(
dt
);
self
.round_duration
+=
dt
;
let
transition
=
match
self
.round
{
RoundState
::
Lobby
=>
None
,
RoundState
::
Waiting
=>
{
if
self
.round_duration
>
3.0
{
Some
(
RoundState
::
Round
)
}
else
{
None
}
},
RoundState
::
Round
=>
None
,
RoundState
::
RoundEnd
=>
{
if
self
.round_duration
>
2.0
{
// If a player is still alive, they win.
let
winner
=
self
.players
.iter
()
.filter
(|(
_
,
player
)|
player
.state
.alive
())
.next
()
.map
(|(
&
id
,
_
)|
id
);
Some
(
RoundState
::
Winner
(
winner
))
}
else
{
None
}
},
RoundState
::
Winner
(
_
)
=>
{
if
self
.round_duration
>
1.0
{
Some
(
RoundState
::
Waiting
)
}
else
{
None
}
},
};
if
let
Some
(
round
)
=
transition
{
self
.round
=
round
;
self
.
switch_
round
(
round
)
;
}
if
!
self
.round
.running
()
{
...
...
@@ -216,15 +254,15 @@ impl Game {
}
if
let
RoundState
::
Round
=
self
.round
{
//
End
the round if there are one or less players
still
// alive.
//
Start
the round
ending
if there are one or less players
//
still
alive.
let
num_alive
=
self
.players
.values
()
.filter
(|
player
|
player
.state
.alive
())
.count
();
if
num_alive
<=
1
{
self
.round
=
RoundState
::
post_r
ound
(
);
self
.
switch_
round
(
RoundState
::
R
ound
End
);
return
Some
(
Event
::
RoundState
(
self
.round
))
.into_iter
();
}
}
...
...
@@ -287,7 +325,7 @@ impl Game {
// Queue a waiting round if there are two or more players.
if
let
RoundState
::
Lobby
=
self
.round
{
if
self
.players
.len
()
>=
2
{
self
.round
=
RoundState
::
w
aiting
(
);
self
.
switch_
round
(
RoundState
::
W
aiting
);
events
.push
(
Event
::
RoundState
(
self
.round
));
}
}
...
...
@@ -306,7 +344,7 @@ impl Game {
events
.push
(
Event
::
RemovePlayer
(
id
));
// If there are less than two players left, stop the round.
if
self
.players
.len
()
<
2
{
self
.round
=
RoundState
::
Lobby
;
self
.
switch_
round
(
RoundState
::
Lobby
)
;
events
.push
(
Event
::
RoundState
(
self
.round
))
}
...
...
src/networking/client.rs
View file @
376c1e2f
...
...
@@ -556,12 +556,21 @@ impl Client {
match
packet
{
ServerPacket
::
Handshake
{
players
,
round
,
round_duration
,
settings
,
snapshot
,
id
,
}
=>
{
let
(
game
,
game_handle
)
=
Game
::
new
(
players
,
snapshot
,
settings
,
id
,
*
cursor
);
let
(
game
,
game_handle
)
=
Game
::
new
(
players
,
snapshot
,
round
,
round_duration
,
settings
,
id
,
*
cursor
,
);
let
tick
=
Interval
::
new
(
TICK_RATE
);
let
ping
=
Interval
::
new
(
PING_RATE
);
// Start the timer for sending input ticks and pings.
...
...
src/networking/server.rs
View file @
376c1e2f
...
...
@@ -5,7 +5,7 @@ use crate::game::{
GameSettings
,
GetPlayer
,
PlayerId
,
RoundState
Kind
,
RoundState
,
Snapshot
,
StaticPlayerState
,
};
...
...
@@ -60,6 +60,8 @@ pub enum ServerPacket {
id
:
PlayerId
,
settings
:
GameSettings
,
players
:
HashMap
<
PlayerId
,
StaticPlayerState
>
,
round
:
RoundState
,
round_duration
:
f32
,
snapshot
:
Snapshot
,
},
}
...
...
@@ -154,7 +156,7 @@ impl ServerPacket {
ServerPacket
::
Event
(
Event
::
RoundState
(
round
))
=>
{
// Only resend if the round state hasn't
// changed again since it was sent.
RoundStateKind
::
from
(
round
)
==
RoundStateKind
::
from
(
game
.round
)
*
round
==
game
.round
},
ServerPacket
::
Event
(
Event
::
Settings
(
settings
))
=>
{
// Only resend if the round state hasn't
...
...
@@ -444,6 +446,8 @@ impl Server {
.players
()
.map
(|(
id
,
player
)|
(
id
,
player
.static_state
()
.clone
()))
.collect
(),
round
:
self
.game.round
,
round_duration
:
self
.game.round_duration
,
snapshot
:
self
.game
.snapshot
(),
};
let
(
packet
,
_
)
=
client
.encode
(
&
packet
);
...
...
src/state.rs
View file @
376c1e2f
use
crate
::
debug
::
DebugState
;
use
crate
::
game
::{
clamp_cursor
,
client
::
Game
,
GameSettings
,
GetPlayer
};
use
crate
::
game
::{
clamp_cursor
,
client
::
Game
,
GameSettings
,
GetPlayer
,
RoundState
,
};
use
crate
::
graphics
::{
Circle
,
CircleRenderer
,
DrawContext
};
use
crate
::
networking
::{
self
,
client
::{
self
,
ClientHandle
,
ConnectedHandle
,
ConnectingHandle
},
server
::{
self
,
ServerHandle
},
};
use
easer
::
functions
::
*
;
use
gfx_hal
::
Backend
;
use
imgui
::{
im_str
,
ImString
,
Ui
};
use
log
::{
error
,
warn
};
...
...
@@ -24,14 +31,14 @@ use winit::{
const
SCALE
:
f32
=
0.9
;
fn
bounds_circle
(
settings
:
Option
<&
GameSettings
>
)
->
Circle
{
fn
bounds_circle
(
scale
:
f32
,
settings
:
Option
<&
GameSettings
>
)
->
Circle
{
let
bounds_radius
=
match
settings
{
Some
(
settings
)
=>
settings
.bounds_radius
,
None
=>
1.0
,
};
Circle
{
center
:
Point2
::
new
(
0.0
,
0.0
),
radius
:
SCALE
*
bounds_radius
,
radius
:
scale
*
bounds_radius
,
color
:
LinSrgb
::
new
(
1.0
,
1.0
,
1.0
),
}
}
...
...
@@ -257,7 +264,7 @@ impl GameState {
Screen
::
MainMenu
{
..
}
=>
{
circle_rend
.draw
(
ctx
,
iter
::
once
(
bounds_circle
(
None
)));
circle_rend
.draw
(
ctx
,
iter
::
once
(
bounds_circle
(
SCALE
,
None
)));
},
Screen
::
InGame
{
ref
mut
game
,
...
...
@@ -267,23 +274,71 @@ impl GameState {
game
.clean_old_snapshots
(
now
,
debug
.interpolation_delay
);
let
(
round_circles
,
scale
)
=
match
(
game
.last_round
,
game
.round
)
{
(
Some
(
RoundState
::
Winner
(
_
)),
RoundState
::
Waiting
)
=>
{
let
scale
=
Expo
::
ease_out
(
game
.round_duration
,
0.0
,
SCALE
,
0.3
,
);
(
None
,
scale
)
},
(
_
,
RoundState
::
Winner
(
winner
))
=>
{
// No winner is black
let
color
=
match
winner
{
Some
(
id
)
=>
game
.players
[
&
id
]
.color
,
None
=>
LinSrgb
::
new
(
0.5
,
0.5
,
0.5
),
};
let
radius
=
Expo
::
ease_out
(
game
.round_duration
,
0.0
,
game
.settings
()
.bounds_radius
,
0.3
,
);
let
scale
=
if
game
.round_duration
>
0.5
{
Expo
::
ease_in
(
game
.round_duration
-
0.5
,
SCALE
,
-
SCALE
,
0.3
,
)
.max
(
0.0
)
}
else
{
SCALE
};
(
Some
(
Circle
{
center
:
Point2
::
new
(
0.0
,
0.0
),
radius
:
scale
*
radius
,
color
,
}),
scale
,
)
},
_
=>
(
None
,
SCALE
),
};
let
players
=
game
.interpolated_players
(
now
,
clamp_cursor
(
self
.cursor
,
game
.settings
()),
debug
.interpolation_delay
,
);
let
circles
=
players
.into_iter
()
.flat_map
(|(
_
,
player
)|
{
player
.draw
(
SCALE
,
game
.settings
())
player
.draw
(
scale
,
game
.settings
())
});
let
bounds_circle
=
bounds_circle
(
Some
(
game
.settings
()));
let
bounds_circle
=
bounds_circle
(
scale
,
Some
(
game
.settings
()));
if
debug
.draw_latest_snapshot
{
let
players
=
game
.latest_players
();
let
debug_circles
=
players
.into_iter
()
.flat_map
(|(
_
,
player
)|
{
player
.draw
(
SCALE
,
game
.settings
())
player
.draw
(
scale
,
game
.settings
())
})
.map
(|
circle
|
{
Circle
{
...
...
@@ -295,11 +350,16 @@ impl GameState {
ctx
,
iter
::
once
(
bounds_circle
)
.chain
(
debug_circles
)
.chain
(
circles
),
.chain
(
circles
)
.chain
(
round_circles
),
);
}
else
{
circle_rend
.draw
(
ctx
,
iter
::
once
(
bounds_circle
)
.chain
(
circles
));
circle_rend
.draw
(
ctx
,
iter
::
once
(
bounds_circle
)
.chain
(
circles
)
.chain
(
round_circles
),
);
}
},
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment