Skip to content

Commit

Permalink
Merge pull request #3107 from t3du/DrawRoundedRect
Browse files Browse the repository at this point in the history
Draw rounded rect node
  • Loading branch information
luboslenco authored Dec 24, 2024
2 parents 1c610ed + 3f89208 commit 0fe472d
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 0 deletions.
118 changes: 118 additions & 0 deletions armory/Sources/armory/logicnode/DrawRoundedRectNode.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package armory.logicnode;

import kha.Color;
import kha.math.Vector2;
import armory.renderpath.RenderToTexture;

#if arm_ui
using zui.GraphicsExtension;
#end

class DrawRoundedRectNode extends LogicNode {

var vertices: Array<Vector2>;

public function new(tree: LogicTree) {
super(tree);
}

override function run(from: Int) {
#if arm_ui
RenderToTexture.ensure2DContext("DrawPolygonNode");

final anchorH: Int = inputs[4].get();
final anchorV: Int = inputs[5].get();
final x: Float = inputs[8].get();
final y: Float = inputs[9].get();
final width: Float = inputs[10].get();
final height: Float = inputs[11].get();
final drawx = x - 0.5 * width * anchorH;
final drawy = y - 0.5 * height * anchorV;
final angle: Float = inputs[12].get();

var vertArr: Dynamic = drawRoundedRect(drawx, drawy, width, height, inputs[7].get(), inputs[6].get());

if (vertices == null || vertices.length != vertArr.length) {
// Preallocate
vertices = [];
vertices.resize(vertArr.length);
for (i in 0...vertices.length) {
vertices[i] = new Vector2();
}
}

for (i in 0...vertArr.length) {
if (Std.isOfType(vertArr[i], iron.math.Vec4) || Std.isOfType(vertArr[i], iron.math.Vec3)){
vertices[i].x = vertArr[i].x;
vertices[i].y = vertArr[i].y;
} else {
assert(Error, vertArr[i].length >= 2, "Array positions must be an array of two dimensions (X, Y)");
vertices[i].x = vertArr[i][0];
vertices[i].y = vertArr[i][1];
}
}

RenderToTexture.g.rotate(angle, x, y);
final colorVec = inputs[1].get();
RenderToTexture.g.color = Color.fromFloats(colorVec.x, colorVec.y, colorVec.z, colorVec.w);

if (inputs[2].get()) {
RenderToTexture.g.fillPolygon(0, 0, vertices);
} else {
RenderToTexture.g.drawPolygon(0, 0, vertices, inputs[3].get());
}
#end

RenderToTexture.g.rotate(-angle, x, y);
runOutput(0);
}

function drawRoundedRect(x:Float, y:Float, width:Float, height:Float, radius:Float, segments:Int):Array<Array<Float>> {
var vertices:Array<Array<Float>> = [];

vertices.push([x + radius, y]);
vertices.push([x + width - radius, y]);

for (i in 0...segments) {
var angle:Float = Math.PI / 2 * (segments - i) / segments;
vertices.push([
x + width - radius + Math.cos(angle) * radius,
y + radius - Math.sin(angle) * radius
]);
}

vertices.push([x + width, y + radius]);
vertices.push([x + width, y + height - radius]);

for (i in 0...segments) {
var angle:Float = Math.PI / 2 * i / segments;
vertices.push([
x + width - radius + Math.cos(angle) * radius,
y + height - radius + Math.sin(angle) * radius
]);
}

vertices.push([x + width - radius, y + height]);
vertices.push([x + radius, y + height]);

for (i in 0...segments) {
var angle:Float = Math.PI / 2 * (segments - i) / segments;
vertices.push([
x + radius - Math.cos(angle) * radius,
y + height - radius + Math.sin(angle) * radius
]);
}

vertices.push([x, y + height - radius]);

for (i in 0...segments) {
var angle:Float = Math.PI / 2 * i / segments;
vertices.push([
x + radius - Math.cos(angle) * radius,
y + radius - Math.sin(angle) * radius
]);
}

return vertices;
}
}
48 changes: 48 additions & 0 deletions armory/blender/arm/logicnode/draw/LN_draw_rounded_rect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from arm.logicnode.arm_nodes import *


class DrawRoundedRectNode(ArmLogicTreeNode):
"""Draws a rectangle.
@input Draw: Activate to draw the rectangle on this frame. The input must
be (indirectly) called from an `On Render2D` node.
@input Color: The color of the rectangle.
@input Filled: Whether the rectangle is filled or only the outline is drawn.
@input Strength: The line strength if the rectangle is not filled.
@input Left/Center/Right: Horizontal anchor point of the rectangel.
0 = Left, 1 = Center, 2 = Right
@input Top/Middle/Bottom: Vertical anchor point of the rectangel.
0 = Top, 1 = Middle, 2 = Bottom
@input Segments: number of points to consider for rounded rect corners.
@input Radius: radius of the rounded rect.
@input X/Y: Position of the anchor point in pixels.
@input Width/Height: Size of the rectangle in pixels.
@input Angle: Rotation angle in radians. Rectangle will be rotated cloclwiswe
at the anchor point.
@output Out: Activated after the rectangle has been drawn.
@see [`kha.graphics2.Graphics.drawRect()`](http://kha.tech/api/kha/graphics2/Graphics.html#drawRect).
@see [`kha.graphics2.Graphics.fillRect()`](http://kha.tech/api/kha/graphics2/Graphics.html#fillRect).
"""
bl_idname = 'LNDrawRoundedRectNode'
bl_label = 'Draw Rounded Rect'
arm_section = 'draw'
arm_version = 1

def arm_init(self, context):
self.add_input('ArmNodeSocketAction', 'Draw')
self.add_input('ArmColorSocket', 'Color', default_value=[1.0, 1.0, 1.0, 1.0])
self.add_input('ArmBoolSocket', 'Filled', default_value=False)
self.add_input('ArmFloatSocket', 'Strength', default_value=1.0)
self.add_input('ArmIntSocket', '0/1/2 = Left/Center/Right', default_value=0)
self.add_input('ArmIntSocket', '0/1/2 = Top/Middle/Bottom', default_value=0)
self.add_input('ArmIntSocket', 'Segments', default_value=10)
self.add_input('ArmFloatSocket', 'Radius')
self.add_input('ArmFloatSocket', 'X')
self.add_input('ArmFloatSocket', 'Y')
self.add_input('ArmFloatSocket', 'Width')
self.add_input('ArmFloatSocket', 'Height')
self.add_input('ArmFloatSocket', 'Angle')

self.add_output('ArmNodeSocketAction', 'Out')

0 comments on commit 0fe472d

Please sign in to comment.