Featured post

new redirect for blender.org bpy docs.

http://www.blender.org/api/blender_python_api_current/ As of 10/11 november 2015 we can now link to the current api docs and not be worr...

Showing posts with label openGL. Show all posts
Showing posts with label openGL. Show all posts

July 27, 2011

ready to generate geometry - edge fillet blender 2.5


the code is a little lengthy to post in its entirety, but you can download it here
def get_correct_verts(arc_centre, arc_start, arc_end, NUM_VERTS, context):

obj_centre = context.object.location
axis = mathutils.geometry.normal(arc_centre, arc_end, arc_start)

point1 = arc_start - arc_centre
point2 = arc_end - arc_centre
main_angle = point1.angle(point2)
main_angle_degrees = math.degrees(main_angle)

div_angle = main_angle / (NUM_VERTS - 1)

if DEBUG == True:
print("arc_centre =", arc_centre)
print("arc_start =", arc_start)
print("arc_end =", arc_end)
print("NUM_VERTS =", NUM_VERTS)

print("NormalAxis1 =", axis)
print("Main Angle (Rad)", main_angle, " > degrees", main_angle_degrees)
print("Division Angle (Radians)", div_angle)
print("AXIS:", axis)

trig_arc_verts = []

for i in range(NUM_VERTS):
rotation_matrix = mathutils.Matrix.Rotation(i*-div_angle, 3, axis)
# trig_point = (arc_start - obj_centre - arc_centre) * rotation_matrix # old
trig_point = rotation_matrix * (arc_start - obj_centre - arc_centre) # new
trig_point += obj_centre + arc_centre
trig_arc_verts.append(trig_point)

return trig_arc_verts
Matrix.Rotate() was the most complicated part, but now i've broken through that barrier it doesn't seem such utter voodoo anymore. Warning to user: you must apply scale/location/rotation transforms to your profile/object first before running the code or the vertices will appear somewhere else. Not sure if there is a cute way around that.

small update




blender 2.5 GL fillet v0.2 from zeffii stanton on Vimeo.

July 26, 2011

more opengl drawing to viewport blender 2.5

update to the code below can be found h e r e


if DRAW_POINTS is true then the gl drawing includes Vertex drawing like this:


import bpy
import bgl
import mathutils
import bpy_extras
import math

from mathutils import Vector
from mathutils.geometry import interpolate_bezier as bezlerp
from bpy_extras.view3d_utils import location_3d_to_region_2d as loc3d2d

# OBJECTIVE

# fillet the selected vertex of a profile by dragging the mouse inwards
# the max fillet radius is reached at the shortest delta of surrounding verts.


# [x] MILESTONE 1
# [x] get selected vertex index.
# [x] get indices of attached verts.
# [x] get their lengths, return shortest.
# [x] shortest length is max fillet radius.

# [x] MILESTONE 2
# [x] draw gl bevel line from both edges (bev_edge1, bev_edge2)
# [x] find center of bevel line
# [x] draw line of radius length from found_index through center of bevel line.
# [x] call new found point 'fillet_centre"
# [x] draw pline from bev_edge1 to 2, with distance from fillet_centre (kappa)
# [x] draw pline from bev_edge1 to 2, with distance from fillet_centre (trig)

# [ ] MILESTONE 3
# [x] draw faux vertices
# [ ] make shift+rightlick, draw a line from selected vertex to mouse cursor.
# [x] draw opengl filleted line.
# [ ] allow mouse wheel to define segment numbers.
# [ ] enter to accept, and make real. esc to cancel.


# ==============================================================================

''' temporary constants '''

NUM_SEGS = 15
NUM_VERTS = NUM_SEGS + 1
HALF_RAD = 0.5
# KAPPA = 0.5522847498 # approximates the circle, fascinating magic number!
KAPPA = 4 * (( math.sqrt(2) - 1) / 3 )



''' switches '''

mode = 'TRIG'
DRAW_POINTS = True



''' helper functions '''


def find_index_of_selected_vertex(obj):

# force 'OBJECT' mode temporarily. [TODO]
selected_verts = [i.index for i in obj.data.vertices if i.select]

# prevent script from operating if currently >1 vertex is selected.
verts_selected = len(selected_verts)
if verts_selected != 1:
return None
else:
return selected_verts[0]



def find_connected_verts(obj, found_index):

edges = obj.data.edges
connecting_edges = [i for i in edges if found_index in i.vertices[:]]
if len(connecting_edges) != 2:
return None
else:
connected_verts = []
for edge in connecting_edges:
cvert = set(edge.vertices[:])
cvert.remove(found_index)
connected_verts.append(cvert.pop())

return connected_verts



def find_distances(obj, connected_verts, found_index):
edge_lengths = []
for vert in connected_verts:
co1 = obj.data.vertices[vert].co
co2 = obj.data.vertices[found_index].co
edge_lengths.append([vert, (co1-co2).length])
return edge_lengths



def generate_fillet(obj, c_index, max_rad, f_index):

def get_first_cut(outer_point, focal, distance_from_f):
co1 = obj.data.vertices[focal].co
co2 = obj.data.vertices[outer_point].co
real_length = (co1-co2).length
ratio = distance_from_f / real_length

# must use new variable, cannot do co1 += obj_center, changes in place.
new_co1 = co1 + obj_centre
new_co2 = co2 + obj_centre
return new_co1.lerp(new_co2, ratio)

obj_centre = obj.location
distance_from_f = max_rad * HALF_RAD

# make imaginary line between outerpoints
outer_points = []
for point in c_index:
outer_points.append(get_first_cut(point, f_index, distance_from_f))

# make imaginary line from focal point to halfway between outer_points
focal_coordinate = obj.data.vertices[f_index].co + obj_centre
center_of_outer_points = (outer_points[0] + outer_points[1]) / 2

# find radial center, by lerping ab -> ad
BC = (center_of_outer_points-outer_points[1]).length
AB = (focal_coordinate-center_of_outer_points).length
BD = (BC/AB)*BC
AD = AB + BD
ratio = AD / AB
radial_center = focal_coordinate.lerp(center_of_outer_points, ratio)

guide_line = [focal_coordinate, radial_center]
return outer_points, guide_line



def resposition_arc_points(arc_verts, radial_centre):
# ensure that every arc point is indeed radial distance away from center.
revised_arc_points = []

radial_dist_first = (arc_verts[0]-radial_centre).length
radial_dist_last = (arc_verts[-1]-radial_centre).length
desired_radial_distance = (radial_dist_first + radial_dist_last) * 0.5

for point in arc_verts:
radial_distance = (point-radial_centre).length
ratio = 1/(radial_distance / desired_radial_distance)
new_location = radial_centre.lerp(point, ratio)
new_distance = (radial_centre-new_location).length
revised_arc_points.append(new_location)
print("was", radial_distance, "becomes", new_distance)

return revised_arc_points



''' director function '''



def init_functions(self, context):

obj = context.object

# Finding vertex.
found_index = find_index_of_selected_vertex(obj)
if found_index != None:
print("you selected vertex with index", found_index)
connected_verts = find_connected_verts(obj, found_index)
else:
print("select one vertex, no more, no less")
return


# Find connected vertices.
if connected_verts == None:
print("vertex connected to only 1 other vert, or none at all")
print("remove doubles, the script operates on vertices with 2 edges")
return
else:
print(connected_verts)


# reaching this stage means the vertex has 2 connected vertices. good.
# Find distances and maximum radius.
distances = find_distances(obj, connected_verts, found_index)
for d in distances:
print("from", found_index, "to", d[0], "=", d[1])

max_rad = min(distances[0][1],distances[1][1])
print("max radius", max_rad)


