Help with GPU Shader Drawing?

So I am trying to make a custom brush size similar to Sculpt mode:

This is for visualizing the Brush Proximity for an object with Dynamic Paint modifier.

ZQLbgkLMj7

I have this example which draws a rectangle on the viewport. I’d like this rectangle to be a circle & have its location where the cursor is when called.

import bpy
import gpu
from gpu_extras.batch import batch_for_shader

vertices = (
    (100, 100), (300, 100),
    (100, 200), (300, 200))

indices = (
    (0, 1, 2), (2, 1, 3))

shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)


def draw():
    shader.bind()
    shader.uniform_float("color", (0, 0.5, 0.5, 1.0))
    batch.draw(shader)


bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

Okay, I found a better example. This script lets you draw a circle & change its radius by mouse scrolling. But it only works in Blender 2.79 & I’d like to get it working on 2.8…

import bpy
import bgl
import blf

import math


def draw_circle_2d(color, cx, cy, r, num_segments):
    theta = 2 * 3.1415926 / num_segments
    c = math.cos(theta) #precalculate the sine and cosine
    s = math.sin(theta)
    x = r # we start at angle = 0 
    y = 0
    bgl.glColor4f(*color)
    bgl.glBegin(bgl.GL_LINE_LOOP)
    for i in range (num_segments):
        bgl.glVertex2f(x + cx, y + cy) # output vertex 
        # apply the rotation matrix
        t = x
        x = c * x - s * y
        y = s * t + c * y
    bgl.glEnd()

def draw_callback_px(self, context):

    bgl.glEnable(bgl.GL_BLEND)

    # ...api_current/bpy.types.Area.html?highlight=bpy.types.area
    header_height = context.area.regions[0].height # 26px
    npanel_width = context.area.regions[1].width
    transorm_panel_width = context.area.regions[3].width

    width = context.area.width - npanel_width - transorm_panel_width
    height = context.area.height + header_height

    # draw text
    bgl.glLineWidth(4)
    draw_circle_2d((1.0, 1.0, 1.0, 0.8), self._mouse_pos_x, self._mouse_pos_y, self._radius, 360)

    # restore opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)
    bgl.glColor4f(0.0, 0.0, 0.0, 1.0)



class ModalWheelTest(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_wheel_test"
    bl_label = "Test mouse wheel"

    _timer = None
    _radius = 50

    def set_wheel_zoom_state(self, state):
        """
        Disable or enable zooming by mouse wheel
        """
        for key_map_item in bpy.context.window_manager.keyconfigs.default.keymaps["3D View"].keymap_items:
            if key_map_item.name == 'Zoom View' and key_map_item.type in {'WHEELINMOUSE', 'WHEELOUTMOUSE'}:
                key_map_item.active = state

    def modal(self, context, event):
        context.area.tag_redraw()

        if event.type in {'RIGHTMOUSE', 'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}

        if event.type == 'MOUSEMOVE':
            for area in bpy.context.screen.areas:
                if area.type == 'VIEW_3D':
                    self._mouse_pos_x = event.mouse_region_x
                    self._mouse_pos_y = event.mouse_region_y

                    override = bpy.context.copy()
                    override['area'] = area
                    override['region'] = area.regions[4]

                    bpy.ops.view3d.select_circle(
                        override,
                        x=event.mouse_region_x,
                        y=event.mouse_region_y,
                        radius=self._radius,
                        gesture_mode = 3
                    )
                    break

        if event.type in {'WHEELUPMOUSE'}:
            self._radius = min(200, self._radius+5)
            print("radius: {}".format(self._radius))

        if event.type in {'WHEELDOWNMOUSE'}:
            self._radius = max(5, self._radius-5) 
            print("radius: {}".format(self._radius))

        return {'RUNNING_MODAL'}

    def execute(self, context):
        if context.area.type != 'VIEW_3D':
            print("Must use in a 3d region")
            return {'CANCELLED'}

        self.set_wheel_zoom_state(False)

        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        self.set_wheel_zoom_state(True)
        bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
        wm = context.window_manager

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            # the arguments we pass the the callback
            args = (self, context)
            # Add the region OpenGL drawing callback
            # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
            self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
            context.window_manager.modal_handler_add(self)

            self._mouse_pos = []

            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "View3D not found, cannot run operator")
            return {'CANCELLED'}

def register():
    bpy.utils.register_class(ModalWheelTest)


def unregister():
    bpy.utils.unregister_class(ModalWheelTest)


if __name__ == "__main__":
    register()