import bpy, mathutils, bmesh
import random, math, time, typing, shapely, shapely.geometry, shapely.ops
from . import Node, DATA_Mesh, DATA_GETTER_NODE_SC_Meshes, DATA_Geometries, DATA_GETTER_NODE_Geometries, SC_Operator, \
	SOCKET_Meshes, DATA_GETTER_NODE_SC_Objects, \
	SOCKET_Geoms2D, DATA_GETTER_NODE_SC_Curves, SC_Curve, SC_Curves_Socket, SC_Objects_Socket, SC_Node_variable_inputs
from .. import my_globals, utils
from typing import List, Tuple

memory_storage = {}
next_time_to_clean_storage = time.time()


def clean_memory_storage():
	memory_storage.clear()
	# print("memory cleaned")
	global next_time_to_clean_storage
	next_time_to_clean_storage = time.time() + 2


class Shade_smooth_flat(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_ntt3fpjgqhr6cp6l34vs'
	bl_label = 'Shade smooth / flat'
	# bl_icon = 'MESH_DATA'


	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	# def sc_draw_buttons(self, context, layout):
	# 	layout.prop(self, 'slot_nb')

	@Node.get_data_first
	def get_sc_meshes(self, meshes: List[DATA_Mesh], *args, **kwargs):
		pass
	# 	result_sc_meshes = []
	#
	# 	for mesh in meshes:
	# 		utils.selectionner_un_seul_obj(mesh.tmp_obj)
	# 		mesh.tmp_obj.active_material_index = self.slot_nb
	# 		utils.definir_blender_mode('EDIT')
	#
	# 		bpy.context.scene.tool_settings.mesh_select_mode[2] = True
	# 		bpy.ops.object.material_slot_assign()
	#
	# 		result_sc_meshes.append(mesh)
	# 	return result_sc_meshes


class Copy_Verts_Z_From_Input_1_to_2(bpy.types.Node, SC_Node_variable_inputs, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_4j5pt7wajwdkqv6ethdu'
	bl_label = 'Copy closest vert Z'
	# icon_value = 'MESH_3D'
	icon_scale = 5

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True, label='From mesh(es)')
		self.create_input(SOCKET_Meshes, is_required=True, label='To mesh(es)')
		self.create_output(SOCKET_Meshes, is_new_data_output=True)

	def sc_draw_buttons(self, context, layout):
		pass

	def _get_sc_meshes_necessary_data(self, *args, **kwargs):
		input0: SOCKET_Meshes = self.inputs[0]
		input1: SOCKET_Meshes = self.inputs[1]
		return input0.get_input_sc_meshes(), input1.get_input_sc_meshes()

	@Node.get_data_first
	def get_sc_meshes(self, meshes: Tuple[List[DATA_Mesh], List[DATA_Mesh]], *args, **kwargs):
		# raise Exception
		source_meshes = meshes[0]
		target_meshes = meshes[1]

		# raise Exception

		# on construit la collection des points source
		source_meshes_points = []
		coord2d_to_source_vert = {}
		for source_sc_mesh in source_meshes:
			for v in source_sc_mesh.bl_mesh.vertices:
				coord2d_to_source_vert[(v.co[0], v.co[1])] = v
				source_meshes_points.append(shapely.geometry.Point(tuple(v.co)))
		source_meshes_points_collection = shapely.geometry.GeometryCollection(source_meshes_points)

		# raise Exception

		# on compare chaque target mesh à la totalité des points source
		for target_sc_mesh in target_meshes:
			# target_bm = bmesh.new()
			# target_bm.from_mesh(target_sc_mesh.bl_mesh)

			# pour chaque vert du target mesh
			# for v in target_bm.verts:
			for v in target_sc_mesh.bl_mesh.vertices:
				# on cherche le vert le plus proche des sources meshes, en passant par les points 2d
				v_point = shapely.geometry.Point(tuple(v.co))
				point1, point2 = shapely.ops.nearest_points(v_point, source_meshes_points_collection)
				# raise Exception
				assert point1.x == v_point.x and point1.y == v_point.y
				source_vert = coord2d_to_source_vert[(point2.x, point2.y)]
				v.co[2] = source_vert.co[2]

		# target_bm.to_mesh(target_sc_mesh.bl_mesh)
		# target_bm.free()

		return target_meshes


class Mesh_Grouping(bpy.types.Node, SC_Node_variable_inputs, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_5aewo220o0z364tomfw6'
	bl_label = 'Group meshes (NOT a merge!)'
	new_inputs_socket_type = SOCKET_Meshes
	new_inputs_are_required = True
	# icon_value = 'MESH_3D'
	icon_scale = 5

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def sc_draw_buttons(self, context, layout):
		row = layout.row()
		# self.create_operator(row, self.SC_OT_AddInput, 'add_input')
		self.create_operator(row, self.SC_OT_AddInput)
		# self.create_operator(row, self.SC_OT_RemoveInput, 'remove_input')
		self.create_operator(row, self.SC_OT_RemoveInput)

	def _get_sc_meshes_necessary_data(self, *args, **kwargs):
		meshes: List[DATA_Mesh] = []
		for input in self.inputs:
			input: SOCKET_Meshes = input
			meshes.extend(input.get_input_sc_meshes())
		return meshes

	@Node.get_data_first
	def get_sc_meshes(self, meshes: List[DATA_Mesh], *args, **kwargs):
		return meshes


class Extrude_Selection(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_sy8kcvp35wlvixs49r8r'
	bl_label = 'Extrude mesh selection'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	move_x: bpy.props.FloatProperty(
		name='Move X',
		description='Displacement of extruded part along x axis of selected orientation', )
	move_y: bpy.props.FloatProperty(
		name='Move Y',
		description='Displacement of extruded part along y axis of selected orientation', )
	move_z: bpy.props.FloatProperty(
		name='Move Z',
		description='Displacement of extruded part along z axis of selected orientation', )
	orientation: bpy.props.EnumProperty(
		name='Orientation',
		description='Orientation of movement after extrusion',
		items=[
			('GLOBAL', 'Global', ''),
			# ('LOCAL', 'Local', ''),
			# ('NORMAL', 'Normal', ''),
		])

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'move_x')
		layout.prop(self, 'move_y')
		layout.prop(self, 'move_z')
		layout.prop(self, 'orientation')

	@Node.get_data_first
	def get_sc_meshes(self, meshes: List[DATA_Mesh], *args, **kwargs):
		for mesh in meshes:
			utils.selectionner_un_seul_obj(mesh.tmp_obj)
			utils.definir_blender_mode('EDIT')
			# bpy.context.scene.tool_settings.mesh_select_mode[2] = True

			# print(self.move_x, self.move_y, self.move_z)
			# bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={"value": (self.move_x, self.move_y, self.move_z)})
			# bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={"value": (0,0,1)})
			bpy.ops.mesh.extrude_region()
			# print('1')
			# utils.definir_blender_mode('OBJECT')
			# bpy.ops.transform.translate(value=(-2.32831e-10, -4.65661e-10, 0.835406), orient_type='NORMAL',
			# 	orient_matrix=((0.98091, 0.19443, -0.00357267), (-0.194436, 0.980914, -0.00142737), (0.00322696, 0.00209478, 0.999993)),
			# 	orient_matrix_type='NORMAL', constraint_axis=(False, False, True), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH',
			# 	proportional_size=9.84974, use_proportional_connected=False, use_proportional_projected=False)
			# print('2')
			utils.definir_blender_mode('OBJECT')
			for v in mesh.bl_mesh.vertices:
				if v.select:
					v.co = (v.co[0] + self.move_x, v.co[1] + self.move_y, v.co[2] + self.move_z)
			# bpy.ops.mesh.extrude_region_move(MESH_OT_extrude_region={"use_normal_flip":False, "mirror":False}, TRANSFORM_OT_translate={"value":(-1.16415e-10, -1.16415e-10, 0.248824), "orient_type":'NORMAL', "orient_matrix":((0.98091, 0.19443, -0.00357267), (-0.194436, 0.980914, -0.00142737), (0.00322696, 0.00209478, 0.999993)), "orient_matrix_type":'NORMAL', "constraint_axis":(False, False, True), "mirror":False, "use_proportional_edit":False, "proportional_edit_falloff":'SMOOTH', "proportional_size":9.84974, "use_proportional_connected":False, "use_proportional_projected":False, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "gpencil_strokes":False, "cursor_transform":False, "texture_space":False, "remove_on_cancel":False, "release_confirm":False, "use_accurate":False})

			# bpy.ops.transform.translate(value=(-1.16415e-10, -1.01863e-10, 0.128971), orient_type='NORMAL',
			# 	orient_matrix=((0.98091, 0.19443, -0.00357267), (-0.194436, 0.980914, -0.00142737), (0.00322696, 0.00209478, 0.999993)),
			# 	orient_matrix_type='NORMAL', constraint_axis=(False, False, True), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH',
			# 	proportional_size=9.84974, use_proportional_connected=False, use_proportional_projected=False)
			# bpy.ops.transform.translate(value=(0, -1.16415e-10, 1), orient_type='NORMAL',
			# 	orient_matrix=((0.98091, 0.19443, -0.00357267), (-0.194436, 0.980914, -0.00142737), (0.00322696, 0.00209478, 0.999993)),
			# 	orient_matrix_type='NORMAL', constraint_axis=(False, False, True), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH',
			# 	proportional_size=9.84974, use_proportional_connected=False, use_proportional_projected=False)
			# utils.definir_blender_mode('OBJECT')

		return meshes


class Mesh_Assign_Material_Slot_To_Selection(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_wb085uc00jakrsv2kzjw'
	bl_label = 'Assign material slot to mesh selection'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	slot_nb: bpy.props.IntProperty(
		name='Slot nb',
		description='The index of the material slot to assign to the selected faces of the mesh. First slot is at index 0',
		min=0,
		default=0)

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'slot_nb')

	@Node.get_data_first
	def get_sc_meshes(self, meshes: List[DATA_Mesh], *args, **kwargs):
		result_sc_meshes = []

		for mesh in meshes:
			utils.selectionner_un_seul_obj(mesh.tmp_obj)
			mesh.tmp_obj.active_material_index = self.slot_nb
			utils.definir_blender_mode('EDIT')

			bpy.context.scene.tool_settings.mesh_select_mode[2] = True
			bpy.ops.object.material_slot_assign()

			result_sc_meshes.append(mesh)
		return result_sc_meshes


class Mesh_Inset(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_y6t81ue4wgfr05z1sul4'
	bl_label = 'Inset mesh selection'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	thickness: bpy.props.FloatProperty(
		name='Thickness',
		description='Choose what type of selection you need',
		min=0,
		default=.5)
	interpolate: bpy.props.BoolProperty(
		name='Interpolate',
		description='Blend face data across the inset',
		default=True)

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'thickness')
		layout.prop(self, 'interpolate')

	@Node.get_data_first
	def get_sc_meshes(self, meshes: List[DATA_Mesh], *args, **kwargs):
		result_sc_meshes = []

		for mesh in meshes:
			utils.selectionner_un_seul_obj(mesh.tmp_obj)
			utils.definir_blender_mode('EDIT')

			bpy.ops.mesh.inset(
				use_boundary=True,
				use_even_offset=False,
				use_relative_offset=False,
				use_edge_rail=False,
				thickness=self.thickness,
				depth=0,
				use_outset=False,
				use_select_inset=False,
				use_individual=False,
				use_interpolate=False)

			result_sc_meshes.append(mesh)
		return result_sc_meshes


class Select(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_wu7jley323ypjtayaare'
	bl_label = 'Select mesh parts'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	selection_type: bpy.props.EnumProperty(
		name='Selection',
		description='Choose what type of selection you need',
		items=[
			('all', 'All', ''),
			('none', 'None', ''),
			('invert', 'Invert', ''),
		])
	selection_mode_verts: bpy.props.BoolProperty(
		name='Selection mode: verts',
		description='Should the selection action consider verts as well? (like in the 3d viewport)',
		default=False)
	selection_mode_edges: bpy.props.BoolProperty(
		name='Selection mode: edges',
		description='Should the selection action consider edges as well? (like in the 3d viewport)',
		default=False)
	selection_mode_faces: bpy.props.BoolProperty(
		name='Selection mode: faces',
		description='Should the selection action consider faces as well? (like in the 3d viewport)',
		default=True)

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'selection_type')
		row = layout.row()
		row.prop(self, 'selection_mode_verts', text='', icon='VERTEXSEL')
		row.prop(self, 'selection_mode_edges', text='', icon='EDGESEL')
		row.prop(self, 'selection_mode_faces', text='', icon='FACESEL')

	@Node.get_data_first
	def get_sc_meshes(self, meshes: List[DATA_Mesh], *args, **kwargs):
		# result_sc_meshes = []

		for mesh in meshes:
			utils.selectionner_un_seul_obj(mesh.tmp_obj)
			utils.definir_blender_mode('EDIT')

			bpy.context.scene.tool_settings.mesh_select_mode = (self.selection_mode_verts, self.selection_mode_edges, self.selection_mode_faces)

			if 'all' == self.selection_type:
				bpy.ops.mesh.select_all(action='SELECT')
			elif 'none' == self.selection_type:
				bpy.ops.mesh.select_all(action='DESELECT')
			elif 'invert' == self.selection_type:
				bpy.ops.mesh.select_all(action='INVERT')

			# result_sc_meshes.append(mesh)
		# return result_sc_meshes
		return meshes


class Assign_Material_To_Slot(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_tl9r5gez9k7e7hzg4hpu'
	bl_label = 'Assign material to mesh slot'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	material: bpy.props.PointerProperty(name='Material', type=bpy.types.Material, update=Node.prop_updated)
	slot_number: bpy.props.IntProperty(
		name='Slot number',
		min=0,
		default=0,
		max=100,
		description='First slot starts at zero. Make sure the mesh has enough material slots available first. This node does not create the slots')

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def sc_draw_buttons(self, context, layout):
		col = layout.column(align=True)
		col.prop(self, 'slot_number')
		col.prop(self, 'material')

	@Node.get_data_first
	def get_sc_meshes(self, meshes: List[DATA_Mesh], *args, **kwargs):
		result_sc_meshes = []
		for mesh in meshes:
			mesh.bl_mesh.materials[self.slot_number] = self.material
			result_sc_meshes.append(mesh)
		return result_sc_meshes


class Create_Material_Slots(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_bgz72b3lt42kxtifmt8d'
	bl_label = 'Create material slots on mesh'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	total: bpy.props.IntProperty(
		name='Total',
		min=1,
		default=1,
		max=100,
		description='Ensures there are at least so many material slots present on the mesh, to assign different materials to it later')

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'total')

	@Node.get_data_first
	def get_sc_meshes(self, sc_meshes: List[DATA_Mesh], *args, **kwargs):
		result_sc_meshes = []
		for mesh in sc_meshes:
			total_missing_slots = self.total - len(mesh.bl_mesh.materials)
			for _ in range(total_missing_slots):
				mesh.bl_mesh.materials.append(None)
			result_sc_meshes.append(mesh)
		return result_sc_meshes


# class Mesh_Generate_Buffer2D(bpy.types.Node, Node, SC_Meshes_Getter_Node):
# 	bl_idname = 'sc_node_nfobyxya9qqc1pub5osj'
# 	bl_label = 'Create mesh 2d buffer'
# 	buffer_distance: bpy.props.FloatProperty(
# 		name='Distance',
# 		default=1,
# 		description='Units are in mesh space. Negative distances are allowed but have no effects on lines and points')
# 	buffer_resolution: bpy.props.IntProperty(
# 		name='Curves resolution',
# 		min=1,
# 		default=3,
# 		max=100,
# 		description='When the buffer has curved parts, this controls their resolution. Higher = more detailed and precise curves')
# 	cap_style: bpy.props.EnumProperty(
# 		name='Dead ends style',
# 		description='Shape of the buffers at edges extremities and around isolated vertices',
# 		items=[
# 			(str(shapely.geometry.CAP_STYLE.flat), 'Flat', ''),
# 			(str(shapely.geometry.CAP_STYLE.round), 'Round', ''),
# 			(str(shapely.geometry.CAP_STYLE.square), 'Square', ''),
# 		])
# 	join_style: bpy.props.EnumProperty(
# 		name='Join style',
# 		description='Shape of the buffers when edges meet each other and at polygons corners',
# 		items=[
# 			(str(shapely.geometry.JOIN_STYLE.mitre), 'Pointy', ''),
# 			(str(shapely.geometry.JOIN_STYLE.round), 'Round', ''),
# 			(str(shapely.geometry.JOIN_STYLE.bevel), 'Bevel', ''),
# 		])
# 	join_style_pointiness_distance: bpy.props.FloatProperty(
# 		name='Pointiness max distance',
# 		default=2,
# 		description="Control how far at the maximum pointy corners can go. Must be GREATER OR EQUAL to the buffer distance!")
# 	should_use_z: bpy.props.BoolProperty(
# 		name='Use Z',
# 		default=True,
# 		description="The result buffer is by nature computed in 2d, but will try to follow the input mesh on the z axis. Works best with edges-only meshes, if the input mesh has faces and volumes, the result will look odd. Makes the computation heavier")
# 	should_simplify_result: bpy.props.BoolProperty(
# 		name='Simplify output mesh',
# 		default=False,
# 		description="Close edges in the output mesh will be merged, while trying to maintain the result shape as much as possible")
# 	simplify_distance: bpy.props.FloatProperty(
# 		name='Simplification distance',
# 		default=.01,
# 		description="Verticess closer to that distance will be merged together, if they don't contribute too much to the shape of the result mesh")
# 	should_remove_not_overlapping_faces: bpy.props.BoolProperty(
# 		name='Remove non-overlapping faces',
# 		default=False,
# 		description="Some parts of the buffer may give incorrect faces that don't overlap with the input mesh. This option greatly reduces unwanted artifact faces. Makes the computation slower!	")
#
# 	def sc_init(self, context):
# 		self.create_input(SOCKET_Meshes, is_required=True)
# 		self.create_output(SOCKET_Meshes, is_new_data_output=True)
#
# 	def sc_draw_buttons(self, context, layout):
# 		row = layout.row()
# 		row.prop(self, 'buffer_distance')
# 		row.prop(self, 'buffer_resolution')
# 		layout.prop(self, 'cap_style')
# 		layout.prop(self, 'join_style')
# 		if self.join_style == str(shapely.geometry.JOIN_STYLE.mitre):
# 			layout.prop(self, 'join_style_pointiness_distance')
# 		layout.prop(self, 'should_use_z')
# 		layout.prop(self, 'should_simplify_result')
# 		if self.should_simplify_result:
# 			layout.prop(self, 'simplify_distance')
# 		layout.prop(self, 'should_remove_not_overlapping_faces')
#
# 	# def _get_sc_meshes_necessary_data(self, *args, **kwargs):
# 	# 	input: SC_Meshes_Getter_Node = self.inputs[0].links[0].from_node
# 	# 	return input.get_sc_meshes()
#
# 	@Node.get_data_first
# 	def get_sc_meshes(self, sc_meshes: List[DATA_Mesh], *args, **kwargs):
# 		result_sc_meshes = []
# 		for sc_mesh in sc_meshes:
# 			# bl_mesh = bpy.data.meshes.new('MeshGenerate2dBuffer')
# 			input_bm = bmesh.new()
# 			input_bm.from_mesh(sc_mesh.bl_mesh)
#
# 			geoms = utils.sc_mesh_to_2d_geoms(sc_mesh, bm=input_bm)
# 			all_lines = []
# 			# all_polygons = []
# 			geoms_to_buffer = []
# 			for shapely_geom in geoms.shapely_geoms:
# 				if isinstance(shapely_geom, shapely.geometry.LineString):
# 					all_lines.append(shapely_geom)
# 				# print(shapely_geom.has_z)
# 				elif isinstance(shapely_geom, shapely.geometry.MultiLineString):
# 					for line in shapely_geom.geoms:
# 						all_lines.append(line)
# 				# elif isinstance(shapely_geom, shapely.geometry.Polygon) or isinstance(shapely_geom, shapely.geometry.MultiPolygon):
# 				# 	all_polygons.append(shapely_geom)
# 				else:
# 					geoms_to_buffer.append(shapely_geom)
# 			all_lines_multi_linestring: shapely.geometry.MultiLineString = shapely.ops.linemerge(all_lines)
# 			# all_polygons_multipolygon: shapely.geometry.MultiPolygon = shapely.geometry.MultiPolygon(all_polygons)
# 			geoms_to_buffer.append(all_lines_multi_linestring)
# 			# geoms_to_buffer.extend(all_lines)
# 			ready_geoms = shapely.geometry.GeometryCollection(geoms_to_buffer)
# 			newGeoms = ready_geoms.buffer(self.buffer_distance, resolution=self.buffer_resolution, cap_style=int(self.cap_style),
# 				join_style=int(self.join_style),
# 				mitre_limit=int(self.join_style_pointiness_distance))
# 			if self.should_simplify_result:
# 				newGeoms = newGeoms.simplify(self.simplify_distance, preserve_topology=True)
# 			if isinstance(newGeoms, shapely.geometry.GeometryCollection):
# 				result = DATA_Geometries.from_geom_collection(newGeoms)
# 			else:
# 				result = DATA_Geometries()
# 				result.shapely_geoms.append(newGeoms)
# 			result_sc_mesh = utils.geoms_to_sc_mesh(result)
#
# 			if self.should_remove_not_overlapping_faces or self.should_use_z:
# 				result_bm = bmesh.new()
# 				result_bm.from_mesh(result_sc_mesh.bl_mesh)
#
# 				if self.should_remove_not_overlapping_faces:
# 					geom_collection = geoms.get_shapely_geom_collection()
# 					for f in result_bm.faces:
# 						coords = []
# 						for v in f.verts:
# 							coords.append(tuple(v.co))
# 						face_polygon = shapely.geometry.Polygon(coords)
# 						# print(face_polygon)
# 						if not face_polygon.intersects(geom_collection):
# 							# result_bm.faces.remove(f)
# 							for v in f.verts:
# 								if len(v.link_faces) <= 1:
# 									result_bm.verts.remove(v)
#
# 				if self.should_use_z:
# 					coord_to_vert = {}
# 					geoms_points = DATA_Geometries()
# 					# result_bm = bmesh.new()
# 					# result_bm.from_mesh(result_sc_mesh.bl_mesh)
# 					for v in input_bm.verts:
# 						coord_to_vert[(v.co[0], v.co[1])] = v
# 						geoms_points.shapely_geoms.append(shapely.geometry.Point(tuple(v.co)))
#
# 					points_geom_collection = geoms_points.get_shapely_geom_collection()
# 					for v in result_bm.verts:
# 						point1, point2 = shapely.ops.nearest_points(shapely.geometry.Point(tuple(v.co)), points_geom_collection)
# 						vert = coord_to_vert[(point2.x, point2.y)]
# 						v.co[2] = vert.co[2]
# 					result_bm.to_mesh(result_sc_mesh.bl_mesh)
# 				result_bm.free()
# 			input_bm.free()
# 			result_sc_meshes.append(result_sc_mesh)
# 		return result_sc_meshes


class Mesh_Edges_From_Curves(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_jfx5qu6s2csmzgc25w1f'
	bl_label = 'Bezier curves to mesh edges'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	distance_between_points: bpy.props.FloatProperty(name='Distance between points', default=1, description='The curves will be sampled every x distance',
		min=.01)

	def sc_init(self, context):
		self.create_input(SC_Curves_Socket, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=True)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'distance_between_points')

	# def _get_sc_meshes_necessary_data(self, *args, **kwargs):
	# 	input: SC_Curves_Getter_Node = self.inputs[0].links[0].from_node
	# 	return input.get_sc_curves()

	@Node.get_data_first
	def get_sc_meshes(self, sc_curves: List[SC_Curve], *args, **kwargs):
		result_sc_meshes: List[DATA_Mesh] = []
		for sc_curve in sc_curves:
			# geoms = DATA_Geometries()
			bl_mesh = bpy.data.meshes.new('MeshGenerateEdgesFromCurves')
			bm = bmesh.new()
			sc_mesh = DATA_Mesh(bl_mesh)

			for spline in sc_curve.bl_curve.splines:
				previous_bezier_point = spline.bezier_points[0]
				# line_string_points_pos = [tuple(previous_bezier_point.co)]
				verts = [bm.verts.new(previous_bezier_point.co)]
				spline_bezier_points = list(spline.bezier_points)
				if spline.use_cyclic_u:
					spline_bezier_points.append(spline_bezier_points[0])
				# for i in range(1, len(spline.bezier_points)):
				for i in range(1, len(spline_bezier_points)):
					# bezier_point = spline.bezier_points[i]
					bezier_point = spline_bezier_points[i]
					# estimate spline segment with length with a fixed resolution
					points_coord_vects = mathutils.geometry.interpolate_bezier(
						previous_bezier_point.co, previous_bezier_point.handle_right,
						bezier_point.handle_left, bezier_point.co,
						30)
					linestring = shapely.geometry.LineString(map(tuple, points_coord_vects))

					# divide spline segment length by distance to get number of points
					total_points = max(2, int(math.ceil(linestring.length / self.distance_between_points)))
					# evaluate spline segment a second time with correct number of points this time
					points_coord_vects = mathutils.geometry.interpolate_bezier(
						previous_bezier_point.co, previous_bezier_point.handle_right,
						bezier_point.handle_left, bezier_point.co,
						total_points)
					# add points to the final line string
					# line_string_points_pos.extend(map(tuple, points_coord_vects[1:]))
					for co in points_coord_vects[1:]:
						verts.append(bm.verts.new(co))

					previous_bezier_point = bezier_point
				for i in range(1, len(verts)):
					# print('OKKKK', verts[i - 1], verts[i])
					bm.edges.new((verts[i - 1], verts[i]))
			# geoms.shapely_geoms.append(shapely.geometry.LineString(line_string_points_pos))
			bm.to_mesh(bl_mesh)
			result_sc_meshes.append(sc_mesh)
		return result_sc_meshes


class GeometriesToMeshNode(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_61wbnimfbn6oawafqv31'
	bl_label = '2D Geometries sets to meshes'
	# icon_value = 'MESH_3D'
	icon_scale = 5

	def sc_init(self, context):
		self.create_input(SOCKET_Geoms2D, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=True)

	def _get_sc_meshes_necessary_data(self, *args, **kwargs):
		input_socket: SOCKET_Geoms2D = self.inputs[0]
		return input_socket.get_input_2d_geometries()

	@Node.get_data_first
	def get_sc_meshes(self, geoms: List[DATA_Geometries], *args, **kwargs):
		# raise Exception
		return utils.geoms_to_sc_mesh(geoms)


class Mesh_Generate_DisconnectIntersections(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_pisrr9se0amvwnf7ph2h'
	bl_label = 'Disconnect mesh intersections'
	# icon_value = 'MESH_3D'
	icon_scale = 5
	should_use_angles: bpy.props.BoolProperty(name='Use angles at intersections', default=True,
		description="Where 3 or more edges meet at a vertex, keep the edges that make the largest angles (=closely aligned) connected, "
					"and disconnect those that form sharp angles")
	sharp_angle_limit_radians: bpy.props.FloatProperty(name='Sharp angle limit',
		description='Edges that form angles below this angle will be disconnected (only works at intersections)', default=math.pi / 2,
		subtype='ANGLE', min=0, max=math.pi)

	def sc_init(self, context):
		self.create_input(SOCKET_Meshes, is_required=True)
		self.create_output(SOCKET_Meshes, is_new_data_output=True)

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'should_use_angles')
		if self.should_use_angles:
			layout.prop(self, 'sharp_angle_limit_radians')

	@Node.get_data_first
	def get_sc_meshes(self, sc_meshes: List[DATA_Mesh], *args, **kwargs):
		result_sc_meshes = []
		for sc_mesh in sc_meshes:
			bm = bmesh.new()
			bm.from_mesh(sc_mesh.bl_mesh)
			sharp_angle_limit_radians = self.sharp_angle_limit_radians
			treated_verts = list(bm.verts)

			# remove all faces
			for face in bm.faces:
				bm.faces.remove(face)

			for v in bm.verts:
				if len(v.link_edges) > 2:
					# compute and store angles for all pairs of edges
					edge_pairs_angles = []
					for edge1 in v.link_edges:
						for edge2 in v.link_edges:
							if edge1 == edge2:
								continue
							vector_edge1 = edge1.other_vert(v).co - v.co
							vector_edge2 = edge2.other_vert(v).co - v.co
							angle_vect1_to_vect2 = vector_edge1.angle(vector_edge2)
							# print(angle_vect1_to_vect2)
							# keep only valid angles
							if self.should_use_angles and angle_vect1_to_vect2 >= sharp_angle_limit_radians:
								edge_pairs_angles.append((edge1, edge2, angle_vect1_to_vect2, edge1.other_vert(v), edge2.other_vert(v)))
					# put best largest angles last, for popping later
					edge_pairs_angles.sort(key=lambda tuple: tuple[2], reverse=False)
					# remove unecessary pairs
					pairs_to_keep = []
					edges_to_restore = set(v.link_edges)
					while len(edge_pairs_angles) > 0:
						best_edges_pair = edge_pairs_angles.pop()
						pairs_to_keep.append(best_edges_pair)
						edge0 = best_edges_pair[0]
						edge1 = best_edges_pair[1]
						angle = best_edges_pair[2]
						edges_to_restore.remove(edge0)
						edges_to_restore.remove(edge1)
						pairs_to_remove = []
						for pair in edge_pairs_angles:
							if edge0 in pair or edge1 in pair:
								pairs_to_remove.append(pair)
						for pair in pairs_to_remove:
							edge_pairs_angles.remove(pair)

					# save data before deletiion
					v_co = v.co
					lone_edges_verts = []
					for edge in edges_to_restore:
						lone_edges_verts.append(edge.other_vert(v))
					# delete vert and all associated edges + faces
					bm.verts.remove(v)

					treated_verts.remove(v)

					# reconnect best pairs together
					while len(pairs_to_keep) > 0:
						best_edges_pair = pairs_to_keep.pop()
						vert0 = best_edges_pair[3]
						vert1 = best_edges_pair[4]
						newVert = bm.verts.new(v_co)
						bm.edges.new((newVert, vert0))
						bm.edges.new((newVert, vert1))
					# reconstruct lone dead-end edges
					for lone_edge_vert in lone_edges_verts:
						newVert = bm.verts.new(v_co)
						bm.edges.new((newVert, lone_edge_vert))
			# else:
			# 	print(len(v.link_edges), len(v.link_faces))
			# s'il reste des vertices non traités, c'est qu'il y a des boucles isolées

			# bl_mesh = bpy.data.meshes.new(mesh.bl_mesh.name)
			bl_mesh = bpy.data.meshes.new(sc_mesh.bl_mesh.name + '(isolate edges)')
			bm.to_mesh(bl_mesh)
			bm.free()
			sc_mesh = DATA_Mesh(bl_mesh)
			result_sc_meshes.append(sc_mesh)
		return result_sc_meshes


class Get_Mesh(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_x7lau8r62f8si9mif35t'
	bl_label = 'Get mesh'
	# icon = 'MESH_DATA'
	# icon_value= 'GET_MESH_DATA'
	icon_scale = 4
	mesh: bpy.props.PointerProperty(name='Mesh', type=bpy.types.Mesh, update=Node.prop_updated)
	at_least_one_input_socket_required = False

	def sc_init(self, context):
		self.create_output(SOCKET_Meshes, is_new_data_output=False)

	def are_all_inputs_correct(self):
		return self.mesh != None

	def sc_draw_buttons(self, context, layout):
		layout.prop(self, 'mesh', text='')

	# layout.label(text='hey', icon_value=my_globals.icônes['tick'].icon_id)

	def _get_sc_meshes_necessary_data(self, *args, **kwargs):
		pass

	@Node.get_data_first
	def get_sc_meshes(self, *args, **kwargs):
		if not self.mesh:
			self.print('No mesh specified')
			raise ValueError
		else:
			sc_mesh = DATA_Mesh(self.mesh)
			self.print(f'Mesh "{self.mesh.name}" found')
		# print(dir(bpy.context))
		# prev_mode = bpy.context.object.mode
		# prev_mode = bpy.context.mode
		utils.definir_blender_mode('OBJECT')
		# utils.definir_blender_mode(prev_mode)
		self.mesh.update()
		return [sc_mesh]


class ProceduralCubeMeshNode(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_x8ib99s96soyg58yng37'
	bl_label = 'Procedural cube mesh'
	at_least_one_input_socket_required = False

	bottom_face: bpy.props.BoolProperty(
		name='Create bottom face',
		description='Create or not the bottom face',
		default=True)

	height_below_center: bpy.props.FloatProperty(
		name='Height below origin',
		description='Add some padding below the center point on Z. The center point is centered around XY, and at the base of the cube on Z',
		default=0)

	uv_scale: bpy.props.FloatProperty(
		name='UV scale',
		description='UVs will be scaled by this value. Unwrap modes may restrict the scale of the UVs',
		default=.05,
		precision=5)

	# u_translation = bpy.props.FloatProperty(
	# 	name='U translation',
	# 	description='U will be translated by this value. Unwrap modes may restrict the translation',
	# 	default=0,
	# 	precision=6)
	#
	# v_translation = bpy.props.FloatProperty(
	# 	name='V translation',
	# 	description='U will be translated by this value. Unwrap modes may restrict the translation',
	# 	default=0,
	# 	precision=6)

	should_randomize_uv_pos: bpy.props.BoolProperty(
		name='Random UV translation',
		# description='For the UV modes that allow it, the faces will have their bottom left UVs starting at the same random location. '
		description='(Depends on the selected mode for facade UVs) The cube faces will have their bottom left UVs starting at the same random location. '
					'The random seed will depend on the nodes that use this procedural cube',
		default=False)

	faces_mat_indices: bpy.props.IntVectorProperty(
		name='Face material indices',
		description='Material index for each face of the cube, in this order: -y, +x, +y, -x, top, bottom (if enabled)',
		default=(0, 0, 0, 0, 0, 0),
		size=6,
		min=0
	)

	# facades_u_mode = bpy.props.EnumProperty(
	# 	name='Façades U mode',
	# 	description='How to UV unwrap on the U axis, for the façade faces. First option = U on all facade faces will be from 0 to 1, independently of the face'
	# 				' size. Option 2 = same but U will scale with the face, only in increments on 1 (1,2,3,4...). Option 3 = U will scale just like the face '
	# 				'(1 unit in UV space = 1 unit in object space)',
	# 	items=[
	# 		('mode1', 'Face size + scaling + translation', '', 1),
	# 		('mode2', '0 to 1, scaling inc. of 1, no translation', '', 2),
	# 		('mode3', '0 to 1, no scaling, no translation', '', 3),
	# 	])
	#
	# facades_v_mode = bpy.props.EnumProperty(
	# 	name='Façades V mode',
	# 	description='How to UV unwrap on the V axis, for the façade faces. First option = V on all facade faces will be from 0 to 1, independently of the face'
	# 				' size. Option 2 = same but V will scale with the face, only in increments on 1 (1,2,3,4...). Option 3 = V will scale just like the face '
	# 				'(1 unit in UV space = 1 unit in object space)',
	# 	items=[
	# 		('mode1', 'Face size + scaling + translation', '', 1),
	# 		('mode2', '0 to 1, scaling inc. of 1, no translation', '', 2),
	# 		('mode3', '0 to 1, no scaling, no translation', '', 3),
	# 	])

	facades_uv_mode: bpy.props.EnumProperty(
		name='Façades UV mode',
		description='How to UV unwrap the façade faces. '
					'Option 1 = UVs will scale with the size of the face (multiplied by UV scale) and random translation is allowed. '
					'Option 2 = UVs will scale with the size of the face but in increments on 1 (1,2,3,4...). '
					'Option 3 = UVs on all facade faces will be from 0 to 1, independently of the face size'
		,
		items=[
			('mode1', 'UVs have exact face size, rand. translation allowed', '', 1),
			('mode2', 'UVs scale in inc. of 1, no translation allowed', '', 2),
			('mode3', 'UVs 0 to 1, no scaling, no translation allowed', '', 3),
		])

	# top_bottom_uv_mode = bpy.props.EnumProperty(
	# 	name='Top/bottom UV mode',
	# 	description='How to UV unwrap the top and bottom faces. First option = UV will be from 0 to 1, independently of the face'
	# 				' size. Option 2 = same but UV will scale with the face, only in increments on 1 (1,2,3,4...). Option 3 = UV will scale just like the face '
	# 				'(1 unit in UV space = 1 unit in object space)',
	# 	items=[
	# 		('mode1', '0 to 1, no scaling, no translation', '', 1),
	# 		('mode2', '0 to 1, scaling inc. of 1, no translation', '', 2),
	# 		('mode3', 'Face size + scaling + translation', '', 3),
	# 	])

	# should_align_facades_u = bpy.props.BoolProperty(
	# 	name='Align façades on U',
	# 	description='Side faces will have contiguous U',
	# 	default=True)

	def sc_init(self, context):
		self.width = 350
		# self.outputs.new('SOCKET_Meshes', 'DATA_Mesh data')
		self.create_output(SOCKET_Meshes)

	def sc_draw_buttons(self, context, layout):
		# self.ui_display_doc2(layout)
		layout.prop(self, 'bottom_face')
		layout.prop(self, 'height_below_center')
		# layout.label(text='UVs')
		layout.prop(self, 'uv_scale')
		layout.prop(self, 'facades_uv_mode')
		# if self.facades_u_mode == 'mode3':
		# 	layout.prop(self, 'should_align_facades_u')
		# layout.prop(self, 'facades_v_mode')
		# layout.prop(self, 'top_bottom_uv_mode')
		# if self.facades_u_mode in ['mode2', 'mode3'] or self.facades_v_mode in ['mode2', 'mode3'] or self.top_bottom_uv_mode in ['mode2', 'mode3']:
		# 	layout.prop(self, 'uv_scale')
		# if self.facades_u_mode == 'mode3' or self.facades_v_mode == 'mode3' or self.top_bottom_uv_mode == 'mode3':
		layout.prop(self, 'should_randomize_uv_pos')
		# 	row = layout.row()
		# 	row.enabled = not self.should_randomize_uv_pos
		# 	row.prop(self, 'u_translation')
		# 	row.prop(self, 'v_translation')
		layout.label(text='Faces material indices')
		layout.prop(self, 'faces_mat_indices', text='')

	def is_dynamic_mesh(self):
		return True

	def is_mesh_param_supported(self, paramName):
		# return paramName in ['location', 'size']
		return paramName in ['size']

	def get_data(self, *args, **kwargs):
		return self.get_sc_meshes()

	def _get_sc_meshes_necessary_data(self, *args, **kwargs):
		pass

	@Node.get_data_first
	def get_sc_meshes(self, *args, **kwargs):
		mesh_data = DATA_Mesh(None)
		# try:
		# 	pos = kwargs['location']
		# except:
		# 	pos = (0, 0, 0)
		pos = (0, 0, 0)
		try:
			size = kwargs['size']
		except:
			size = (1, 1, 1)
		self.create_cube(pos, size, mesh_data.verts, mesh_data.faces, mesh_data.faces_material_indices, mesh_data.uvs_per_face_layers['generated main'])
		return mesh_data

	def create_cube(self, pos, size, verts, faces, material_indices, uvs_per_face):
		size_x = size[0]
		size_y = size[1]
		height = size[2]
		half_size_x = size_x / 2
		half_size_y = size_y / 2
		pos_x = pos[0]
		pos_y = pos[1]
		pos_z = pos[2]

		total_verts = len(verts)
		# Faces -------------------------------------------------------------------------------------
		faces.extend([
			(total_verts + 0, total_verts + 1, total_verts + 2, total_verts + 3),  # -y
			(total_verts + 1, total_verts + 6, total_verts + 5, total_verts + 2),  # +x
			(total_verts + 6, total_verts + 7, total_verts + 4, total_verts + 5),  # +y
			(total_verts + 7, total_verts + 0, total_verts + 3, total_verts + 4),  # -x
			(total_verts + 2, total_verts + 5, total_verts + 4, total_verts + 3),  # top face
		])
		# Material indices -------------------------------------------------------------------------------------
		material_indices.extend([
			self.faces_mat_indices[0],
			self.faces_mat_indices[1],
			self.faces_mat_indices[2],
			self.faces_mat_indices[3],
			self.faces_mat_indices[4],
		])
		# Verts UVs -------------------------------------------------------------------------------------
		if self.should_randomize_uv_pos:
			uv_pos_x = random.uniform(-.5, .5)
			uv_pos_y = random.uniform(-.5, .5)
		else:
			uv_pos_x = 0
			uv_pos_y = 0
		uv_scale = self.uv_scale

		if self.facades_uv_mode == 'mode1':
			uvs_per_face.extend([
				((uv_pos_x + 0, uv_pos_y + 0),
				 (uv_pos_x + size_x * uv_scale, uv_pos_y + 0),
				 (uv_pos_x + size_x * uv_scale, uv_pos_y + height * uv_scale),
				 (uv_pos_x + 0, uv_pos_y + height * uv_scale)),
				((uv_pos_x + 0, uv_pos_y + 0),
				 (uv_pos_x + size_y * uv_scale, uv_pos_y + 0),
				 (uv_pos_x + size_y * uv_scale, uv_pos_y + height * uv_scale),
				 (uv_pos_x + 0, uv_pos_y + height * uv_scale)),
				((uv_pos_x + 0, uv_pos_y + 0),
				 (uv_pos_x + size_x * uv_scale, uv_pos_y + 0),
				 (uv_pos_x + size_x * uv_scale, uv_pos_y + height * uv_scale),
				 (uv_pos_x + 0, uv_pos_y + height * uv_scale)),
				((uv_pos_x + 0, uv_pos_y + 0),
				 (uv_pos_x + size_y * uv_scale, uv_pos_y + 0),
				 (uv_pos_x + size_y * uv_scale, uv_pos_y + height * uv_scale),
				 (uv_pos_x + 0, uv_pos_y + height * uv_scale)),
			])
		elif self.facades_uv_mode == 'mode2':
			uvs_per_face.extend([
				((0, 0),
				 (max(1, math.ceil(size_x * uv_scale)), 0),
				 (max(1, math.ceil(size_x * uv_scale)), max(1, math.ceil(height * uv_scale))),
				 (0, max(1, math.ceil(height * uv_scale)))),
				((0, 0),
				 (max(1, math.ceil(size_y * uv_scale)), 0),
				 (max(1, math.ceil(size_y * uv_scale)), max(1, math.ceil(height * uv_scale))),
				 (0, max(1, math.ceil(height * uv_scale)))),
				((0, 0),
				 (max(1, math.ceil(size_x * uv_scale)), 0),
				 (max(1, math.ceil(size_x * uv_scale)), max(1, math.ceil(height * uv_scale))),
				 (0, max(1, math.ceil(height * uv_scale)))),
				((0, 0),
				 (max(1, math.ceil(size_y * uv_scale)), 0),
				 (max(1, math.ceil(size_y * uv_scale)), max(1, math.ceil(height * uv_scale))),
				 (0, max(1, math.ceil(height * uv_scale)))),
			])
		elif self.facades_uv_mode == 'mode3':
			uvs_per_face.extend([
				((0, 0),
				 (1, 0),
				 (1, 1),
				 (0, 1)),
				((0, 0),
				 (1, 0),
				 (1, 1),
				 (0, 1)),
				((0, 0),
				 (1, 0),
				 (1, 1),
				 (0, 1)),
				((0, 0),
				 (1, 0),
				 (1, 1),
				 (0, 1)),
			])
		uvs_per_face.append((
			(uv_pos_x + 0, uv_pos_y + 0),
			(uv_pos_x + size_y * uv_scale, uv_pos_y + 0),
			(uv_pos_x + size_y * uv_scale, uv_pos_y + size_x * uv_scale),
			(uv_pos_x + 0, uv_pos_y + size_x * uv_scale)
		))
		# Verts pos -------------------------------------------------------------------------------------
		# vert 0 = -x, -y, 0
		# vert 1 = +x, -y, 0
		# vert 2 = +x, -y, +z
		# vert 3 = -x, -y, +z
		# vert 4 = -x, +y, +z
		# vert 5 = +x, +y, +z
		# vert 6 = +x, +y, 0
		# vert 7 = -x, +y, 0
		verts.extend([
			(pos_x - half_size_x, pos_y - half_size_y, pos_z - self.height_below_center),
			(pos_x + half_size_x, pos_y - half_size_y, pos_z - self.height_below_center),
			(pos_x + half_size_x, pos_y - half_size_y, pos_z + height),
			(pos_x - half_size_x, pos_y - half_size_y, pos_z + height),
			(pos_x - half_size_x, pos_y + half_size_y, pos_z + height),
			(pos_x + half_size_x, pos_y + half_size_y, pos_z + height),
			(pos_x + half_size_x, pos_y + half_size_y, pos_z - self.height_below_center),
			(pos_x - half_size_x, pos_y + half_size_y, pos_z - self.height_below_center),
		])
		# Bottom face -------------------------------------------------------------------------------------
		if self.bottom_face:
			faces.append((total_verts + 6, total_verts + 1, total_verts + 0, total_verts + 7))
			material_indices.append(self.faces_mat_indices[5])
			uvs_per_face.append((
				(uv_pos_x + 0, uv_pos_y + 0),
				(uv_pos_x + size_y * uv_scale, uv_pos_y + 0),
				(uv_pos_x + size_y * uv_scale, uv_pos_y + size_x * uv_scale),
				(uv_pos_x + 0, uv_pos_y + size_x * uv_scale)
			))

class BlenderMeshToMeshDataNode(bpy.types.Node, Node, DATA_GETTER_NODE_SC_Meshes):
	bl_idname = 'sc_node_4onu51z8meeevirb2129'
	bl_label = 'Blender mesh to mesh data'
	at_least_one_input_socket_required = False

	mesh_name: bpy.props.StringProperty(
		name='Mesh name',
		description="")

	def sc_init(self, context):
		self.width = 300
		self.create_output(SOCKET_Meshes)

	def sc_draw_buttons(self, context, layout):
		# self.ui_display_doc2(layout)
		split = layout.split(factor=.95)
		split.prop(self, 'mesh_name', icon='OUTLINER_DATA_MESH')
		if self.mesh_name in bpy.data.meshes:
			# split.label(text='', icon='FILE_TICK')
			split.label(text='', icon_value=my_globals.icônes['tick'].icon_id)
		else:
			split.label(text='', icon='CANCEL')

	def _get_sc_meshes_necessary_data(self, *args, **kwargs):
		pass

	@Node.get_data_first
	def get_sc_meshes(self, *args, **kwargs):
		mesh_data = DATA_Mesh(None)
		mesh = bpy.data.meshes[self.mesh_name]
		for v in mesh.vertices:
			mesh_data.verts.append(tuple(v.co))
		for p in mesh.polygons:
			mesh_data.faces.append(tuple(p.vertices))
			mesh_data.faces_material_indices.append(p.material_index)
			face_uvs = []
			for loop_nb in p.loop_indices:
				face_uvs.append(mesh.uv_layers[0].data[loop_nb].uv)
			mesh_data.uvs_per_face_layers['generated main'].append(tuple(face_uvs))
		return mesh_data