return generate_fillet(obj, connected_verts, max_rad, found_index)



''' GL drawing '''



def draw_polyline_from_coordinates(context, points, LINE_TYPE):
region = context.region
rv3d = context.space_data.region_3d

bgl.glColor4f(1.0, 1.0, 1.0, 1.0)

if LINE_TYPE == "GL_LINE_STIPPLE":
bgl.glLineStipple(4, 0x5555)
bgl.glEnable(bgl.GL_LINE_STIPPLE)
bgl.glColor4f(0.3, 0.3, 0.3, 1.0)

bgl.glBegin(bgl.GL_LINE_STRIP)
for coord in points:
vector3d = (coord.x, coord.y, coord.z)
vector2d = loc3d2d(region, rv3d, vector3d)
bgl.glVertex2f(*vector2d)
bgl.glEnd()

if LINE_TYPE == "GL_LINE_STIPPLE":
bgl.glDisable(bgl.GL_LINE_STIPPLE)
bgl.glEnable(bgl.GL_BLEND) # back to uninterupted lines

return



def draw_points(context, points, size):
region = context.region
rv3d = context.space_data.region_3d


bgl.glEnable(bgl.GL_POINT_SMOOTH)
bgl.glPointSize(size)
# bgl.glEnable(bgl.GL_BLEND)
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)

bgl.glBegin(bgl.GL_POINTS)
# draw red
bgl.glColor4f(1.0, 0.2, 0.2, 1.0)
for coord in points:
vector3d = (coord.x, coord.y, coord.z)
vector2d = loc3d2d(region, rv3d, vector3d)
bgl.glVertex2f(*vector2d)
bgl.glEnd()

bgl.glDisable(bgl.GL_POINT_SMOOTH)
bgl.glDisable(bgl.GL_POINTS)
return



def draw_callback_px(self, context):

objlist = context.selected_objects
names_of_empties = [i.name for i in objlist]

region = context.region
rv3d = context.space_data.region_3d
points, guide_verts = init_functions(self, context)

# draw bevel
draw_polyline_from_coordinates(context, points, "GL_LINE_STIPPLE")

# draw symmetry line
draw_polyline_from_coordinates(context, guide_verts, "GL_LINE_STIPPLE")

# get control points and knots.
h_control = guide_verts[0]
knot1, knot2 = points[0], points[1]
kappa_ctrl_1 = knot1.lerp(h_control, KAPPA)
kappa_ctrl_2 = knot2.lerp(h_control, KAPPA)
arc_verts = bezlerp(knot1, kappa_ctrl_1, kappa_ctrl_2, knot2, NUM_VERTS)

# draw fillet ( 2 modes )
if mode == 'TRIG':
radial_centre = guide_verts[1]
arc_verts = resposition_arc_points(arc_verts, radial_centre)
if mode == 'KAPPA':
print("using vanilla kappa, this mode produces a poor approximation")
pass

draw_polyline_from_coordinates(context, arc_verts, "GL_BLEND")

if DRAW_POINTS == True:
draw_points(context, arc_verts, 4.2)

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



''' UI elements '''



class UIPanel(bpy.types.Panel):
bl_label = "Hello from UI panel"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"

scn = bpy.types.Scene
object = bpy.context.object
# scn.Monster = object.location.x
# scn.MyMove = bpy.props.FloatProperty()


def draw(self, context):
layout = self.layout
ob = context.object
scn = context.scene

row1 = layout.row(align=True)
# row1.prop(ob, "location")
row1.operator("dynamic.fillet")
# row1.prop(ob, 'location', index = 0, text = "Spine Spline", slider = True)




class OBJECT_OT_add_object(bpy.types.Operator):
bl_idname = "dynamic.fillet"
bl_label = "Check Vertice"
bl_description = "Allows the user to dynamically fillet a vert/edge"
bl_options = {'REGISTER', 'UNDO'}

