1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

⚠️ Rename Rounding to CornerRadius (#5673)

Breaking change!

* `Rounding` -> `CornerRadius`
* `rounding` -> `corner_radius`

This is to:
* Clarify
* Conform to other systems (e.g. Figma)
* Avoid confusion with `GuiRounding`
This commit is contained in:
Emil Ernerfeldt
2025-02-04 12:53:18 +01:00
committed by GitHub
parent 3c07e01d08
commit 23ed49334e
44 changed files with 378 additions and 330 deletions

View File

@@ -7,10 +7,10 @@
/// The rounding uses `u8` to save space,
/// so the amount of rounding is limited to integers in the range `[0, 255]`.
///
/// For calculations, you may want to use [`crate::Roundingf`] instead, which uses `f32`.
/// For calculations, you may want to use [`crate::CornerRadiusF32`] instead, which uses `f32`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Rounding {
pub struct CornerRadius {
/// Radius of the rounding of the North-West (left top) corner.
pub nw: u8,
@@ -24,28 +24,28 @@ pub struct Rounding {
pub se: u8,
}
impl Default for Rounding {
impl Default for CornerRadius {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
impl From<u8> for Rounding {
impl From<u8> for CornerRadius {
#[inline]
fn from(radius: u8) -> Self {
Self::same(radius)
}
}
impl From<f32> for Rounding {
impl From<f32> for CornerRadius {
#[inline]
fn from(radius: f32) -> Self {
Self::same(radius.round() as u8)
}
}
impl Rounding {
impl CornerRadius {
/// No rounding on any corner.
pub const ZERO: Self = Self {
nw: 0,
@@ -99,7 +99,7 @@ impl Rounding {
}
}
impl std::ops::Add for Rounding {
impl std::ops::Add for CornerRadius {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
@@ -112,7 +112,7 @@ impl std::ops::Add for Rounding {
}
}
impl std::ops::Add<u8> for Rounding {
impl std::ops::Add<u8> for CornerRadius {
type Output = Self;
#[inline]
fn add(self, rhs: u8) -> Self {
@@ -125,7 +125,7 @@ impl std::ops::Add<u8> for Rounding {
}
}
impl std::ops::AddAssign for Rounding {
impl std::ops::AddAssign for CornerRadius {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = Self {
@@ -137,7 +137,7 @@ impl std::ops::AddAssign for Rounding {
}
}
impl std::ops::AddAssign<u8> for Rounding {
impl std::ops::AddAssign<u8> for CornerRadius {
#[inline]
fn add_assign(&mut self, rhs: u8) {
*self = Self {
@@ -149,7 +149,7 @@ impl std::ops::AddAssign<u8> for Rounding {
}
}
impl std::ops::Sub for Rounding {
impl std::ops::Sub for CornerRadius {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
@@ -162,7 +162,7 @@ impl std::ops::Sub for Rounding {
}
}
impl std::ops::Sub<u8> for Rounding {
impl std::ops::Sub<u8> for CornerRadius {
type Output = Self;
#[inline]
fn sub(self, rhs: u8) -> Self {
@@ -175,7 +175,7 @@ impl std::ops::Sub<u8> for Rounding {
}
}
impl std::ops::SubAssign for Rounding {
impl std::ops::SubAssign for CornerRadius {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = Self {
@@ -187,7 +187,7 @@ impl std::ops::SubAssign for Rounding {
}
}
impl std::ops::SubAssign<u8> for Rounding {
impl std::ops::SubAssign<u8> for CornerRadius {
#[inline]
fn sub_assign(&mut self, rhs: u8) {
*self = Self {
@@ -199,7 +199,7 @@ impl std::ops::SubAssign<u8> for Rounding {
}
}
impl std::ops::Div<f32> for Rounding {
impl std::ops::Div<f32> for CornerRadius {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self {
@@ -212,7 +212,7 @@ impl std::ops::Div<f32> for Rounding {
}
}
impl std::ops::DivAssign<f32> for Rounding {
impl std::ops::DivAssign<f32> for CornerRadius {
#[inline]
fn div_assign(&mut self, rhs: f32) {
*self = Self {
@@ -224,7 +224,7 @@ impl std::ops::DivAssign<f32> for Rounding {
}
}
impl std::ops::Mul<f32> for Rounding {
impl std::ops::Mul<f32> for CornerRadius {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self {
@@ -237,7 +237,7 @@ impl std::ops::Mul<f32> for Rounding {
}
}
impl std::ops::MulAssign<f32> for Rounding {
impl std::ops::MulAssign<f32> for CornerRadius {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
*self = Self {

View File

@@ -1,11 +1,11 @@
use crate::Rounding;
use crate::CornerRadius;
/// How rounded the corners of things should be, in `f32`.
///
/// This is used for calculations, but storage is usually done with the more compact [`Rounding`].
/// This is used for calculations, but storage is usually done with the more compact [`CornerRadius`].
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Roundingf {
pub struct CornerRadiusF32 {
/// Radius of the rounding of the North-West (left top) corner.
pub nw: f32,
@@ -19,38 +19,38 @@ pub struct Roundingf {
pub se: f32,
}
impl From<Rounding> for Roundingf {
impl From<CornerRadius> for CornerRadiusF32 {
#[inline]
fn from(rounding: Rounding) -> Self {
fn from(cr: CornerRadius) -> Self {
Self {
nw: rounding.nw as f32,
ne: rounding.ne as f32,
sw: rounding.sw as f32,
se: rounding.se as f32,
nw: cr.nw as f32,
ne: cr.ne as f32,
sw: cr.sw as f32,
se: cr.se as f32,
}
}
}
impl From<Roundingf> for Rounding {
impl From<CornerRadiusF32> for CornerRadius {
#[inline]
fn from(rounding: Roundingf) -> Self {
fn from(cr: CornerRadiusF32) -> Self {
Self {
nw: rounding.nw.round() as u8,
ne: rounding.ne.round() as u8,
sw: rounding.sw.round() as u8,
se: rounding.se.round() as u8,
nw: cr.nw.round() as u8,
ne: cr.ne.round() as u8,
sw: cr.sw.round() as u8,
se: cr.se.round() as u8,
}
}
}
impl Default for Roundingf {
impl Default for CornerRadiusF32 {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
impl From<f32> for Roundingf {
impl From<f32> for CornerRadiusF32 {
#[inline]
fn from(radius: f32) -> Self {
Self {
@@ -62,7 +62,7 @@ impl From<f32> for Roundingf {
}
}
impl Roundingf {
impl CornerRadiusF32 {
/// No rounding on any corner.
pub const ZERO: Self = Self {
nw: 0.0,
@@ -111,7 +111,7 @@ impl Roundingf {
}
}
impl std::ops::Add for Roundingf {
impl std::ops::Add for CornerRadiusF32 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
@@ -124,7 +124,7 @@ impl std::ops::Add for Roundingf {
}
}
impl std::ops::AddAssign for Roundingf {
impl std::ops::AddAssign for CornerRadiusF32 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = Self {
@@ -136,7 +136,7 @@ impl std::ops::AddAssign for Roundingf {
}
}
impl std::ops::AddAssign<f32> for Roundingf {
impl std::ops::AddAssign<f32> for CornerRadiusF32 {
#[inline]
fn add_assign(&mut self, rhs: f32) {
*self = Self {
@@ -148,7 +148,7 @@ impl std::ops::AddAssign<f32> for Roundingf {
}
}
impl std::ops::Sub for Roundingf {
impl std::ops::Sub for CornerRadiusF32 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
@@ -161,7 +161,7 @@ impl std::ops::Sub for Roundingf {
}
}
impl std::ops::SubAssign for Roundingf {
impl std::ops::SubAssign for CornerRadiusF32 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = Self {
@@ -173,7 +173,7 @@ impl std::ops::SubAssign for Roundingf {
}
}
impl std::ops::SubAssign<f32> for Roundingf {
impl std::ops::SubAssign<f32> for CornerRadiusF32 {
#[inline]
fn sub_assign(&mut self, rhs: f32) {
*self = Self {
@@ -185,7 +185,7 @@ impl std::ops::SubAssign<f32> for Roundingf {
}
}
impl std::ops::Div<f32> for Roundingf {
impl std::ops::Div<f32> for CornerRadiusF32 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self {
@@ -198,7 +198,7 @@ impl std::ops::Div<f32> for Roundingf {
}
}
impl std::ops::DivAssign<f32> for Roundingf {
impl std::ops::DivAssign<f32> for CornerRadiusF32 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
*self = Self {
@@ -210,7 +210,7 @@ impl std::ops::DivAssign<f32> for Roundingf {
}
}
impl std::ops::Mul<f32> for Roundingf {
impl std::ops::Mul<f32> for CornerRadiusF32 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self {
@@ -223,7 +223,7 @@ impl std::ops::Mul<f32> for Roundingf {
}
}
impl std::ops::MulAssign<f32> for Roundingf {
impl std::ops::MulAssign<f32> for CornerRadiusF32 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
*self = Self {

View File

@@ -25,13 +25,13 @@
mod brush;
pub mod color;
mod corner_radius;
mod corner_radius_f32;
pub mod image;
mod margin;
mod marginf;
mod mesh;
pub mod mutex;
mod rounding;
mod roundingf;
mod shadow;
pub mod shape_transform;
mod shapes;
@@ -48,12 +48,12 @@ mod viewport;
pub use self::{
brush::Brush,
color::ColorMode,
corner_radius::CornerRadius,
corner_radius_f32::CornerRadiusF32,
image::{ColorImage, FontImage, ImageData, ImageDelta},
margin::Margin,
marginf::Marginf,
mesh::{Mesh, Mesh16, Vertex},
rounding::Rounding,
roundingf::Roundingf,
shadow::Shadow,
shapes::{
CircleShape, CubicBezierShape, EllipseShape, PaintCallback, PaintCallbackInfo, PathShape,
@@ -69,6 +69,9 @@ pub use self::{
viewport::ViewportInPixels,
};
#[deprecated = "Renamed to CornerRadius"]
pub type Rounding = CornerRadius;
#[allow(deprecated)]
pub use tessellator::tessellate_shapes;

View File

@@ -1,4 +1,4 @@
use crate::{Color32, Marginf, Rect, RectShape, Rounding, Vec2};
use crate::{Color32, CornerRadius, Marginf, Rect, RectShape, Vec2};
/// The color and fuzziness of a fuzzy shape.
///
@@ -44,7 +44,7 @@ impl Shadow {
};
/// The argument is the rectangle of the shadow caster.
pub fn as_shape(&self, rect: Rect, rounding: impl Into<Rounding>) -> RectShape {
pub fn as_shape(&self, rect: Rect, corner_radius: impl Into<CornerRadius>) -> RectShape {
// tessellator.clip_rect = clip_rect; // TODO(emilk): culling
let Self {
@@ -58,9 +58,9 @@ impl Shadow {
let rect = rect
.translate(Vec2::new(offset_x as _, offset_y as _))
.expand(spread as _);
let rounding = rounding.into() + Rounding::from(spread);
let corner_radius = corner_radius.into() + CornerRadius::from(spread);
RectShape::filled(rect, rounding, color).with_blur_width(blur as _)
RectShape::filled(rect, corner_radius, color).with_blur_width(blur as _)
}
/// How much larger than the parent rect are we in each direction?

View File

@@ -60,7 +60,7 @@ pub fn adjust_colors(
})
| Shape::Rect(RectShape {
rect: _,
rounding: _,
corner_radius: _,
fill,
stroke,
stroke_kind: _,

View File

@@ -10,7 +10,7 @@ pub struct RectShape {
/// How rounded the corners of the rectangle are.
///
/// Use `Rounding::ZERO` for for sharp corners.
/// Use [`CornerRadius::ZERO`] for for sharp corners.
///
/// This is the corner radii of the rectangle.
/// If there is a stroke, then the stroke will have an inner and outer corner radius,
@@ -18,7 +18,7 @@ pub struct RectShape {
///
/// For [`StrokeKind::Inside`], the outside of the stroke coincides with the rectangle,
/// so the rounding will in this case specify the outer corner radius.
pub rounding: Rounding,
pub corner_radius: CornerRadius,
/// How to fill the rectangle.
pub fill: Color32,
@@ -73,14 +73,14 @@ impl RectShape {
#[inline]
pub fn new(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
fill_color: impl Into<Color32>,
stroke: impl Into<Stroke>,
stroke_kind: StrokeKind,
) -> Self {
Self {
rect,
rounding: rounding.into(),
corner_radius: corner_radius.into(),
fill: fill_color.into(),
stroke: stroke.into(),
stroke_kind,
@@ -93,12 +93,12 @@ impl RectShape {
#[inline]
pub fn filled(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
fill_color: impl Into<Color32>,
) -> Self {
Self::new(
rect,
rounding,
corner_radius,
fill_color,
Stroke::NONE,
StrokeKind::Outside, // doesn't matter
@@ -108,12 +108,12 @@ impl RectShape {
#[inline]
pub fn stroke(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
stroke: impl Into<Stroke>,
stroke_kind: StrokeKind,
) -> Self {
let fill = Color32::TRANSPARENT;
Self::new(rect, rounding, fill, stroke, stroke_kind)
Self::new(rect, corner_radius, fill, stroke, stroke_kind)
}
/// Set if the stroke is on the inside, outside, or centered on the rectangle.

View File

@@ -7,7 +7,7 @@ use emath::{pos2, Align2, Pos2, Rangef, Rect, TSTransform, Vec2};
use crate::{
stroke::PathStroke,
text::{FontId, Fonts, Galley},
Color32, Mesh, Rounding, Stroke, StrokeKind, TextureId,
Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId,
};
use super::{
@@ -279,21 +279,21 @@ impl Shape {
#[inline]
pub fn rect_filled(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
fill_color: impl Into<Color32>,
) -> Self {
Self::Rect(RectShape::filled(rect, rounding, fill_color))
Self::Rect(RectShape::filled(rect, corner_radius, fill_color))
}
/// See also [`Self::rect_filled`].
#[inline]
pub fn rect_stroke(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
stroke: impl Into<Stroke>,
stroke_kind: StrokeKind,
) -> Self {
Self::Rect(RectShape::stroke(rect, rounding, stroke, stroke_kind))
Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))
}
#[allow(clippy::needless_pass_by_value)]
@@ -451,7 +451,7 @@ impl Shape {
}
Self::Rect(rect_shape) => {
rect_shape.rect = transform * rect_shape.rect;
rect_shape.rounding *= transform.scaling;
rect_shape.corner_radius *= transform.scaling;
rect_shape.stroke.width *= transform.scaling;
rect_shape.blur_width *= transform.scaling;
}

View File

@@ -9,8 +9,8 @@ use emath::{pos2, remap, vec2, GuiRounding as _, NumExt, Pos2, Rect, Rot2, Vec2}
use crate::{
color::ColorMode, emath, stroke::PathStroke, texture_atlas::PreparedDisc, CircleShape,
ClippedPrimitive, ClippedShape, Color32, CubicBezierShape, EllipseShape, Mesh, PathShape,
Primitive, QuadraticBezierShape, RectShape, Roundingf, Shape, Stroke, StrokeKind, TextShape,
ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape, EllipseShape, Mesh,
PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke, StrokeKind, TextShape,
TextureId, Vertex, WHITE_UV,
};
@@ -534,19 +534,19 @@ impl Path {
pub mod path {
//! Helpers for constructing paths
use crate::Roundingf;
use crate::CornerRadiusF32;
use emath::{pos2, Pos2, Rect};
/// overwrites existing points
pub fn rounded_rectangle(path: &mut Vec<Pos2>, rect: Rect, rounding: Roundingf) {
pub fn rounded_rectangle(path: &mut Vec<Pos2>, rect: Rect, cr: CornerRadiusF32) {
path.clear();
let min = rect.min;
let max = rect.max;
let r = clamp_rounding(rounding, rect);
let cr = clamp_corner_radius(cr, rect);
if r == Roundingf::ZERO {
if cr == CornerRadiusF32::ZERO {
path.reserve(4);
path.push(pos2(min.x, min.y)); // left top
path.push(pos2(max.x, min.y)); // right top
@@ -557,27 +557,27 @@ pub mod path {
// Duplicated vertices can happen when one side is all rounding, with no straight edge between.
let eps = f32::EPSILON * rect.size().max_elem();
add_circle_quadrant(path, pos2(max.x - r.se, max.y - r.se), r.se, 0.0); // south east
add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); // south east
if rect.width() <= r.se + r.sw + eps {
if rect.width() <= cr.se + cr.sw + eps {
path.pop(); // avoid duplicated vertex
}
add_circle_quadrant(path, pos2(min.x + r.sw, max.y - r.sw), r.sw, 1.0); // south west
add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); // south west
if rect.height() <= r.sw + r.nw + eps {
if rect.height() <= cr.sw + cr.nw + eps {
path.pop(); // avoid duplicated vertex
}
add_circle_quadrant(path, pos2(min.x + r.nw, min.y + r.nw), r.nw, 2.0); // north west
add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); // north west
if rect.width() <= r.nw + r.ne + eps {
if rect.width() <= cr.nw + cr.ne + eps {
path.pop(); // avoid duplicated vertex
}
add_circle_quadrant(path, pos2(max.x - r.ne, min.y + r.ne), r.ne, 3.0); // north east
add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); // north east
if rect.height() <= r.ne + r.se + eps {
if rect.height() <= cr.ne + cr.se + eps {
path.pop(); // avoid duplicated vertex
}
}
@@ -633,11 +633,11 @@ pub mod path {
}
// Ensures the radius of each corner is within a valid range
fn clamp_rounding(rounding: Roundingf, rect: Rect) -> Roundingf {
fn clamp_corner_radius(cr: CornerRadiusF32, rect: Rect) -> CornerRadiusF32 {
let half_width = rect.width() * 0.5;
let half_height = rect.height() * 0.5;
let max_cr = half_width.min(half_height);
rounding.at_most(max_cr).at_least(0.0)
cr.at_most(max_cr).at_least(0.0)
}
}
@@ -1729,7 +1729,7 @@ impl Tessellator {
let brush = rect_shape.brush.as_ref();
let RectShape {
mut rect,
rounding,
corner_radius,
mut fill,
mut stroke,
mut stroke_kind,
@@ -1738,7 +1738,7 @@ impl Tessellator {
brush: _, // brush is extracted on its own, because it is not Copy
} = *rect_shape;
let mut rounding = Roundingf::from(rounding);
let mut corner_radius = CornerRadiusF32::from(corner_radius);
let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels);
let pixel_size = 1.0 / self.pixels_per_point;
@@ -1848,7 +1848,7 @@ impl Tessellator {
.at_most(rect.size().min_elem() - eps - 2.0 * stroke.width)
.at_least(0.0);
rounding += 0.5 * blur_width;
corner_radius += 0.5 * blur_width;
self.feathering = self.feathering.max(blur_width);
}
@@ -1858,54 +1858,54 @@ impl Tessellator {
// We do this because `path::rounded_rectangle` uses the
// corner radius to pick the fidelity/resolution of the corner.
let original_rounding = rounding;
let original_cr = corner_radius;
match stroke_kind {
StrokeKind::Inside => {}
StrokeKind::Middle => {
rect = rect.expand(stroke.width / 2.0);
rounding += stroke.width / 2.0;
corner_radius += stroke.width / 2.0;
}
StrokeKind::Outside => {
rect = rect.expand(stroke.width);
rounding += stroke.width;
corner_radius += stroke.width;
}
}
stroke_kind = StrokeKind::Inside;
// A small rounding is incompatible with a wide stroke,
// A small corner_radius is incompatible with a wide stroke,
// because the small bend will be extruded inwards and cross itself.
// There are two ways to solve this (wile maintaining constant stroke width):
// either we increase the rounding, or we set it to zero.
// We choose the former: if the user asks for _any_ rounding, they should get it.
// either we increase the corner_radius, or we set it to zero.
// We choose the former: if the user asks for _any_ corner_radius, they should get it.
let min_inside_rounding = 0.1; // Large enough to avoid numerical issues
let min_outside_rounding = stroke.width + min_inside_rounding;
let min_inside_cr = 0.1; // Large enough to avoid numerical issues
let min_outside_cr = stroke.width + min_inside_cr;
let extra_rounding_tweak = 0.4; // Otherwise is doesn't _feels_ enough.
let extra_cr_tweak = 0.4; // Otherwise is doesn't _feels_ enough.
if 0.0 < original_rounding.nw {
rounding.nw += extra_rounding_tweak;
rounding.nw = rounding.nw.at_least(min_outside_rounding);
if 0.0 < original_cr.nw {
corner_radius.nw += extra_cr_tweak;
corner_radius.nw = corner_radius.nw.at_least(min_outside_cr);
}
if 0.0 < original_rounding.ne {
rounding.ne += extra_rounding_tweak;
rounding.ne = rounding.ne.at_least(min_outside_rounding);
if 0.0 < original_cr.ne {
corner_radius.ne += extra_cr_tweak;
corner_radius.ne = corner_radius.ne.at_least(min_outside_cr);
}
if 0.0 < original_rounding.sw {
rounding.sw += extra_rounding_tweak;
rounding.sw = rounding.sw.at_least(min_outside_rounding);
if 0.0 < original_cr.sw {
corner_radius.sw += extra_cr_tweak;
corner_radius.sw = corner_radius.sw.at_least(min_outside_cr);
}
if 0.0 < original_rounding.se {
rounding.se += extra_rounding_tweak;
rounding.se = rounding.se.at_least(min_outside_rounding);
if 0.0 < original_cr.se {
corner_radius.se += extra_cr_tweak;
corner_radius.se = corner_radius.se.at_least(min_outside_cr);
}
}
let path = &mut self.scratchpad_path;
path.clear();
path::rounded_rectangle(&mut self.scratchpad_points, rect, rounding);
path::rounded_rectangle(&mut self.scratchpad_points, rect, corner_radius);
path.add_line_loop(&self.scratchpad_points);
let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind);