Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add warnings for when you (I) do silly things #135

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 35 additions & 19 deletions scripts/addons/cam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def updateMaterial(self, context):
def updateOperation(self, context):
scene = context.scene
ao = scene.cam_operations[scene.cam_active_operation]
operationValid(self, context)

if ao.hide_all_others:
for _ao in scene.cam_operations:
Expand Down Expand Up @@ -350,41 +351,56 @@ class import_settings(bpy.types.PropertyGroup):
max_segment_size: FloatProperty(name="", description="Only Segments bigger then this value get subdivided",
default=0.001, min=0.0001, max=1.0, unit="LENGTH")


def operationValid(self, context):
o = self
o.changed = True
o.valid = True
invalidmsg = "Operation has no valid data input\n"
o.info.warnings = ""
o = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]
def isValid(o,context):
valid=True
if o.geometry_source == 'OBJECT':
if o.object_name not in bpy.data.objects:
o.valid = False
o.info.warnings = invalidmsg
valid = False
if o.geometry_source == 'COLLECTION':
if o.collection_name not in bpy.data.collections:
o.valid = False
o.info.warnings = invalidmsg
valid = False
elif len(bpy.data.collections[o.collection_name].objects) == 0:
o.valid = False
o.info.warnings = invalidmsg
valid = False

if o.geometry_source == 'IMAGE':
if o.source_image_name not in bpy.data.images:
o.valid = False
o.info.warnings = invalidmsg
valid = False
return valid

def operationValid(self, context):
scene=context.scene
o = scene.cam_operations[scene.cam_active_operation]
o.changed = True
o.valid = isValid(o,context)
invalidmsg = "Invalid source object for operation.\n"
if o.valid:
o.info.warnings = ""
else:
o.info.warnings = invalidmsg

if o.geometry_source == 'IMAGE':
o.optimisation.use_exact = False
o.update_offsetimage_tag = True
o.update_zbufferimage_tag = True
print('validity ')

def isChainValid(chain,context):
s = context.scene
if len(chain.operations)==0:
return (False,"")
for cho in chain.operations:
found_op = None
for so in s.cam_operations:
if so.name == cho.name:
found_op= so
if found_op == None:
return (False,f"Couldn't find operation {cho.name}")
if cam.isValid(found_op,context) is False:
return (False,f"Operation {found_op.name} is not valid")
return (True,"")

# print(o.valid)

def updateOperationValid(self, context):
operationValid(self, context)
updateOperation(self, context)


Expand Down Expand Up @@ -868,7 +884,7 @@ class camOperation(bpy.types.PropertyGroup):
update=updateRest)