'''
scale = FloatVectorProperty(name='scale',
default=(1.0, 1.0, 1.0),
subtype='TRANSLATION',
description='scaling')
'''


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

if event.type == 'RIGHTMOUSE':
if event.value == 'RELEASE':
print("discontinue drawing")
context.area.tag_redraw()
context.region.callback_remove(self._handle)
return {'CANCELLED'}

return {'RUNNING_MODAL'}



def invoke(self, context, event):

if context.area.type == 'VIEW_3D':
context.area.tag_redraw()
context.window_manager.modal_handler_add(self)

# Add the region OpenGL drawing callback
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
self._handle = context.region.callback_add(
draw_callback_px,
(self, context),
'POST_PIXEL')

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


'''

def execute(self, context):

#add_object(self, context)
init_functions(self, context)

return {'FINISHED'}

'''



bpy.utils.register_module(__name__)

July 18, 2011

basic calliper blender 2.5 (distance and angle)



Two modes to the plugin, this shows the 3 empty setup, it displays the angle between them. When you have two empties selected it displays the imaginary tetrahedron created by the bounding box of the 2 points in space.



code here : download


# it's over 700 lines of code i think, plenty of juicy examples

July 13, 2011

OpenGL / bgl in blender 2.5

select two empties, run this code.

import bpy
import bgl
import blf
import bpy_extras

from mathutils import Vector
from bpy.props import StringProperty, FloatProperty
from bpy_extras import view3d_utils

'''
helper functions
'''

def get_objects(context):

sel_obs = context.selected_objects
names = [object.name for object in sel_obs if object.type=='EMPTY']
if len(names) == 2:
return names
else:
return None


def get_distance(names_of_empties):

if names_of_empties == None:
return 0.0

coordlist = []
for name in names_of_empties:
coordlist.append(bpy.data.objects[name].location)

return (coordlist[0]-coordlist[1]).length


def get_distance_from_context(context):
distance = get_distance(get_objects(context))
return distance


def get_coordinates_from_empties(object_list):
coordlist = [obj.location for obj in object_list]
return coordlist


def get_difference(axis, coord):

if axis == 'z':
return abs((coord[0]-coord[1]).z)
elif axis == 'y':
return abs((coord[0]-coord[1]).y)
elif axis == 'x':
return abs((coord[0]-coord[1]).x)
else:
return None


def return_sorted_coordlist(coords):
def MyFn(coord):
return coord.z
return sorted(coords, key=MyFn, reverse=True)


'''
openGL drawing
'''


def draw_text(col, y_pos, display_text, view_width, context):

# calculate text width, then draw
font_id = 0
blf.size(font_id, 18, 72) #fine tune

text_width, text_height = blf.dimensions(font_id, display_text)
right_align = view_width-text_width-18
blf.position(font_id, right_align, y_pos, 0)
blf.draw(font_id, display_text)
return


def draw_tetrahedron(region, rv3d, context, clist):

# highest point is apex
apex, baseco = return_sorted_coordlist(clist)

# define the base of the tetrahydron
base1 = Vector((apex.x, apex.y, baseco.z))
base2 = Vector((apex.x, baseco.y, baseco.z))
base3 = baseco

# converting to screen coordinates
screen_apex = view3d_utils.location_3d_to_region_2d(region, rv3d, apex)
screen_base1 = view3d_utils.location_3d_to_region_2d(region, rv3d, base1)
screen_base2 = view3d_utils.location_3d_to_region_2d(region, rv3d, base2)
screen_base3 = view3d_utils.location_3d_to_region_2d(region, rv3d, base3)

# bgl.glBegin(bgl.GL_LINE)

# colour + line setup, 50% alpha, 1 px width line
bgl.glEnable(bgl.GL_BLEND)
bgl.glColor4f(0.1, 0.3, 1.0, 0.8)
bgl.glLineWidth(1)

