function Node_Path_Shape(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
	name = "Path Shape";
	setDimension(96, 48);
	
	newInput(0, nodeValue_Vec2("Position", self, [ .5, .5 ]))
		.setUnitRef(function() /*=>*/ {return DEF_SURF}, VALUE_UNIT.reference);
	
	newInput(1, nodeValue_Vec2("Half Size", self, [ .5, .5 ]))
		.setUnitRef(function() /*=>*/ {return DEF_SURF}, VALUE_UNIT.reference);
	
	newInput(2, nodeValue_Rotation("Rotation", self, 0));
	
	shapeScroll = [ 
	    new scrollItem("Rectangle",     s_node_shape_type,  0), 
	    new scrollItem("Trapezoid",     s_node_shape_type,  2), 
	    new scrollItem("Parallelogram", s_node_shape_type,  3), 
	    -1,
	    new scrollItem("Ellipse",       s_node_shape_type,  5), 
	    new scrollItem("Arc",           s_node_shape_type,  6), 
	    new scrollItem("Squircle",      s_node_shape_type, 11), 
	    -1,
	    new scrollItem("Polygon",       s_node_shape_type, 12), 
	    new scrollItem("Star",          s_node_shape_type, 13), 
	    -1,
	    new scrollItem("Line",          s_node_shape_type, 16), 
	    new scrollItem("Curve",         s_shape_curve,      0), 
    ];
	newInput(3, nodeValue_Enum_Scroll("Shape", self, 0, shapeScroll));
	
	newInput(4, nodeValue_Float("Skew", self, .5))
	    .setDisplay(VALUE_DISPLAY.slider, { range: [ -1, 1, 0.01] });
	
	newInput(5, nodeValue_Rotation_Range("Angle Range", self, [ 0, 90 ]));
	
	newInput(6, nodeValue_Float("Factor", self, 4));
	
	newInput(7, nodeValue_Int("Sides", self, 4));
	
	newInput(8, nodeValue_Float("Inner Radius", self, .5));
	
	newOutput(0, nodeValue_Output("Path data", self, VALUE_TYPE.pathnode, self));
		
	input_display_list = [
		["Transform", false], 0, 2, 1, 
		["Shape",     false], 3, 4, 5, 6, 7, 8, 
	];
	
	points      = [];
	lengths		= [];
	lengthAccs	= [];
	lengthTotal	= 0;
	boundary    = new BoundingBox();
	cached_pos  = ds_map_create();
	
	loop  = true;
	shape = 0;
	posx  = 0; posy  = 0;
	scax  = 1; scay  = 1;
	rot   = 0;
	
	pa1   = 0;
	pa2x  = 0; pa2y  = 0;
	pa3   = 0;
	
	preview_surf = noone;
	
	static getLineCount		= function() /*=>*/ {return 1};
	static getSegmentCount	= function() /*=>*/ {return array_length(lengths)};
	static getBoundary		= function() /*=>*/ {return boundary};
	static getLength		= function() /*=>*/ {return lengthTotal};
	static getAccuLength	= function() /*=>*/ {return lengthAccs};
	
	static getPointRatio    = function(_rat, _ind = 0, out = undefined) { 
	    if(out == undefined) out = new __vec2();
	    _rat = frac(_rat);
	    
		switch(shapeScroll[shape].name) {
            case "Rectangle" : 
            case "Trapezoid" : 
            case "Parallelogram" : 
                if(_rat <= .25) {
                    var r = _rat * 4;
                    out.x = lerp(points[0][0], points[1][0], r);
                    out.y = lerp(points[0][1], points[1][1], r);
                } else if(_rat <= .50) {
                    var r = (_rat - .25) * 4;
                    out.x = lerp(points[1][0], points[2][0], r);
                    out.y = lerp(points[1][1], points[2][1], r);
                } else if(_rat <= .75) {
                    var r = (_rat - .50) * 4;
                    out.x = lerp(points[2][0], points[3][0], r);
                    out.y = lerp(points[2][1], points[3][1], r);
                } else {
                    var r = (_rat - .75) * 4;
                    out.x = lerp(points[3][0], points[0][0], r);
                    out.y = lerp(points[3][1], points[0][1], r);
                }
                break;
                
            case "Ellipse" : 
                var a = 360 * _rat;
                out.x = posx + lengthdir_x(scax, a);
                out.y = posy + lengthdir_y(scay, a);
                break;
                
            case "Arc" : 
                var a = lerp_float_angle(pa2x, pa2y, _rat);
                out.x = posx + lengthdir_x(scax, a);
                out.y = posy + lengthdir_y(scay, a);
                break;
                
            case "Squircle" : 
                var a = 360 * _rat;
                var r = 1 / power(power(dcos(a % 90), pa3) + power(dsin(a % 90), pa3), 1 / pa3);
                    
                out.x = posx + lengthdir_x(scax * r, a);
                out.y = posy + lengthdir_y(scay * r, a);
                break;
                
            case "Polygon" : 
            case "Star"    : 
            case "Line"    : 
            case "Curve"   : 
            	return getPointDistance(_rat * lengthTotal, _ind, out);
                
        }
        
        point_vec2_rotate(out, posx, posy, rot);
        return out;
	}
	    
	static getPointDistance = function(_dist, _ind = 0, out = undefined) {
	    if(out == undefined) out = new __vec2();
	    _dist = safe_mod(_dist, lengthTotal);
	    
	    var _d = _dist, l;
	    var np = array_length(points);
	    
	    for( var i = 0, n = array_length(lengths); i < n; i++ ) {
	        l = lengths[i];
	        if(_d > l) { _d -= l; continue; }
	        
            var p0 = points[(i + 0) % np];
            var p1 = points[(i + 1) % np];
            
            out.x = lerp(p0[0], p1[0], _d / l);
            out.y = lerp(p0[1], p1[1], _d / l);
            break;
	    }
	    
        // point_vec2_rotate(out, posx, posy, rot);
        return out;
	}
	
	static drawOverlay = function(hover, active, _x, _y, _s, _mx, _my, _snx, _sny) {
	    if(array_empty(points)) return;
	    
	    var ox = _x + points[0][0] * _s, x0 = ox;
	    var oy = _y + points[0][1] * _s, y0 = oy;
	    var nx, ny;
	    
	    draw_set_color(COLORS._main_accent);
	    for( var i = 1, n = array_length(points); i < n; i++ ) {
	        nx = _x + points[i][0] * _s;
	        ny = _y + points[i][1] * _s;
	        
            draw_line(ox, oy, nx, ny);
	        
	        ox = nx;
	        oy = ny;
	    }
	    
	    if(loop) draw_line(ox, oy, x0, y0);
	    
	    var _px = _x + posx * _s;
	    var _py = _y + posy * _s;
	    
	    var h = inputs[0].drawOverlay(hover, active,  _x,  _y, _s, _mx, _my, _snx, _sny); hover &= !h;
	    var h = inputs[2].drawOverlay(hover, active, _px, _py, _s, _mx, _my, _snx, _sny); hover &= !h;
	    var h = inputs[1].drawOverlay(hover, active, _px, _py, _s, _mx, _my, _snx, _sny); hover &= !h;
	    
	}
	
	static update = function(frame = CURRENT_FRAME) {
        var _pos  = inputs[0].getValue();
        var _rot  = inputs[2].getValue();
        var _sca  = inputs[1].getValue();
        var _pa1  = inputs[4].getValue();
        var _aran = inputs[5].getValue();
        var _pa3  = inputs[6].getValue();
        var _sid  = inputs[7].getValue();
        var _inn  = inputs[8].getValue();
        
	    shape = inputs[3].getValue();
	    posx  = _pos[0];
        posy  = _pos[1];
        rot   = _rot;
        scax  = _sca[0];
        scay  = _sca[1];
        
        pa1   = _pa1;
        pa2x  = _aran[0];
        pa2y  = _aran[1];
        pa3   = _pa3;
        
        var ox, oy, nx, ny, x0, y0;
        
        inputs[4].setVisible(false);
        inputs[5].setVisible(false);
        inputs[6].setVisible(false);
        inputs[7].setVisible(false);
        inputs[8].setVisible(false);
                
        switch(shapeScroll[shape].name) {
            case "Rectangle" : 
            	loop = true;
                points  = [ 
                    [ posx - scax, posy - scay ],
                    [ posx + scax, posy - scay ],
                    [ posx + scax, posy + scay ],
                    [ posx - scax, posy + scay ],
                ];
                break;
                
            case "Trapezoid" : 
            	loop = true;
                inputs[4].setVisible(true);
                
                points  = [ 
                    [ posx - scax * saturate(1 - _pa1), posy - scay ],
                    [ posx + scax * saturate(1 - _pa1), posy - scay ],
                    [ posx + scax * saturate(1 + _pa1), posy + scay ],
                    [ posx - scax * saturate(1 + _pa1), posy + scay ],
                ];
                break;
                
            case "Parallelogram" : 
            	loop = true;
                inputs[4].setVisible(true);
                
                points  = [ 
                    [ posx - scax * saturate(1 - _pa1), posy - scay ],
                    [ posx + scax * saturate(1 + _pa1), posy - scay ],
                    [ posx + scax * saturate(1 - _pa1), posy + scay ],
                    [ posx - scax * saturate(1 + _pa1), posy + scay ],
                ];
                break;
            
            case "Ellipse" : 
            	loop = true;
                var _st = 64;
                var _as = 360 / _st;
                points  = array_create(_st);
                
                for( var i = 0; i < _st; i++ ) {
                    nx = posx + lengthdir_x(scax, _as * i);
                    ny = posy + lengthdir_y(scay, _as * i);
                    points[i] = [ nx, ny ];
                }
                break;
                
            case "Arc" : 
            	loop = false;
                inputs[5].setVisible(true);
                
                var _st = 64;
                var _as = 1 / (_st - 1);
                points  = array_create(_st);
                
                for( var i = 0; i < _st; i++ ) {
                    var a = lerp_float_angle(_aran[0], _aran[1], i * _as);
                    nx = posx + lengthdir_x(scax, a);
                    ny = posy + lengthdir_y(scay, a);
                    points[i] = [ nx, ny ];
                }
                break;
                
            case "Squircle" :
            	loop = true;
                inputs[6].setVisible(true);
                
                var _st = 64;
                var _as = 360 / _st;
                points  = array_create(_st);
                
                for( var i = 0; i < _st; i++ ) {
                    var a = _as * i;
                    var r = 1 / power(power(dcos(a % 90), pa3) + power(dsin(a % 90), pa3), 1 / pa3);
                    
                    nx = posx + lengthdir_x(scax * r, a);
                    ny = posy + lengthdir_y(scay * r, a);
                    points[i] = [ nx, ny ];
                }
                break;
                
            case "Polygon" :
            	loop = true;
                inputs[7].setVisible(true);
                
                var _st = _sid;
                var _as = 360 / _st;
                points  = array_create(_st);
                
                for( var i = 0; i < _st; i++ ) {
                    nx = posx + lengthdir_x(scax, _as * i);
                    ny = posy + lengthdir_y(scay, _as * i);
                    points[i] = [ nx, ny ];
                }
                break;
                
            case "Star" :
            	loop = true;
                inputs[7].setVisible(true);
                inputs[8].setVisible(true);
                
                var _st = _sid;
                var _as = 360 / _st;
                points  = array_create(_st * 2);
                
                for( var i = 0; i < _st; i++ ) {
                    nx = posx + lengthdir_x(scax, _as * i);
                    ny = posy + lengthdir_y(scay, _as * i);
                    points[i * 2 + 0] = [ nx, ny ];
                    
                    nx = posx + lengthdir_x(scax * _inn, _as * i + _as / 2);
                    ny = posy + lengthdir_y(scay * _inn, _as * i + _as / 2);
                    points[i * 2 + 1] = [ nx, ny ];
                }
                break;
                
            case "Line":
            	loop = false;
            	points = [
            		[ posx - scax, posy ],
            		[ posx + scax, posy ],
        		];
            	break;
            	
            case "Curve":
            	loop = false;
            	inputs[6].setVisible(true);
            	
            	var _st = 64;
                var _as = 180 / (_st - 1);
                var _x0 = posx - scax;
                var _x1 = posx + scax;
                var _yy = posy;
                
            	points = array_create(_st);
            	for( var i = 0; i < _st; i++ ) {
            		points[i] = [ 
            			lerp(_x0, _x1, i / (_st - 1)),
            			_yy + dsin(i * _as) * pa3,
        			]
            	}
            	break;
        }

		array_map_ext(points, function(p) /*=>*/ {return point_rotate(p[0], p[1], posx, posy, rot, p)});
        var n   = array_length(points);
        lengths = array_create(n + loop);
        
        if(n) {
            for( var i = 0; i < n; i++ ) {
                nx = points[i][0];
                ny = points[i][1];
                
                if(i) lengths[i - 1] = point_distance(ox, oy, nx, ny);
                else { x0 = nx; y0 = ny; }
                
                ox = nx;
                oy = ny;
            }
            
            if(loop) lengths[n - 1] = point_distance(ox, oy, x0, y0);
        }
        
        var _len    = array_length(lengths);
    	lengthTotal = 0;
    	lengthAccs  = array_create(_len);
    	
    	for( var i = 0; i < _len; i++ ) {
    	    lengthTotal  += lengths[i];
    	    lengthAccs[i] = lengthTotal;
    	}
    	
    	boundary = new BoundingBox(posx - scax, posy - scay, posx + scax, posy + scay);
    	
    	preview_surf = surface_verify(preview_surf, 128, 128);
    	surface_set_target(preview_surf);
    		DRAW_CLEAR
    		
    		var ox, x0;
		    var oy, y0;
		    var nx, ny;
		    var xx = posx - scax;
		    var yy = posy - scay;
		    var ww = scax * 2;
		    var hh = scay * 2;
		    draw_set_color(COLORS._main_accent);
		    
		    if(array_length(points)) {
			    for( var i = 0, n = array_length(points); i < n; i++ ) {
			        nx = 4 + (points[i][0] - xx) / ww * 120;
			        ny = 4 + (points[i][1] - yy) / hh * 120;
			        
		            if(i) draw_line_width(ox, oy, nx, ny, 8);
		            else  { x0 = nx; y0 = ny; }
			        
			        ox = nx;
			        oy = ny;
			    }
			    
			    if(loop) draw_line_width(ox, oy, x0, y0, 8);
		    }
    	surface_reset_target();
	}
	
	static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) {
		var bbox = drawGetBbox(xx, yy, _s);
		draw_surface_bbox(preview_surf, bbox);
	}
	
	static getPreviewBoundingBox = function() { return BBOX().fromBoundingBox(boundary); }
}