# feeds
feedrate: FloatProperty(name="Feedrate", description="Feedrate", min=0.00005, max=50.0, default=1.0,
feedrate: FloatProperty(name="Feedrate", description="Feedrate in units per minute", min=0.00005, max=50.0, default=1.0,
precision=cam.constants.PRECISION, unit="LENGTH", update=updateChipload)
plunge_feedrate: FloatProperty(name="Plunge speed ", description="% of feedrate", min=0.1, max=100.0, default=50.0,
precision=1, subtype='PERCENTAGE', update=updateRest)
Expand Down
5 changes: 4 additions & 1 deletion scripts/addons/cam/chunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from shapely import geometry as sgeometry
from cam import polygon_utils_cam
from cam.simple import *
from cam.exception import CamException
import math


Expand Down Expand Up @@ -970,7 +971,6 @@ def restoreVisibility(o, storage):


def meshFromCurve(o, use_modifiers=False):
# print(o.name,o)
activate(o)
bpy.ops.object.duplicate()

Expand All @@ -980,6 +980,9 @@ def meshFromCurve(o, use_modifiers=False):

if co.type == 'FONT': # support for text objects is only and only here, just convert them to curves.
bpy.ops.object.convert(target='CURVE', keep_original=False)
elif co.type != 'CURVE': # curve must be a curve...
bpy.ops.object.delete() # delete temporary object
raise CamException("Source curve object must be of type CURVE")
co.data.dimensions = '3D'
co.data.bevel_depth = 0
co.data.extrude = 0
Expand Down
36 changes: 31 additions & 5 deletions scripts/addons/cam/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import shapely
import mathutils
import math
import textwrap
import traceback

import cam
Expand Down Expand Up @@ -194,7 +195,8 @@ async def _calc_path(operator,context):
print("Got path:",context)
except CamException as e:
traceback.print_tb(e.__traceback__)
operator.report({'ERROR'},str(e))
error_str="\n".join(textwrap.wrap(str(e),width=80))
operator.report({'ERROR'},error_str)
return {'FINISHED',False}
except AsyncCancelledException as e:
return {'CANCELLED',False}
Expand All @@ -215,9 +217,15 @@ class CalculatePath(bpy.types.Operator,AsyncOperatorMixin):
bl_idname = "object.calculate_cam_path"
bl_label = "Calculate CAM paths"
bl_options = {'REGISTER', 'UNDO','BLOCKING'}

# this property was actually ignored, so removing it in 0.3
# operation= StringProperty(name="Operation", description="Specify the operation to calculate",default='Operation')

@classmethod
def poll(cls,context):
s = context.scene
o = s.cam_operations[s.cam_active_operation]
if o is not None:
if cam.isValid(o,context):
return True
return False

async def execute_async(self, context):
(retval,success) = await _calc_path(self,context)
Expand Down Expand Up @@ -296,8 +304,14 @@ class PathsChain(bpy.types.Operator,AsyncOperatorMixin):
bl_label = "Calculate CAM paths in current chain and export chain gcode"
bl_options = {'REGISTER', 'UNDO','BLOCKING'}

@classmethod
def poll(cls, context):
s = context.scene
chain = s.cam_chains[s.cam_active_chain]
return cam.isChainValid(chain,context)[0]

async def execute_async(self, context):
s = bpy.context.scene
s = context.scene
bpy.ops.object.mode_set(mode='OBJECT') # force object mode
chain = s.cam_chains[s.cam_active_chain]
chainops = getChainOperations(chain)
Expand Down Expand Up @@ -327,6 +341,12 @@ class PathExportChain(bpy.types.Operator):
bl_label = "Export CAM paths in current chain as gcode"
bl_options = {'REGISTER', 'UNDO'}

@classmethod
def poll(cls, context):
s = context.scene
chain = s.cam_chains[s.cam_active_chain]
return cam.isChainValid(chain,context)[0]

def execute(self, context):
s = bpy.context.scene

Expand Down Expand Up @@ -400,6 +420,12 @@ class CAMSimulateChain(bpy.types.Operator, AsyncOperatorMixin):
bl_label = "CAM simulation"
bl_options = {'REGISTER', 'UNDO','BLOCKING'}

@classmethod
def poll(cls, context):
s = context.scene
chain = s.cam_chains[s.cam_active_chain]
return cam.isChainValid(chain,context)[0]

operation: StringProperty(name="Operation",
description="Specify the operation to calculate", default='Operation')

Expand Down
12 changes: 7 additions & 5 deletions scripts/addons/cam/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ async def curve(o):
pathSamples = []
utils.getOperationSources(o)
if not o.onlycurves:
o.info.warnings += 'at least one of assigned objects is not a curve\n'
raise CamException("All objects must be curves for this operation.")

for ob in o.objects:
pathSamples.extend(curveToChunks(ob)) # make the chunks from curve here
Expand Down Expand Up @@ -257,8 +257,7 @@ async def proj_curve(s, o):

from cam import chunk
if targetCurve.type != 'CURVE':
o.info.warnings += 'Projection target and source have to be curve objects!\n '
return
raise CamException('Projection target and source have to be curve objects!')

if 1:
extend_up = 0.1
Expand Down Expand Up @@ -610,8 +609,7 @@ async def medial_axis(o):
elif o.cutter_type == 'BALLNOSE':
maxdepth = - new_cutter_diameter / 2 - o.skin
else:
o.info.warnings += 'Only Ballnose, V-carve cutters\n are supported'
return
raise CamException("Only Ballnose and V-carve cutters are supported for meial axis.")
# remember resolutions of curves, to refine them,
# otherwise medial axis computation yields too many branches in curved parts
resolutions_before = []
Expand Down Expand Up @@ -771,6 +769,10 @@ def getLayers(operation, startdepth, enddepth):
"""returns a list of layers bounded by startdepth and enddepth
uses operation.stepdown to determine number of layers.
"""
if startdepth < enddepth:
raise CamException("Start depth is lower than end depth. "
"If you have set a custom depth end, it must be lower than depth start, "
"and should usually be negative. Set this in the CAM Operation Area panel.")
if operation.use_layers:
layers = []
n = math.ceil((startdepth - enddepth) / operation.stepdown)
Expand Down
18 changes: 11 additions & 7 deletions scripts/addons/cam/ui_panels/chains.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from bpy.types import UIList
from cam.ui_panels.buttons_panel import CAMButtonsPanel

import cam


class CAM_UL_operations(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
Expand Down Expand Up @@ -37,6 +39,7 @@ class CAM_CHAINS_Panel(CAMButtonsPanel, bpy.types.Panel):
bl_label = "CAM chains"
bl_idname = "WORLD_PT_CAM_CHAINS"
panel_interface_level = 1
always_show_panel = True

def draw(self, context):
layout = self.layout
Expand All @@ -63,13 +66,14 @@ def draw(self, context):
col.operator("scene.cam_chain_operation_down", icon='TRIA_DOWN', text="")

if not chain.computing:
if chain.valid:
pass
layout.operator("object.calculate_cam_paths_chain", text="Calculate chain paths & Export Gcode")
layout.operator("object.cam_export_paths_chain", text="Export chain gcode")
layout.operator("object.cam_simulate_chain", text="Simulate this chain")
else:
layout.label(text="chain invalid, can't compute")
layout.operator("object.calculate_cam_paths_chain", text="Calculate chain paths & Export Gcode")
layout.operator("object.cam_export_paths_chain", text="Export chain gcode")
layout.operator("object.cam_simulate_chain", text="Simulate this chain")

valid,reason=cam.isChainValid(chain,context)
if not valid:
layout.label(icon="ERROR",text=f"Can't compute chain - reason:\n")
layout.label(text=reason)
else:
layout.label(text='chain is currently computing')

Expand Down
8 changes: 5 additions & 3 deletions scripts/addons/cam/ui_panels/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel):
bl_label = "CAM info & warnings"
bl_idname = "WORLD_PT_CAM_INFO"
panel_interface_level = 0
always_show_panel = True

prop_level = {
'draw_blendercam_version': 0,
Expand Down Expand Up @@ -115,6 +116,7 @@ def draw(self, context):
self.context = context
self.draw_blendercam_version()
self.draw_opencamlib_version()
self.draw_op_warnings()
self.draw_op_time()
self.draw_op_money_cost()
if self.op:
self.draw_op_warnings()
self.draw_op_time()
self.draw_op_money_cost()
8 changes: 4 additions & 4 deletions scripts/addons/cam/ui_panels/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ def draw_calculate_path(self):
self.layout.label(text='!ERROR! COLLISION!')
self.layout.prop(self.op.movement, 'free_height')

if self.op.valid:
self.layout.operator("object.calculate_cam_path", text="Calculate path & export Gcode")
else:
self.layout.label(text="operation invalid, can't compute")
if not self.op.valid:
self.layout.label(text="Select a valid object to calculate the path.")
# will be disable if not valid
self.layout.operator("object.calculate_cam_path", text="Calculate path & export Gcode")

def draw_export_gcode(self):
if not self.has_correct_level(): return
Expand Down
2 changes: 2 additions & 0 deletions scripts/addons/cam/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ def getBounds(o):
o.max.z = o.source_image_offset.z
s = bpy.context.scene
m = s.cam_machine
# make sure this message only shows once and goes away once fixed
o.info.warnings.replace('Operation exceeds your machine limits\n','')
if o.max.x - o.min.x > m.working_area.x or o.max.y - o.min.y > m.working_area.y \
or o.max.z - o.min.z > m.working_area.z:
o.info.warnings += 'Operation exceeds your machine limits\n'
Expand Down