# from top to base coordinates
bgl.glColor4f(0.6, 0.6, 0.6, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_apex)
bgl.glVertex2f(*screen_base3)
bgl.glEnd()

bgl.glColor4f(0.1, 0.3, 1.0, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_apex)
bgl.glVertex2f(*screen_base1)
bgl.glEnd()

bgl.glColor4f(0.1, 0.3, 1.0, 0.2)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_apex)
bgl.glVertex2f(*screen_base2)
bgl.glEnd()

# between base coordinates
bgl.glColor4f(1.0, 0.1, 0.1, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_base3)
bgl.glVertex2f(*screen_base2)
bgl.glEnd()

bgl.glColor4f(0.0, 1.0, 0.1, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_base2)
bgl.glVertex2f(*screen_base1)
bgl.glEnd()

bgl.glColor4f(0.1, 0.3, 1.0, 0.2)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_base1)
bgl.glVertex2f(*screen_base3)
bgl.glEnd()


return


def draw_callback_px(self, context):
rounding = 6

objlist = context.selected_objects
names_of_empties = [i.name for i in objlist]
distance_value = get_distance(names_of_empties)
coordinate_list = get_coordinates_from_empties(objlist)

region = bpy.context.region
rv3d = bpy.context.space_data.region_3d
view_width = context.region.width

# major rewrite candidate
l_distance = str(round(distance_value, rounding))
x_distance = round(get_difference('x', coordinate_list),rounding)
y_distance = round(get_difference('y', coordinate_list),rounding)
z_distance = round(get_difference('z', coordinate_list),rounding)
l_distance = str(l_distance)+" lin"
x_distance = str(x_distance)+" x"
y_distance = str(y_distance)+" y"
z_distance = str(z_distance)+" z"

y_heights = 88, 68, 48, 20
y_heights = [m-9 for m in y_heights] # fine tune

str_dist = x_distance, y_distance, z_distance, l_distance
for i in range(len(y_heights)):
draw_text(True, y_heights[i], str_dist[i], view_width, context)

# 50% alpha, 2 pixel width line
bgl.glEnable(bgl.GL_BLEND)

bgl.glColor4f(0.7, 0.7, 0.7, 0.5)
bgl.glLineWidth(1)

bgl.glBegin(bgl.GL_LINE_STRIP)
for coord in coordinate_list:
vector3d = (coord.x, coord.y, coord.z)
vector2d = view3d_utils.location_3d_to_region_2d(region, rv3d, vector3d)
bgl.glVertex2f(*vector2d)
bgl.glEnd()

draw_tetrahedron(region, rv3d, context, coordinate_list)

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



'''
tool panel and button definitions
'''


class ToolPropsPanel(bpy.types.Panel):
bl_label = "Empties Calliper"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"

scn = bpy.types.Scene
ctx = bpy.context

@classmethod
def poll(self, context):
names_of_empties = get_objects(context)
if names_of_empties != None:
return True

def draw(self, context):

display_distance_field = False

layout = self.layout
scn = context.scene

names_of_empties = get_objects(context)
names_str = str(names_of_empties)

if names_of_empties != None:
display_distance_field = True
distance_value = get_distance(names_of_empties)


distance_value = str(distance_value)

# drawing
row1 = layout.row(align=True)
row1.operator("hello.hello", text=names_str)

if display_distance_field == True:
row3 = layout.row(align=True)
row3.label(distance_value)


class OBJECT_OT_HelloButton(bpy.types.Operator):
bl_idname = "hello.hello"
bl_label = "Say Hello"

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

if event.type == 'MOUSEMOVE':
print("mouse moved")
elif event.type in ('RIGHTMOUSE', 'ESC'):
context.region.callback_remove(self._handle)
return {'CANCELLED'}

return {'RUNNING_MODAL'}

def invoke(self, context, event):
if context.area.type == 'VIEW_3D':
context.window_manager.modal_handler_add(self)

