diff --git a/examples/3D.lua b/examples/3D.lua index fefb8ae..c3cb3a9 100644 --- a/examples/3D.lua +++ b/examples/3D.lua @@ -8,22 +8,37 @@ end function draw() background(0); - orbitControl(); fill(0, 255, 0); text('Drag the mouse around', 40, 40); + push(); noFill(); stroke(0, 255, 0); - sphere(100); + translate(-90, 0, 0); + sphere(30); + pop(); push(); - rotateX(90); noFill(); - stroke(0, 255, 0) - plane(400, 400) - pop(); + stroke(0, 255, 0); + translate(0, 0, 0); + cylinder(30, 60); + pop(0); + push(); + noFill(); + stroke(0, 255, 0); + translate(90, 0, 0); + box(50, 60); + pop(0); + + push(); + translate(0, 32, 0); + rotateX(90); + fill(51) + plane(400, 400); + pop(); end diff --git a/src/bindings/shapes3D.c b/src/bindings/shapes3D.c index 01a0162..3e35381 100644 --- a/src/bindings/shapes3D.c +++ b/src/bindings/shapes3D.c @@ -109,54 +109,41 @@ int sphere(lua_State *L) return 0; } -// int cylinder(lua_State *L) -// { -// lua_Number radius = lu5_assert_number(L, 1, "cylinder"); -// lua_Number height = lu5_assert_number(L, 2, "cylinder"); - -// lua_Number detail_x = 24; -// if (lua_isboolean(L, 3)) { -// detail_x = lua_toboolean(L, 3); -// } - -// lua_Number detail_y = 24; -// if (lua_isboolean(L, 4)) { -// detail_y = lua_toboolean(L, 4); -// } - -// bool top_cap = false; -// if (lua_isboolean(L, 5)) { -// top_cap = lua_toboolean(L, 5); -// } - -// bool bottom_cap = false; -// if (lua_isboolean(L, 6)) { -// bottom_cap = lua_toboolean(L, 6); -// } - -// if (lu5_has_fill()) -// { -// lu5_apply_color(lu5_style(&lu5)->fill); -// lu5_render_cylinder_faces(radius, height, -// top_cap, -// bottom_cap); -// } - -// if (lu5_has_stroke()) -// { -// // Draw sphere edges -// lu5_apply_color(lu5_style(&lu5)->stroke); -// glLineWidth(lu5_style(&lu5)->strokeWeight); - -// // Draw stroke with a larger radius -// lu5_render_cylinder_edges( -// radius + 1.0f, -// height, -// detail_x, detail_y -// top_cap, -// bottom_cap -// ); -// } - -// return 0; -// } \ No newline at end of file +int cylinder(lua_State *L) +{ + lua_Number radius = lua_isnumber(L, 1) ? lua_tonumber(L, 1) : 50.0; + lua_Number height = lua_isnumber(L, 2) ? lua_tonumber(L, 2) : radius; + + lua_Number detail_x = lua_isnumber(L, 3) ? lua_tonumber(L, 3) : 24; + lua_Number detail_y = lua_isnumber(L, 4) ? lua_tonumber(L, 4) : 1; + + bool bottom_cap = lua_isboolean(L, 5) ? lua_toboolean(L, 5) : true; + bool top_cap = lua_isboolean(L, 6) ? lua_toboolean(L, 6) : true; + + if (lu5_has_fill()) + { + lu5_apply_color(lu5_style(&lu5)->fill); + lu5_render_cylinder_faces(radius, height, + detail_x, detail_y, + top_cap, + bottom_cap); + } + + if (lu5_has_stroke()) + { + // Draw sphere edges + lu5_apply_color(lu5_style(&lu5)->stroke); + glLineWidth(lu5_style(&lu5)->strokeWeight); + + // Draw stroke with a larger radius + lu5_render_cylinder_edges( + radius + 0.5f, + height + 0.5f, + detail_x, detail_y, + top_cap, + bottom_cap + ); + } + + return 0; +} \ No newline at end of file diff --git a/src/bindings/shapes3D.h b/src/bindings/shapes3D.h index 9145d58..ad30b2d 100644 --- a/src/bindings/shapes3D.h +++ b/src/bindings/shapes3D.h @@ -70,5 +70,29 @@ int box(lua_State *L); */ int sphere(lua_State *L); +/** + * Draw a 3D Cylinder. + * + * @param radius radius of the cylinder. Defaults to 50. + * @param height height of the cylinder. Defaults to the value of radius. + * @param detailX number of horizontal edges. Defaults to 24. + * @param detailY number of subdivisions along the y-axis. Defaults to 1. + * @param bottomCap whether to draw the cylinder's bottom. Defaults to true. + * @param topCap whether to draw the cylinder's top. Defaults to true. + * + * @example + * function setup() + * createWindow(800, 800, GL3D); + * end + * + * function draw() + * background(51); + * + * cylinder(30, 50); + * end + * @example + */ +int cylinder(lua_State *L); + #endif /* _LU5_SHAPES3D_H_ */ \ No newline at end of file diff --git a/src/geometry/3D/lu5_cylinder.c b/src/geometry/3D/lu5_cylinder.c index 573c490..be7777f 100644 --- a/src/geometry/3D/lu5_cylinder.c +++ b/src/geometry/3D/lu5_cylinder.c @@ -64,13 +64,14 @@ void lu5_render_cylinder_faces( } void lu5_render_cylinder_edges( - lua_Number radius, lua_Number height, - lua_Integer detail_x, lua_Integer detail_y, - bool bottom_cap, - bool top_cap) + lua_Number radius, lua_Number height, + lua_Integer detail_x, lua_Integer detail_y, + bool bottom_cap, + bool top_cap) { lua_Number half_height = height / 2.0; + // Draw vertical lines for (lua_Integer j = 0; j <= detail_x; j++) { lua_Number theta = 2 * M_PI * j / (lua_Number)detail_x; lua_Number cos_theta = cosf(theta); @@ -87,6 +88,7 @@ void lu5_render_cylinder_edges( glEnd(); } + // Draw horizontal lines for (lua_Integer i = 0; i <= detail_y; i++) { lua_Number y = -half_height + (height * i / (lua_Number)detail_y); @@ -104,34 +106,80 @@ void lu5_render_cylinder_edges( glEnd(); } + // Draw diagonal edges + for (lua_Integer j = 0; j < detail_x; j++) { + lua_Number theta = 2 * M_PI * j / (lua_Number)detail_x; + lua_Number next_theta = 2 * M_PI * (j + 1) / (lua_Number)detail_x; + lua_Number cos_theta = cosf(theta); + lua_Number sin_theta = sinf(theta); + lua_Number cos_next_theta = cosf(next_theta); + lua_Number sin_next_theta = sinf(next_theta); + + lua_Number x1 = cos_theta * radius; + lua_Number z1 = sin_theta * radius; + lua_Number x2 = cos_next_theta * radius; + lua_Number z2 = sin_next_theta * radius; + + glBegin(GL_LINES); + for (lua_Integer i = 0; i < detail_y; i++) { + lua_Number y = -half_height + (height * i / (lua_Number)detail_y); + lua_Number next_y = -half_height + (height * (i + 1) / (lua_Number)detail_y); + lu5_glVertex3(x2, y, z2); + lu5_glVertex3(x1, next_y, z1); + } + glEnd(); + } + + // Draw bottom cap if (bottom_cap) { + lua_Number y = -half_height; + glBegin(GL_LINE_LOOP); for (lua_Integer j = 0; j < detail_x; j++) { lua_Number theta = 2 * M_PI * j / (lua_Number)detail_x; + lua_Number next_theta = 2 * M_PI * ((j + 1) % detail_x) / (lua_Number)detail_x; lua_Number cos_theta = cosf(theta); lua_Number sin_theta = sinf(theta); - - lua_Number x = cos_theta * radius; - lua_Number z = sin_theta * radius; - - lu5_glVertex3(x, -half_height, z); + lua_Number cos_next_theta = cosf(next_theta); + lua_Number sin_next_theta = sinf(next_theta); + + lua_Number x1 = cos_theta * radius; + lua_Number z1 = sin_theta * radius; + lua_Number x2 = cos_next_theta * radius; + lua_Number z2 = sin_next_theta * radius; + + // Draw the triangle fan lines + lu5_glVertex3(0, y, 0); // center + lu5_glVertex3(x1, y, z1); // current vertex on the circle + lu5_glVertex3(x2, y, z2); // next vertex on the circle } glEnd(); } + // Draw top cap if (top_cap) { + lua_Number y = half_height; + glBegin(GL_LINE_LOOP); for (lua_Integer j = 0; j < detail_x; j++) { lua_Number theta = 2 * M_PI * j / (lua_Number)detail_x; + lua_Number next_theta = 2 * M_PI * ((j + 1) % detail_x) / (lua_Number)detail_x; lua_Number cos_theta = cosf(theta); lua_Number sin_theta = sinf(theta); - - lua_Number x = cos_theta * radius; - lua_Number z = sin_theta * radius; - - lu5_glVertex3(x, half_height, z); + lua_Number cos_next_theta = cosf(next_theta); + lua_Number sin_next_theta = sinf(next_theta); + + lua_Number x1 = cos_theta * radius; + lua_Number z1 = sin_theta * radius; + lua_Number x2 = cos_next_theta * radius; + lua_Number z2 = sin_next_theta * radius; + + // Draw the triangle fan lines + lu5_glVertex3(0, y, 0); // center + lu5_glVertex3(x1, y, z1); // current vertex on the circle + lu5_glVertex3(x2, y, z2); // next vertex on the circle } glEnd(); } diff --git a/src/lu5_bindings.c b/src/lu5_bindings.c index 994f18b..e74e3c4 100644 --- a/src/lu5_bindings.c +++ b/src/lu5_bindings.c @@ -176,7 +176,7 @@ void lu5_register_symbols(lua_State *L) // Shapes 3D LUA_ADD_FUNCTION(L, box); - // LUA_ADD_FUNCTION(L, cylinder); + LUA_ADD_FUNCTION(L, cylinder); LUA_ADD_FUNCTION(L, sphere); LUA_ADD_FUNCTION(L, plane);