from os import getcwd, path
from bpy.app.handlers import persistent
import math
import bpy
import random
from mathutils import Vector
from bpy.types import Panel, PropertyGroup, Operator, UIList
from bpy.props import (BoolProperty, EnumProperty, PointerProperty, IntProperty,
FloatProperty, CollectionProperty, StringProperty, FloatVectorProperty)
from . ui_panel import ILSPpanel
from . import utils
from . import laserBeam as lb
from . import userProcess as userProc
from . import illumination as illum
from . import robotInstructions as robot
from . imageSpot import ColorBar
########################################################################
# PROPERTIES
########################################################################
[docs]
class ProcessProperties(PropertyGroup):
[docs]
def setDefaultValues(self, context):
pass
[docs]
def check(self, context):
if self.selected_domain is None:
message = 'none iLSP domain selected ...'
iconValue = 3
return False, message, iconValue
return True
def _selectedDomainPoll(self, object):
return utils.getDomainByDomainObj(object)
selected_domain: PointerProperty(
name="Selected Domain",
description="Selected Domain",
type=bpy.types.Object,
poll=lambda self, object: self._selectedDomainPoll(object),
)
[docs]
def setPivotPointToSelected(self, context):
obj = self.selected_domain
cursor = bpy.context.scene.cursor.location
obj.location = cursor
obj.rotation_euler = (0.0, 0.0, 0.0)
bpy.ops.view3d.snap_cursor_to_selected()
cursor = bpy.context.scene.cursor.location
utils.selectOnlyObj(obj)
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
self.pivot_point = cursor
set_pivot_point_selected: BoolProperty(
name="Set Pivot Point To Selected",
description="Set Pivot Point To Selected",
update=lambda self, context: self.setPivotPointToSelected(context),
default=False,
) # type: ignore
[docs]
def setPivotPoint(self, context):
obj = self.selected_domain
cursor = bpy.context.scene.cursor.location
obj.location = cursor
obj.rotation_euler = (0.0, 0.0, 0.0)
cursor[0] = self.pivot_point[0]
cursor[1] = self.pivot_point[1]
cursor[2] = self.pivot_point[2]
utils.selectOnlyObj(obj)
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
pivot_point: FloatVectorProperty(
name="Pivot Point",
description="Pivot Point",
unit='LENGTH',
default=(0.0, 0.0, 0.0),
size=3,
update=lambda self, context: self.setPivotPoint(context),
) # type: ignore
[docs]
class UserProcess(PropertyGroup):
rx: FloatProperty(
name="rx",
description="Rotation Increment Around x-axis [deg]",
default=0.0,
step=10,
)
ry: FloatProperty(
name="ry",
description="Rotation Increment Around y-axis [deg]",
default=0.0,
step=10,
)
rz: FloatProperty(
name="rz",
description="Rotation Increment Around z-axis [deg]",
default=0.0,
step=10,
)
dx: FloatProperty(
name="dx",
description="Displacement Increment x-axis Direction",
default=0.0,
step=10,
)
dy: FloatProperty(
name="dy",
description="Displacement Increment y-axis Direction",
default=0.0,
step=10,
)
dz: FloatProperty(
name="dz",
description="Displacement Increment z-axis Direction",
default=0.0,
step=10,
)
velocity: FloatProperty(
name="v",
description="Velocity",
default=1.0,
step=10,
)
frequency: FloatProperty(
name="f",
description="Frequency [Hz]",
default=1.0,
step=10,
)
user_process: CollectionProperty(type=UserProcess)
user_process_idx: IntProperty(
name="Index of user process",
default=0,
min=0,
)
simulation_type: EnumProperty(
name='',
description="Simulation Type",
items=[('SUMMATION', 'SUMMATION', ''),
('MAXIMUM', 'MAXIMUM', '')]
)
[docs]
def readRobotInstructionFile(self, context):
if self.robot_file_path_import == '':
return
robotInstructions = robot.RobotInstructions(self.robot_file_path_import)
robotInstructions.importInstructions()
instructions = robotInstructions.instructions
user_process = context.scene.ilsp_process.user_process
user_process.clear()
for idx in range(0, len(instructions)):
item = user_process.add()
prevPosition = robot.Position()
if idx > 0:
prevPosition = instructions[idx - 1].position
item.dx = 0.0
item.dy = 0.0
item.dz = 0.0
item.rx = 0.0
item.ry = 0.0
item.rz = 0.0
item.velocity = 0.0
item.frequency = 0.0
instruction = instructions[idx]
if instruction.velocity > 0.0:
position = instruction.position
item.dx = position.x - prevPosition.x
item.dy = position.y - prevPosition.y
item.dz = position.z - prevPosition.z
item.rx = position.rx - prevPosition.rx
item.ry = position.ry - prevPosition.ry
item.rz = position.rz - prevPosition.rz
item.velocity = instruction.velocity
item.frequency = instruction.frequency
[docs]
def exportRobotInstructionFile(self, context):
if self.robot_file_path_export == '':
return
if path.exists(self.robot_file_path_export):
return
robotInstructions = robot.RobotInstructions(self.robot_file_path_export)
robotInstructions.exportInstructions(context.scene.ilsp_process.user_process)
# instructions = robot.RobotInstructions(self.robot_file_path_import).instructions
# user_process = context.scene.ilsp_process.user_process
# user_process.clear()
# for idx in range(0, len(instructions)):
# item = user_process.add()
# prevPosition = robot.Position()
# if idx > 0:
# prevPosition = instructions[idx - 1].position
# item.dx = 0.0
# item.dy = 0.0
# item.dz = 0.0
# item.rx = 0.0
# item.ry = 0.0
# item.rz = 0.0
# item.velocity = 0.0
# item.frequency = 0.0
# instruction = instructions[idx]
# if instruction.velocity > 0.0:
# position = instruction.position
# item.dx = position.x - prevPosition.x
# item.dy = position.y - prevPosition.y
# item.dz = position.z - prevPosition.z
# item.rx = position.rx - prevPosition.rx
# item.ry = position.ry - prevPosition.ry
# item.rz = position.rz - prevPosition.rz
# item.velocity = instruction.velocity
# item.frequency = instruction.frequency
robot_file_path_import: StringProperty(
name="",
description="Import Robot Instructions File Path",
default="",
maxlen=1024,
subtype='FILE_PATH',
update=lambda self, context: self.readRobotInstructionFile(context))
robot_file_path_export: StringProperty(
name="",
description="Export Robot Instructions File Path",
default="",
maxlen=1024,
subtype='FILE_PATH',
update=lambda self, context: self.exportRobotInstructionFile(context))
[docs]
def setMinTreshold(self, context):
if self.auto_tresholds:
if self.min_treshold < self.min_treshold_min:
self.min_treshold = self.min_treshold_min
min = self.min_treshold
max = self.max_treshold
if max < min:
self.max_treshold = min
domainObj = context.scene.ilsp_process.selected_domain
colorRamp = domainObj.data.materials[0].node_tree.nodes['Color Ramp']
multiply = domainObj.data.materials[0].node_tree.nodes['Multiply']
add = domainObj.data.materials[0].node_tree.nodes['Add']
a = 1.0 / (self.max_treshold_max - self.min_treshold_min)
b = self.min_treshold_min / (self.min_treshold_min - self.max_treshold_max)
multiply.inputs[1].default_value = a
add.inputs[1].default_value = b
unitMin = a * min + b
unitMax = a * max + b
colorRamp.color_ramp.elements[0].position = 0
colorRamp.color_ramp.elements[1].position = unitMin
colorRamp.color_ramp.elements[2].position = unitMin
colorRamp.color_ramp.elements[3].position = 0.5 * (unitMin + unitMax)
colorRamp.color_ramp.elements[4].position = unitMax
colorRamp.color_ramp.elements[5].position = unitMax
# colorBar = ColorBar()
# for i in range(1, len(colorRamp.color_ramp.elements) - 2):
# elem = colorRamp.color_ramp.elements[i]
# position = elem.position
# if position < min:
# c = (0.8, 0.8, 0.8, 1)
# else:
# try:
# c = colorBar._data[i - 1][1]
# except Exception:
# pass
# elem.color = c
min_treshold: FloatProperty(
name="minimal treshold",
description="minimal treshold",
default=0.0,
step=10,
update=lambda self, context: self.setMinTreshold(context),
)
min_treshold_min: FloatProperty(
name="minimal treshold min",
description="minimal treshold min",
default=0.0,
step=10
)
max_treshold: FloatProperty(
name="maximal treshold",
description="maximal treshold",
default=1.0,
step=10,
update=lambda self, context: self.setMaxTreshold(context),
)
max_treshold_max: FloatProperty(
name="maximal treshold max",
description="maximal treshold max",
default=1.0,
step=10
)
[docs]
def setTresholds(self, context):
self._updateSimulation(context)
self.setMinTreshold(context)
self.setMaxTreshold(context)
auto_tresholds: BoolProperty(
name="automatic tresholds",
description="automatic tresholds",
default=True,
update=lambda self, context: self.setTresholds(context),
)
[docs]
def setMaxTreshold(self, context):
if self.auto_tresholds:
if self.max_treshold > self.max_treshold_max:
self.max_treshold = self.max_treshold_max
min = self.min_treshold
max = self.max_treshold
if max < min:
self.min_treshold = max
domainObj = context.scene.ilsp_process.selected_domain
colorRamp = domainObj.data.materials[0].node_tree.nodes['Color Ramp']
a = 1.0 / (self.max_treshold_max - self.min_treshold_min)
b = self.min_treshold_min / (self.min_treshold_min - self.max_treshold_max)
unitMin = a * min + b
unitMax = a * max + b
colorRamp.color_ramp.elements[0].position = 0
colorRamp.color_ramp.elements[1].position = unitMin
colorRamp.color_ramp.elements[2].position = unitMin
colorRamp.color_ramp.elements[3].position = 0.5 * (unitMin + unitMax)
colorRamp.color_ramp.elements[4].position = unitMax
colorRamp.color_ramp.elements[5].position = unitMax
# for i in range(1, len(colorRamp.color_ramp.elements) - 2):
# elem = colorRamp.color_ramp.elements[i]
# position = elem.position
# if position > max:
# c = (0.8, 0.8, 0.8, 1)
# else:
# try:
# c = colorBar._data[i - 1][1]
# except Exception:
# pass
# elem.color = c
[docs]
class Material(PropertyGroup):
material: bpy.props.PointerProperty(type=bpy.types.Material)
materials: CollectionProperty(type=Material)
def _updateSimulation(self, context):
domainObj = self.selected_domain
if domainObj.data.materials:
domainObj.data.materials[0] = self.materials[self.material_idx].material
frame = bpy.context.scene.frame_current
domainObj = bpy.context.scene.ilsp_process.selected_domain
illumination = domainObj.data.materials[0]
illumination.node_tree.nodes["Attribute"].attribute_name =\
illumination.name + '_' + str(frame)
name = illumination.node_tree.nodes["Attribute"].attribute_name
min = float('inf')
max = -float('inf')
if self.auto_tresholds:
values = domainObj.data.attributes.get(name)
if values is not None:
for polygon in domainObj.data.polygons:
value = values.data[polygon.index].value
if value > max:
max = value
if value < min:
min = value
self.min_treshold_min = min
self.max_treshold_max = max
self.min_treshold = min
self.max_treshold = max
material_idx: IntProperty(
name="Index of material",
default=0,
min=0,
update=lambda self, context: self._updateSimulation(context)
)
start_line: IntProperty(
name="Start Line Number",
default=1,
min=1,
)
end_line: IntProperty(
name="End Line Number",
default=1,
min=1,
)
no_repeats: IntProperty(
name="Number of Repeats",
default=1,
min=1,
)
########################################################################
# PANELs
########################################################################
[docs]
class ILSP_PT_process(ILSPpanel, Panel):
bl_label = "Process Prescription"
[docs]
@classmethod
def poll(cls, context):
return utils.domainsExists()
def _enabled(self, context):
if context.scene.ilsp_process.selected_domain is None:
return False
else:
return True
[docs]
def draw(self, context):
layout = self.layout
layout.use_property_split = False
layout.use_property_decorate = False
process = context.scene.ilsp_process
enabled = self._enabled(context)
row = layout.row()
row.enabled = True
row.prop(process, "selected_domain", icon='OUTLINER_OB_MESH')
row = layout.row(align=True)
row.enabled = enabled
row.prop(process, 'pivot_point')
col = row.column()
col.prop(process, "set_pivot_point_selected", icon='RESTRICT_SELECT_OFF',
icon_only=True, emboss=False)
layout.separator()
layout.label(text="Robot Instructions:")
grid = layout.grid_flow(columns=2, align=True)
grid.prop(process, "robot_file_path_import", text="Import", icon='IMPORT')
grid.prop(process, "robot_file_path_export", text="Export", icon='EXPORT')
layout.template_list("ILSP_UL_user_process",
"The_List",
process, "user_process",
process, "user_process_idx")
layout.operator('ilsp_user_process.new_item', text='ADD LINE', icon='ADD')
grid = layout.grid_flow(columns=4, align=True)
grid.prop(process, 'start_line', text='Start Line')
grid.prop(process, 'end_line', text='End Line')
grid.prop(process, 'no_repeats', text='Repetitions')
grid.operator('ilsp_user_process.copy', text='ADD REPEATING BLOCK', icon='DECORATE_OVERRIDE')
grid = layout.grid_flow(columns=2, align=True)
layout.operator('ilsp_user_process.clear', text='CLEAR', icon='TRASH')
layout.separator()
row = layout.row()
row.enabled = enabled
row.prop(process, 'simulation_type')
row.operator("process.simulate",
text="Simulate Illumination")
layout.separator()
layout.label(text="Results")
domainObj = context.scene.ilsp_process.selected_domain
colorRamp = domainObj.data.materials[0].node_tree.nodes['Color Ramp']
layout.template_color_ramp(colorRamp, "color_ramp", expand=True)
grid = layout.grid_flow(columns=2, align=True)
grid.enabled = enabled
grid.prop(process, 'min_treshold', slider=True)
grid.prop(process, 'max_treshold', slider=True)
layout.prop(process, 'auto_tresholds')
domainObj = process.selected_domain
layout.template_list("ILSP_UL_materials",
"The_List",
process, "materials",
process, "material_idx")
status = process.check(context)
try:
len(status)
except Exception:
return
layout.label(text=status[1], icon_value=status[2])
########################################################################
# OPERATORS
########################################################################
[docs]
class ILSP_UL_user_process(UIList):
[docs]
def draw_item(self, context, layout, data, item, icon, active_data,
active_propname, index):
grid = layout.grid_flow(columns=10, align=True)
grid.label(text=str(index + 1))
grid.prop(item, 'dx')
grid.prop(item, 'dy')
grid.prop(item, 'dz')
grid.prop(item, 'rx')
grid.prop(item, 'ry')
grid.prop(item, 'rz')
grid.prop(item, 'velocity')
grid.prop(item, 'frequency')
op = grid.operator('ilsp_user_process.delete_item',
text='', icon='X')
op.index = index
[docs]
class ILSP_UL_materials(UIList):
[docs]
def draw_item(self, context, layout, data, item, icon, active_data,
active_propname, index):
layout.prop(item, "name", text="", emboss=False, icon_value=icon)
[docs]
class ILSP_OT_user_process_ADD(Operator):
bl_idname = "ilsp_user_process.new_item"
bl_label = "Add"
[docs]
def execute(self, context):
user_process = context.scene.ilsp_process.user_process
item = user_process.add()
if len(user_process) > 1:
item.dx = user_process[-2].dx
item.dy = user_process[-2].dy
item.dz = user_process[-2].dz
item.rx = user_process[-2].rx
item.ry = user_process[-2].ry
item.rz = user_process[-2].rz
item.velocity = user_process[-2].velocity
item.frequency = user_process[-2].frequency
return {'FINISHED'}
[docs]
class ILSP_OT_user_process_COPY(Operator):
bl_idname = "ilsp_user_process.copy"
bl_label = "DO"
[docs]
def execute(self, context):
process = context.scene.ilsp_process
user_process = process.user_process
startIdx = process.start_line - 1
endIdx = process.end_line - 1
noRepeats = process.no_repeats
for repeat in range(0, noRepeats):
for itemIdx in range(startIdx, endIdx + 1):
item = user_process.add()
item.dx = user_process[itemIdx].dx
item.dy = user_process[itemIdx].dy
item.dz = user_process[itemIdx].dz
item.rx = user_process[itemIdx].rx
item.ry = user_process[itemIdx].ry
item.rz = user_process[itemIdx].rz
item.velocity = user_process[itemIdx].velocity
item.frequency = user_process[itemIdx].frequency
return {'FINISHED'}
[docs]
class ILSP_OT_user_process_CLEAR(Operator):
bl_idname = "ilsp_user_process.clear"
bl_label = "Clear"
[docs]
def execute(self, context):
user_process = context.scene.ilsp_process.user_process
user_process.clear()
return {'FINISHED'}
[docs]
class ILSP_OT_user_process_DELETE(Operator):
bl_idname = "ilsp_user_process.delete_item"
bl_label = "Delete"
index: IntProperty()
[docs]
@classmethod
def poll(cls, context):
return True
[docs]
def execute(self, context):
context.scene.ilsp_process.user_process.remove(self.index)
return {'FINISHED'}
[docs]
class ILSP_OT_process_simulate(Operator):
bl_idname = "process.simulate"
bl_label = "3D-Print Info Volume"
bl_description = "Report the volume of the active mesh"
[docs]
@classmethod
def poll(cls, context):
status = context.scene.ilsp_process.check(context)
try:
len(status)
except Exception:
return True
return False
[docs]
def execute(self, context):
ui_laserSpot = context.scene.ilsp_spot
ui_laserBeam = context.scene.ilsp_beam
ui_process = context.scene.ilsp_process
ui_laserBeam.show_beam = False
ui_laserBeam.deleteLaserBeam(context)
domainObj = ui_process.selected_domain
process = userProc.UserProcess(
ilspDomain=domainObj, processSteps=ui_process.user_process)
laserBeam = lb.LaserBeam(laserSpot=ui_laserSpot,
laserBeam=ui_laserBeam)
utils.selectOnlyObj(domainObj)
illumination = illum.Illumination(process=process,
spaceProfile=laserBeam.spaceProfil)
min, max = illumination.simulate(laserBeam=laserBeam)
domainObj = context.scene.ilsp_process.selected_domain
ui_process.min_treshold = min
ui_process.min_treshold_min = min
ui_process.max_treshold = max
ui_process.max_treshold_max = max
ui_process.materials.clear()
idx = -1
for material in bpy.data.materials:
if domainObj.name in material.name:
item = ui_process.materials.add()
item.material = material
item.name = material.name.removeprefix(domainObj.name + '_illumination_')
item.name = 'Simulation ' + item.name
idx += 1
if idx <= 0:
idx = 0
ui_process.material_idx = idx
return {'FINISHED'}
[docs]
@persistent
def my_handler(scene, context):
frame = scene.frame_current
domainObj = scene.ilsp_process.selected_domain
illumination = domainObj.data.materials[0]
illumination.node_tree.nodes["Attribute"].attribute_name =\
illumination.name + '_' + str(frame)
scene.ilsp_process._updateSimulation(context)
bpy.app.handlers.frame_change_pre.append(my_handler)