# Add the region OpenGL drawing callback
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
self._handle = context.region.callback_add(
draw_callback_px,
(self, context),
'POST_PIXEL')

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

return {'CANCELLED'}



bpy.utils.register_module(__name__)



and

latest version (wed 13 july 2011):

i'll want something like

July 04, 2011

bgl drawing with OpenGL onto blender 2.5 view

original thread over at blenderartists.com, i never finished it but the bgl is decent enough to get someone started.
'''
This script resects the camera position into virtual 3d space.
Dealga McArdle (c) 2011

The program may be distributed under the terms of the GNU General
Public License. The full terms of GNU GPL2 can be found at: 
http://www.gnu.org/licenses/gpl-2.0.html

Be sure that you understand the GLP2 terms prior to using this script.
Nothing between these tripple quote marks should be construed as
having deminished the full extent of the GPL2 license.
'''

import bpy
import bgl
import blf
from mathutils.geometry import intersect_line_line
from mathutils import Vector

'''
- TODO: complete vanishing point and horizon drawing
- TODO: correctly deal with impossible guides orientations
- TODO: implement rudimentary double buffer for 3d openGL drawing
'''


''' defining globals '''

# initial end point locations, lateron modified by the user.
guide_green_1 = [50, 100, 280, 120]
guide_green_2 = [50, 70, 280, 60]
guide_red_1 = [300, 120, 580, 100]
guide_red_2 = [300, 30, 580, 70]
guide_blue = [250, 50, 250, 250]
h_collection = [guide_green_1, guide_green_2, guide_red_1, guide_red_2, guide_blue]

# colours defined here for scope
line_green = 0.0, 1.0, 0.0, 0.4
line_red = 1.0, 0.0, 0.0, 0.4
line_blue = 0.0, 0.0, 1.0, 0.4
l_col_green = 0.5, 1.0, 0.5, 0.6
l_col_red = 1.0, 0.3, 0.3, 0.6
l_col_cyan = 0.6, 0.6, 1.0, 0.4

# handle size is double this value
hSize = 5

# colours/transparency of viewport text
dimension_colour = (1.0, 1.0, 1.0, 1.0)
explanation_colour = (1.0, 1.0, 1.0, 0.7)


''' G L  D R A W I N G '''


def drawOneLine(x1, y1, x2, y2, colour):
    '''accepts 2 coordinates and a colour then draws
    the line and the handle'''

    def DrawHandle(hX, hY):
        bgl.glBegin(bgl.GL_LINE_LOOP)
        bgl.glVertex2i(hX+hSize, hY-hSize)
        bgl.glVertex2i(hX-hSize, hY-hSize)
        bgl.glVertex2i(hX-hSize, hY+hSize)
        bgl.glVertex2i(hX+hSize, hY+hSize)
        bgl.glEnd()

    #set colour to use
    bgl.glColor4f(*colour)

    #draw main line and handles
    bgl.glBegin(bgl.GL_LINES)
    bgl.glVertex2i(x1,y1)
    bgl.glVertex2i(x2,y2)
    bgl.glEnd()
    DrawHandle(x1, y1)
    DrawHandle(x2, y2)


def DrawOrientationLines():
    '''configure and initialize the 5 orientation lines
    drawOneLine(x1, y1, x2, y2, colour)'''
    drawOneLine(*guide_green_1, colour=line_green)  #green
    drawOneLine(*guide_green_2, colour=line_green)
    drawOneLine(*guide_red_1, colour=line_red)  #red
    drawOneLine(*guide_red_2, colour=line_red)
    drawOneLine(*guide_blue, colour=line_blue)  #blue


def DrawPerspectiveLine(x1, y1, x2, y2, l_colour):
    '''reckon could be refactored with DrawOneLine
    but i'm considering giving these lines dashed style'''
    bgl.glColor4f(*l_colour)
    bgl.glBegin(bgl.GL_LINES)
    bgl.glVertex2i(x1,y1)
    bgl.glVertex2i(x2,y2)
    bgl.glEnd()


