diff --git a/Apps/Playground/Scripts/experience.js b/Apps/Playground/Scripts/experience.js index b9c9d6f4a..16f09cb59 100644 --- a/Apps/Playground/Scripts/experience.js +++ b/Apps/Playground/Scripts/experience.js @@ -162,6 +162,46 @@ CreateBoxAsync(scene).then(function () { context.fillStyle = gradient; context.fillRect(10, 310, 400, 100); + + context.lineWidth = 5; + // Rounded rectangle with zero radius (specified as a number) + context.strokeStyle = "red"; + context.beginPath(); + context.roundRect(10, 220, 150, 100, 0); + context.stroke(); + + // Rounded rectangle with 40px radius (single element list) + context.strokeStyle = "blue"; + context.beginPath(); + context.roundRect(10, 220, 150, 100, [40]); + context.stroke(); + + // Rounded rectangle with 2 different radii + context.strokeStyle = "orange"; + context.beginPath(); + context.roundRect(10, 350, 150, 100, [10, 40]); + context.stroke(); + + // Rounded rectangle with four different radii + context.strokeStyle = "green"; + context.beginPath(); + context.roundRect(200, 220, 200, 100, [0, 30, 50, 60]); + context.stroke(); + + // Same rectangle drawn backwards + context.strokeStyle = "magenta"; + context.beginPath(); + context.roundRect(400, 350, -200, 100, [0, 30, 50, 60]); + context.stroke(); + + // Draw clipped round rect + // TODO: this is currently broken, clipping area does not have round corners + context.beginPath(); + context.roundRect(40, 450, 100, 50, 10); + context.clip(); + context.fillStyle = "blue"; + context.fillRect(0, 0, 1000, 1000); + // tick update dynamicTexture.update(); t += 0.01; diff --git a/Polyfills/Canvas/Source/Context.cpp b/Polyfills/Canvas/Source/Context.cpp index 7598c3413..f6b35679d 100644 --- a/Polyfills/Canvas/Source/Context.cpp +++ b/Polyfills/Canvas/Source/Context.cpp @@ -54,6 +54,7 @@ namespace Babylon::Polyfills::Internal InstanceMethod("translate", &Context::Translate), InstanceMethod("strokeRect", &Context::StrokeRect), InstanceMethod("rect", &Context::Rect), + InstanceMethod("roundRect", &Context::RoundRect), InstanceMethod("clip", &Context::Clip), InstanceMethod("putImageData", &Context::PutImageData), InstanceMethod("arc", &Context::Arc), @@ -178,7 +179,7 @@ namespace Babylon::Polyfills::Internal { throw Napi::Error::New(info.Env(), "Fillstyle is not a color string or a gradient."); } - + nvgFill(*m_nvg); SetDirty(); } @@ -331,6 +332,62 @@ namespace Babylon::Polyfills::Internal SetDirty(); } + void Context::RoundRect(const Napi::CallbackInfo& info) + { + const auto x = info[0].As().FloatValue(); + const auto y = info[1].As().FloatValue(); + const auto width = info[2].As().FloatValue(); + const auto height = info[3].As().FloatValue(); + const auto radii = info[4]; + + if (radii.IsNumber()) + { + const auto radius = radii.As().FloatValue(); + nvgRoundedRect(*m_nvg, x, y, width, height, radius); + } + else + { + const auto radiiArray = radii.As(); + const auto radiiArrayLength = radiiArray.Length(); + if (radiiArrayLength == 1) + { + const auto radius = radiiArray[0u].As().FloatValue(); + nvgRoundedRect(*m_nvg, x, y, width, height, radius); + } + else if (radiiArrayLength == 2) + { + const auto topLeftBottomRight = radiiArray[0u].As().FloatValue(); + const auto topRightBottomLeft = radiiArray[1].As().FloatValue(); + + nvgRoundedRectVarying(*m_nvg, x, y, width, height, topLeftBottomRight, topRightBottomLeft, topLeftBottomRight, topRightBottomLeft); + } + else if (radiiArrayLength == 3) + { + const auto topLeft = radiiArray[0u].As().FloatValue(); + const auto topRightBottomLeft = radiiArray[1].As().FloatValue(); + const auto bottomRight = radiiArray[2].As().FloatValue(); + + nvgRoundedRectVarying(*m_nvg, x, y, width, height, topLeft, topRightBottomLeft, bottomRight, topRightBottomLeft); + } + else if (radiiArrayLength == 4) + { + const auto topLeft = radiiArray[0u].As().FloatValue(); + const auto topRight = radiiArray[1].As().FloatValue(); + const auto bottomRight = radiiArray[2].As().FloatValue(); + const auto bottomLeft = radiiArray[3].As().FloatValue(); + + nvgRoundedRectVarying(*m_nvg, x, y, width, height, topLeft, topRight, bottomRight, bottomLeft); + } + else + { + throw Napi::Error::New(info.Env(), "Invalid number of parameters for radii"); + } + } + + m_rectangleClipping = {left : x, top : y, width, height}; + SetDirty(); + } + void Context::Clip(const Napi::CallbackInfo& /*info*/) { m_isClipped = true; @@ -655,7 +712,7 @@ namespace Babylon::Polyfills::Internal const auto y0 = info[1].As().FloatValue(); const auto x1 = info[2].As().FloatValue(); const auto y1 = info[3].As().FloatValue(); - + auto gradient = CanvasGradient::CreateLinear(info.Env(), m_nvg, x0, y0, x1, y1); return gradient; } diff --git a/Polyfills/Canvas/Source/Context.h b/Polyfills/Canvas/Source/Context.h index a0f3a417b..07e842e3e 100644 --- a/Polyfills/Canvas/Source/Context.h +++ b/Polyfills/Canvas/Source/Context.h @@ -37,6 +37,7 @@ namespace Babylon::Polyfills::Internal void ClosePath(const Napi::CallbackInfo&); void Clip(const Napi::CallbackInfo&); void Rect(const Napi::CallbackInfo&); + void RoundRect(const Napi::CallbackInfo&); void StrokeRect(const Napi::CallbackInfo&); void Stroke(const Napi::CallbackInfo&); void MoveTo(const Napi::CallbackInfo&); @@ -89,7 +90,7 @@ namespace Babylon::Polyfills::Internal std::string m_font{}; std::variant m_fillStyle{}; std::string m_strokeStyle{}; - std::string m_lineCap{}; // 'butt', 'round', 'square' + std::string m_lineCap{}; // 'butt', 'round', 'square' std::string m_lineJoin{}; // 'round', 'bevel', 'miter' float m_miterLimit{0.f}; float m_lineWidth{0.f};