From 95c2b480bd750ee06ae490714b0e21a7aeb72573 Mon Sep 17 00:00:00 2001 From: Tanasart Date: Sat, 8 Jun 2024 14:38:41 +0700 Subject: [PATCH] svg --- scripts/__polygon/__polygon.gml | 42 ++- scripts/node_registry/node_registry.gml | 2 +- scripts/svg_objects/svg_objects.gml | 349 ++++++++++++++++++++++-- scripts/svg_reader/svg_reader.gml | 18 +- 4 files changed, 367 insertions(+), 44 deletions(-) diff --git a/scripts/__polygon/__polygon.gml b/scripts/__polygon/__polygon.gml index 5d7c8b2d8..c769d5c0c 100644 --- a/scripts/__polygon/__polygon.gml +++ b/scripts/__polygon/__polygon.gml @@ -1,22 +1,31 @@ function polygon_simplify(points, tolerance = 4) { #region - var remSt = ds_stack_create(); - var len = array_length(points); + // Delete duplicated points + for( var i = array_length(points) - 1; i >= 1; i-- ) { + if(points[i].equal(points[i - 1])) + array_delete(points, i, 1); + } + if(points[0].equal(points[array_length(points) - 1])) + array_pop(points); + + var remSt = ds_stack_create(); + var len = array_length(points); + for( var i = 0; i < len; i++ ) { - var _px0 = points[i].x; - var _py0 = points[i].y; - var _px1 = points[(i + 1) % len].x; - var _py1 = points[(i + 1) % len].y; - var _px2 = points[(i + 2) % len].x; - var _py2 = points[(i + 2) % len].y; + var _px0 = points[(i - 1 + len) % len].x; + var _py0 = points[(i - 1 + len) % len].y; + var _px1 = points[i].x; + var _py1 = points[i].y; + var _px2 = points[(i + 1 + len) % len].x; + var _py2 = points[(i + 1 + len) % len].y; var dir0 = point_direction(_px0, _py0, _px1, _py1); var dir1 = point_direction(_px1, _py1, _px2, _py2); - - if((_px0 == _px1 && _py0 == _py1) || abs(dir0 - dir1) <= tolerance) - ds_stack_push(remSt, (i + 1) % len); - } + if((_px0 == _px1 && _py0 == _py1) || abs(dir0 - dir1) <= tolerance) + ds_stack_push(remSt, i); + } + while(!ds_stack_empty(remSt)) { var ind = ds_stack_pop(remSt); array_delete(points, ind, 1); @@ -102,7 +111,7 @@ function polygon_triangulate(points, tolerance = 4) { #region // ear clipping var triangles = []; var repeated = 0; - //print($"Ear cutting : {array_length(points)} verticies"); + // print($"Ear cutting : {points}"); while(array_length(pointInd) > 3) { if(array_length(convexes) == 0) return [ triangles, points, checkSide ]; @@ -173,6 +182,13 @@ function polygon_triangulate(points, tolerance = 4) { #region // ear clipping array_push(convexes, c0); if(repeated++ > len) { + // print($"point: {points}"); + // print($"index: {pointInd}"); + // print($"convx: {convexes}"); + + // for (var i = 0, n = array_length(pointInd); i < n; i++) + // print(points[pointInd[i]]); + noti_warning("Mesh error"); break; } diff --git a/scripts/node_registry/node_registry.gml b/scripts/node_registry/node_registry.gml index a79809562..921af717c 100644 --- a/scripts/node_registry/node_registry.gml +++ b/scripts/node_registry/node_registry.gml @@ -497,7 +497,7 @@ function __initNodes() { addNodeObject(input, "Image Array", s_node_image_sequence, "Node_Image_Sequence", [0, Node_create_Image_Sequence],, "Load multiple images from your computer as array."); addNodeObject(input, "Animation", s_node_image_animation, "Node_Image_Animated", [0, Node_create_Image_Animated],, "Load multiple images from your computer as animation."); addNodeObject(input, "Array to Anim", s_node_image_sequence_to_anim, "Node_Sequence_Anim", [1, Node_Sequence_Anim],, "Convert array of images into animation."); - /**/addNodeObject(input, "SVG", s_node_image_sequence_to_anim, "Node_SVG", [1, Node_SVG],, "Load SVG file."); + addNodeObject(input, "SVG", s_node_svg, "Node_SVG", [1, Node_SVG],, "Load SVG file."); if(!DEMO) addNodeObject(input, "Export", s_node_export, "Node_Export", [0, Node_create_Export],, "Export image, image array to file, image sequence, animation."); ds_list_add(input, "Files"); diff --git a/scripts/svg_objects/svg_objects.gml b/scripts/svg_objects/svg_objects.gml index 6f7a55bb0..ca1956b00 100644 --- a/scripts/svg_objects/svg_objects.gml +++ b/scripts/svg_objects/svg_objects.gml @@ -1,19 +1,46 @@ -function SVG() constructor { +function SVGElement(svgObj = noone) constructor { + parent = svgObj; + + x = 0; + y = 0; + width = 1; height = 1; + + fill = undefined; + fill_opacity = undefined; + + stroke = undefined; + stroke_width = undefined; + + static setAttr = function(attr) { + fill = struct_try_get(attr, "fill", undefined); + stroke = struct_try_get(attr, "stroke", undefined); + stroke_width = struct_try_get(attr, "stroke-width", undefined); + + shapeAttr(attr); + } + + static shapeAttr = function(attr) {} + + static draw = function(scale = 1) {} + + static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) {} +} + +function SVG(svgObj = noone) : SVGElement(svgObj) constructor { bbox = [ 1, 1, 1, 1 ]; fill = c_black; fill_opacity = 1; - stroke = undefined; - stroke_width = undefined; - contents = []; static mapX = function(px) { return lerp_invert(px, bbox[0], bbox[0] + bbox[2]) * width; } static mapY = function(py) { return lerp_invert(py, bbox[1], bbox[1] + bbox[3]) * height; } + static setAttr = function(attr) {} + static getSurface = function(scale = 1) { return surface_create(width * scale, height * scale); } static draw = function(scale = 1) { @@ -33,14 +60,7 @@ function SVG() constructor { } } -function SVG_path(svgObj) constructor { - parent = svgObj; - - fill = undefined; - fill_opacity = undefined; - - stroke = undefined; - stroke_width = undefined; +function SVG_path(svgObj = noone) : SVGElement(svgObj) constructor { segments = []; shapes = []; @@ -83,7 +103,9 @@ function SVG_path(svgObj) constructor { } - static setDef = function(def) { + static shapeAttr = function(attr) { + + var def = struct_try_get(attr, "d", ""); var _mode = ""; var _len = string_length(def); var _ind = 1; @@ -405,14 +427,13 @@ function SVG_path(svgObj) constructor { setTris(); // print(segments); + + return self; } static draw = function(scale = 1) { - if(!is_undefined(fill)) - draw_set_color(fill); - - if(!is_undefined(fill_opacity)) - draw_set_alpha(fill_opacity); + if(!is_undefined(fill)) draw_set_color(fill); + if(!is_undefined(fill_opacity)) draw_set_alpha(fill_opacity); var _temp = [ parent.getSurface(scale), @@ -520,4 +541,294 @@ function SVG_path(svgObj) constructor { } } } -} \ No newline at end of file +} + +function SVG_rect(svgObj = noone) : SVGElement(svgObj) constructor { + + static shapeAttr = function(attr) { + x = struct_try_get(attr, "x", 0); + y = struct_try_get(attr, "y", 0); + + width = struct_try_get(attr, "width", 0); + height = struct_try_get(attr, "height", 0); + } + + static draw = function(scale = 1) { + if(!is_undefined(fill)) draw_set_color(fill); + if(!is_undefined(fill_opacity)) draw_set_alpha(fill_opacity); + + var _x = x * scale; + var _y = y * scale; + var _w = width * scale; + var _h = height * scale; + + draw_rectangle(_x, _y, _x + _w, _y + _h, false); + + if(is_undefined(stroke) || is_undefined(stroke_width)) + return; + + if(!is_undefined(stroke)) draw_set_color(stroke); + + if(is_undefined(stroke_width) || stroke_width == 1) + draw_rectangle(_x, _y, _x + _w, _y + _h, true); + else + draw_rectangle_border(_x, _y, _x + _w, _y + _h, stroke_width); + } + + static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) { + + var _ox = _x + x * _s; + var _oy = _y + y * _s; + var _ow = width * _s; + var _oh = height * _s; + + draw_set_color(COLORS._main_accent); + draw_rectangle(_ox, _oy, _ox + _ow, _oy + _oh, true); + } +} + +function SVG_circle(svgObj = noone) : SVGElement(svgObj) constructor { + cx = 0; + cy = 0; + r = 0; + + static shapeAttr = function(attr) { + cx = struct_try_get(attr, "cx", 0); + cy = struct_try_get(attr, "cy", 0); + + r = struct_try_get(attr, "r", 0); + } + + static draw = function(scale = 1) { + if(!is_undefined(fill)) draw_set_color(fill); + if(!is_undefined(fill_opacity)) draw_set_alpha(fill_opacity); + + var _cx = cx * scale; + var _cy = cy * scale; + var _r = r * scale; + + draw_circle(_cx, _cy, _r, false); + + if(is_undefined(stroke) || is_undefined(stroke_width)) + return; + + if(!is_undefined(stroke)) draw_set_color(stroke); + + if(is_undefined(stroke_width) || stroke_width == 1) + draw_circle(_cx, _cy, _r, true); + else + draw_circle_border(_cx, _cy, _r, stroke_width); + } + + static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) { + + var _ox = _x + cx * _s; + var _oy = _y + cy * _s; + var _or = r * _s; + + draw_set_color(COLORS._main_accent); + draw_circle(_ox, _oy, _or, true); + } +} + +function SVG_ellipse(svgObj = noone) : SVGElement(svgObj) constructor { + cx = 0; + cy = 0; + rx = 0; + ry = 0; + + static shapeAttr = function(attr) { + cx = struct_try_get(attr, "cx", 0); + cy = struct_try_get(attr, "cy", 0); + + rx = struct_try_get(attr, "rx", 0); + ry = struct_try_get(attr, "ry", 0); + } + + static draw = function(scale = 1) { + if(!is_undefined(fill)) draw_set_color(fill); + if(!is_undefined(fill_opacity)) draw_set_alpha(fill_opacity); + + var _cx = cx * scale; + var _cy = cy * scale; + var _rx = rx * scale; + var _ry = ry * scale; + + draw_ellipse(_cx - _rx, _cy - _ry, _cx + _rx, _cy + _ry, false); + + if(is_undefined(stroke) || is_undefined(stroke_width)) + return; + + if(!is_undefined(stroke)) draw_set_color(stroke); + + if(is_undefined(stroke_width) || stroke_width == 1) + draw_ellipse(_cx - _rx, _cy - _ry, _cx + _rx, _cy + _ry, true); + else + draw_ellipse_border(_cx - _rx, _cy - _ry, _cx + _rx, _cy + _ry, stroke_width); + } + + static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) { + + var _ox = _x + cx * _s; + var _oy = _y + cy * _s; + var _rx = rx * _s; + var _ry = ry * _s; + + draw_set_color(COLORS._main_accent); + draw_ellipse(_ox - _rx, _oy - _ry, _ox + _rx, _oy + _ry, true); + } +} + +function SVG_line(svgObj = noone) : SVGElement(svgObj) constructor { + x0 = 0; + y0 = 0; + x1 = 0; + y1 = 0; + + static shapeAttr = function(attr) { + x0 = struct_try_get(attr, "x0", 0); + y0 = struct_try_get(attr, "y0", 0); + + x1 = struct_try_get(attr, "x1", 0); + y1 = struct_try_get(attr, "y1", 0); + } + + static draw = function(scale = 1) { + if(!is_undefined(stroke)) draw_set_color(stroke); + + if(is_undefined(stroke) && is_undefined(stroke_width)) + return; + + var _x0 = x0 * scale; + var _y0 = y0 * scale; + var _x1 = x1 * scale; + var _y1 = y1 * scale; + + if(is_undefined(stroke_width) || stroke_width == 1) + draw_line(_x0, _y0, _x1, _y1); + else + draw_line_width(_x0, _y0, _x1, _y1, stroke_width); + } + + static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) { + + var _x0 = _x + x0 * _s; + var _y0 = _y + y0 * _s; + var _x1 = _x + x1 * _s; + var _y1 = _y + y1 * _s; + + draw_set_color(COLORS._main_accent); + draw_line(_x0, _y0, _x1, _y1); + } +} + +function SVG_polyline(svgObj = noone) : SVGElement(svgObj) constructor { + points = []; + + static shapeAttr = function(attr) { + points = struct_try_get(attr, "points", []); + } + + static draw = function(scale = 1) { + if(!is_undefined(stroke)) draw_set_color(stroke); + + if(is_undefined(stroke) && is_undefined(stroke_width)) + return; + + var _ox, _oy, _nx, _ny; + + for (var i = 0, n = floor(array_length(points) / 2); i < n; i++) { + _nx = points[i * 2 + 0] * scale; + _ny = points[i * 2 + 1] * scale; + + if(i) { + if(is_undefined(stroke_width) || stroke_width == 1) + draw_line(_ox, _oy, _nx, _ny); + else + draw_line_width(_ox, _oy, _nx, _ny, stroke_width); + } + + _ox = _nx; + _oy = _ny; + } + + } + + static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) { + + draw_set_color(COLORS._main_accent); + + var _ox, _oy, _nx, _ny; + + for (var i = 0, n = floor(array_length(points) / 2); i < n; i++) { + _nx = _x + points[i * 2 + 0] * _s; + _ny = _y + points[i * 2 + 1] * _s; + + if(i) { + if(is_undefined(stroke_width) || stroke_width == 1) + draw_line(_ox, _oy, _nx, _ny); + else + draw_line_width(_ox, _oy, _nx, _ny, stroke_width); + } + + _ox = _nx; + _oy = _ny; + } + } +} + +function SVG_polygon(svgObj = noone) : SVGElement(svgObj) constructor { + points = []; + + static shapeAttr = function(attr) { + points = struct_try_get(attr, "points", []); + } + + static draw = function(scale = 1) { + if(!is_undefined(stroke)) draw_set_color(stroke); + + if(is_undefined(stroke) && is_undefined(stroke_width)) + return; + + var _ox, _oy, _nx, _ny; + + for (var i = 0, n = floor(array_length(points) / 2); i < n; i++) { + _nx = points[i * 2 + 0] * scale; + _ny = points[i * 2 + 1] * scale; + + if(i) { + if(is_undefined(stroke_width) || stroke_width == 1) + draw_line(_ox, _oy, _nx, _ny); + else + draw_line_width(_ox, _oy, _nx, _ny, stroke_width); + } + + _ox = _nx; + _oy = _ny; + } + + } + + static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) { + + draw_set_color(COLORS._main_accent); + + var _ox, _oy, _nx, _ny; + + for (var i = 0, n = floor(array_length(points) / 2); i < n; i++) { + _nx = _x + points[i * 2 + 0] * _s; + _ny = _y + points[i * 2 + 1] * _s; + + if(i) { + if(is_undefined(stroke_width) || stroke_width == 1) + draw_line(_ox, _oy, _nx, _ny); + else + draw_line_width(_ox, _oy, _nx, _ny, stroke_width); + } + + _ox = _nx; + _oy = _ny; + } + } +} + diff --git a/scripts/svg_reader/svg_reader.gml b/scripts/svg_reader/svg_reader.gml index 00cdc7133..552e953fc 100644 --- a/scripts/svg_reader/svg_reader.gml +++ b/scripts/svg_reader/svg_reader.gml @@ -38,20 +38,16 @@ function svg_parse(xmlStr) { var _ch = svg_object.children[i]; switch(_ch.type) { - case "path" : svg.contents[_ind++] = svg_parse_path(_ch, svg); break; + case "path" : svg.contents[_ind++] = new SVG_path(svg).setAttr(_ch.attributes); break; + case "rect" : svg.contents[_ind++] = new SVG_rect(svg).setAttr(_ch.attributes); break; + case "circle" : svg.contents[_ind++] = new SVG_circle(svg).setAttr(_ch.attributes); break; + case "ellipse" : svg.contents[_ind++] = new SVG_ellipse(svg).setAttr(_ch.attributes); break; + case "line" : svg.contents[_ind++] = new SVG_line(svg).setAttr(_ch.attributes); break; + case "polyline" : svg.contents[_ind++] = new SVG_polyline(svg).setAttr(_ch.attributes); break; + case "polygon" : svg.contents[_ind++] = new SVG_polygon(svg).setAttr(_ch.attributes); break; } } } return svg; -} - -function svg_parse_path(pathStr, svgObj) { - var _path = new SVG_path(svgObj); - var attr = pathStr.attributes; - - if(struct_has(attr, "d")) - _path.setDef(attr.d) - - return _path; } \ No newline at end of file