def IntersectionOf(line1, line2):
    '''mathutils.geometry expects lines to be expressed as two
    Vectors with three dimensions, at this point we pick an
    arbitrary value for the z component of this vector.
    I'm only interested in how the two guides extend towards
    a vanishing point, and the resulting x,y coordinate.'''
    arbitrary_z_value = 0.0
    A = Vector((line1[0], line1[1], arbitrary_z_value))
    B = Vector((line1[2], line1[3], arbitrary_z_value))
    C = Vector((line2[0], line2[1], arbitrary_z_value))
    D = Vector((line2[2], line2[3], arbitrary_z_value))
    my_xyz = intersect_line_line(A, B, C, D)
    if my_xyz == None: return 10,40
    return int(my_xyz[0][0]), int(my_xyz[0][1])


def DrawHorizonAndVanishingPoints():
    '''use the current state of the guide coordinates, to draw:
    - both vanishing points, O and R
    - all 4 guide ends (M,N,P,Q)'''
    # setting up extra drawing points/lines.
    p_point_o = IntersectionOf(guide_green_1, guide_green_2)
    p_point_r = IntersectionOf(guide_red_1, guide_red_2)

    p_line_mo = p_point_o[0], p_point_o[1], h_collection[0][0], h_collection[0][1]
    p_line_no = p_point_o[0], p_point_o[1], h_collection[1][0], h_collection[1][1]
    p_line_pr = p_point_r[0], p_point_r[1], h_collection[2][2], h_collection[2][3]
    p_line_qr = p_point_r[0], p_point_r[1], h_collection[3][2], h_collection[3][3]
    h_line_or = p_point_o[0], p_point_o[1], p_point_r[0], p_point_r[1]
    
    # draw the resulting perspective lines and horizon.
    DrawPerspectiveLine(*p_line_mo, l_colour = l_col_green) # green
    DrawPerspectiveLine(*p_line_no, l_colour = l_col_green) 
    DrawPerspectiveLine(*p_line_pr, l_colour = l_col_red) # red
    DrawPerspectiveLine(*p_line_qr, l_colour = l_col_red)
    DrawPerspectiveLine(*h_line_or, l_colour = l_col_cyan) # cyan - horizon
    return


def DrawGeneratedAxis():
    '''for drawing the dashed line to indicate major axis'''
    return


def DrawStringToViewport(my_string, pos_x, pos_y, size, colour_type):
    ''' my_string : the text we want to print
        pos_x, pos_y : coordinates in integer values
        size : font height.
        colour_type : used for definining the colour'''
    my_dpi, font_id = 72, 0 # dirty fast assignment
    bgl.glColor4f(*colour_type)
    blf.position(font_id, pos_x, pos_y, 0)
    blf.size(font_id, size, my_dpi)
    blf.draw(font_id, my_string)


def InitViewportText(self, context):
    '''used to deligate opengl text printing to the viewport'''
    this_h = context.region.height
    this_w = context.region.width
    dimension_string = "IMAGE_EDITOR: H " + str(this_h) + " / W " + str(this_w)
    explanation_string = "right-click to release the script, data will be stored"
    DrawStringToViewport(dimension_string, 10, 20, 20, dimension_colour)
    DrawStringToViewport(explanation_string, 10, 7, 10, explanation_colour)


def InitGLOverlay(self, context):
    InitViewportText(self, context)

    # 50% alpha, 2 pixel width line
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
    bgl.glLineWidth(1.5)

    # start visible drawing
    DrawHorizonAndVanishingPoints()
    DrawOrientationLines()
    ## DrawGeneratedAxis()
    ## DrawGrid()

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


''' H A N D L E  C H E C K I N G '''


