LINE_CORNER_NONE = 0 LINE_CORNER_MITER = 1 LINE_CORNER_ROUND = 2 do local plotting = false local plots, quads = {}, {} local plot_count, quad_count = 0, 0 local thickness = 0 local min_angle = 0 local corner_type = 0 function draw.StartLine(_thickness, _min_angle, _corner_type) assert(not plotting, "how come your mom lets you have 2 lines") plotting = true plot_count, quad_count = 0, 0 thickness = (tonumber(_thickness) or 10) / 2 min_angle = math.pi - math.rad(tonumber(_min_angle) or 20) corner_type = tonumber(_corner_type) or 0 end function draw.AddLinePoint(x, y) assert(plotting, "better start a line boy") if not y then y = x.y x = x.x end plot_count = plot_count + 1 plots[plot_count] = Vector(x, y) end function draw.EndLine() assert(plotting, "cut me point 2 g fam") plotting = false assert(plot_count > 1, "A minimum of 2 points are required to draw a line.") for i = 1, plot_count - 1 do local a, b = plots[i], plots[i + 1] local ab = b - a ab:Normalize() local ab_right = Vector(-ab.y, ab.x) quad_count = quad_count + 1 quads[quad_count] = { a + ab_right * thickness, a - ab_right * thickness, b - ab_right * thickness, b + ab_right * thickness, } end if plot_count >= 3 then for i = 2, plot_count - 1 do local a, b, c = unpack(plots, i - 1, i + 1) local left_quad = quads[i - 1] local right_quad = quads[i] local ab, bc do ab = b - a bc = c - b ab:Normalize() bc:Normalize() end local angle = math.acos(ab:Dot(bc)) local bc_right = Vector(-bc.y, bc.x) local is_left = false if ab:Dot(bc_right) > 0 then is_left = true end if corner_type == LINE_CORNER_MITER then local height = thickness / math.cos(angle / 2) local joint_direction = bc - ab joint_direction:Normalize() if is_left then joint_direction = -joint_direction end if angle <= min_angle then left_quad[3] = b - joint_direction * height left_quad[4] = b + joint_direction * height right_quad[1] = left_quad[4] right_quad[2] = left_quad[3] end elseif corner_type == LINE_CORNER_ROUND then local bd = (is_left and left_quad[4] or right_quad[2]) - b bd:Normalize() local step = 1 / thickness local theta = (0.5 * math.pi - math.atan2(bd.y, bd.x)) % math.tau mesh.Begin(MATERIAL_POLYGON, 1) for a = theta, theta + angle + step, step do local sin, cos = math.sin(a), math.cos(a) local pos = b + Vector(sin, cos) * thickness mesh.Position(pos) mesh.AdvanceVertex() end mesh.Position(b) mesh.AdvanceVertex() mesh.End() end end end mesh.Begin(MATERIAL_QUADS, quad_count) for i, quad in ipairs(quads) do mesh.Quad(unpack(quad)) end mesh.End() end end --[[ EXAMPLE ]]-- local plots = {} for i = 1, 20 do if #plots == 0 then table.insert(plots, Vector(ScrW() / 2, ScrH() / 2)) else local rand_direction = Vector(math.Rand(-1, 1), math.Rand(-1, 1)) rand_direction:Normalize() local last_plot = plots[#plots] table.insert(plots, last_plot + rand_direction * math.random(100, 400)) end end hook.Add("PostRender", "", function() cam.Start2D() surface.SetDrawColor(255, 255 ,255) render.SetMaterial(Material("vgui/white")) draw.StartLine(50, 15, math.floor(CurTime() % 3)) for i, plot in ipairs(plots) do draw.AddLinePoint(plot) end draw.EndLine() cam.End2D() end)