def CheckIsCursorOnPickPoints(event):
    '''CheckIsCursorOnPickPoints is not a prime example of HiQ code,
    but for now it does what i need, and identifies what handle will
    be modified by the drag, 0 = first handle, 2= second handle'''

    def FindGuideIndex(coordinates):
        for coord_iterator in range(len(h_collection)):
            if coordinates == h_collection[coord_iterator]:
                return coord_iterator

    def CheckHandle(guide, g_handle):
        if cmX >= (guide[g_handle]-hSize):
            if cmX <= (guide[g_handle]+hSize):
                if cmY >= (guide[g_handle+1]-hSize):
                    if cmY <= (guide[g_handle+1]+hSize):
                        return True
        else: return False

    def IsOnHandle(h_collection, cmX, cmY):
        for guide in h_collection:
            if CheckHandle(guide, 0): return (guide, 0)
            elif CheckHandle(guide, 2): return (guide, 2)
        return 'None'

    cmX, cmY = event.mouse_region_x, event.mouse_region_y
    is_on_handle_response = IsOnHandle(h_collection, cmX, cmY)
    if is_on_handle_response == 'None':
        return('None')
    else:
        handle_coordinates, handle_num = is_on_handle_response
        guide_index = FindGuideIndex(handle_coordinates)
        return(guide_index, handle_num)



class CameraMatchingPanel(bpy.types.Panel):
    bl_label = "Camera Matching"
    bl_space_type = "IMAGE_EDITOR"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        layout.label("Blue: nearest vertical")
        layout.label("Red/Green: perpendicular lines")
        layout.separator()

        layout = self.layout
        layout.label("Draw Perspective Lines")
        row = layout.row(align=True)
        row.operator("object.button", icon="MANIPUL")

        layout = self.layout
        layout.label("Solve Camera Location")
        row = layout.row(align=True)
        row.operator("object.button2", icon='SCENE')


class OBJECT_OT_Button(bpy.types.Operator):
    bl_idname = "object.button"
    bl_label = "Enable"

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

        if event.type == 'LEFTMOUSE':
            if event.value == 'PRESS':
                self.cursor_on_handle = CheckIsCursorOnPickPoints(event)
                if self.cursor_on_handle == 'None': print("no handle associated")
                else: print(self.cursor_on_handle)
            if event.value == 'RELEASE':
                self.cursor_on_handle = 'None'

        if event.type == 'MOUSEMOVE' and self.cursor_on_handle != 'None':
            print("mouse moving x", event.mouse_region_x,"y", event.mouse_region_y)
            global h_collection
            h_collection[self.cursor_on_handle[0]][self.cursor_on_handle[1]] = \   
                                                            event.mouse_region_x
            h_collection[self.cursor_on_handle[0]][self.cursor_on_handle[1]+1] = \                                                                 
                                                            event.mouse_region_y

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

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.area.type == 'IMAGE_EDITOR':
            self.cursor_on_handle = 'None'
            context.window_manager.modal_handler_add(self)

            # Add the region OpenGL drawing callback
            # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
            PP = 'POST_PIXEL'
            dest = (self, context)
            self._handle = context.region.callback_add(InitGLOverlay, dest, PP)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Image View not found, cannot run operator")
            return {'CANCELLED'}


class OBJECT_OT_Button2(bpy.types.Operator):
    bl_idname = "object.button2"
    bl_label = "Place Camera and Empty"

    def execute(self, context):
        print("Hello camera")
        return{'FINISHED'}

def register():
    bpy.utils.register_class(OBJECT_OT_Button)
    bpy.utils.register_class(OBJECT_OT_Button2)
    bpy.utils.register_class(CameraMatchingPanel)

def unregister():
    bpy.utils.unregister_class(OBJECT_OT_Button)
    bpy.utils.unregister_class(OBJECT_OT_Button2)
    bpy.utils.unregister_class(CameraMatchingPanel)

if __name__ == "__main__":
    register()