Porównaj commity

...

37 Commity

Autor SHA1 Wiadomość Data
Alain Pelletier 99ae721762
Merge pull request #236 from pppalain/master
may cleanup
2023-05-09 11:38:54 -03:00
Alain Pelletier 502a3bdf8c
Merge pull request #100 from migo1001/master
Reorganisation of Optimisation Properties
2023-04-30 10:58:25 -03:00
migo101 77b838ddd8 Properties Functions cleanup 2023-04-30 15:43:46 +02:00
migo101 40a4d2b7ba Fix object name 2023-04-30 15:31:15 +02:00
migo101 ff4a39666e Merged diff 2023-04-29 19:06:56 +02:00
migo101 f423a5ce0f Moved all optimisation properties to CAM_OPTIMISATION_Properties 2023-04-29 18:53:51 +02:00
migo1001 415df8eb74
Merge branch 'pppalain:master' into master 2023-04-29 16:45:51 +02:00
Alain Pelletier ac5a16c2b4
Merge pull request #99 from abosafia/master
clean eyedropper effects
2023-04-28 15:33:04 -03:00
abosafia 1438d529ae clean eyedropper effects 2023-04-28 17:09:57 +03:00
Alain Pelletier c85e156efd
Merge pull request #98 from abosafia/master
remove eye dropper
2023-04-28 08:28:21 -03:00
abosafia 0270f2195e remove eye dropper 2023-04-28 13:50:42 +03:00
migo101 f248e23f72 Moved Op duration to Info Panel; PEP8 cleanup 2023-04-28 11:30:38 +02:00
Alain Pelletier 4c71b8a224
Update __init__.py
typo fixed
2023-04-27 16:24:11 -03:00
Alain Pelletier 10816b3fc7
Merge pull request #97 from migo1001/master
Moved warnings and chipload to dedicated class in info panel
2023-04-26 10:56:54 -03:00
migo101 d986c210a5 Resinstated call to updateRest() 2023-04-26 15:39:06 +02:00
migo1001 fb3740fac7
Merge branch 'pppalain:master' into master 2023-04-26 15:17:17 +02:00
migo101 9781d0888a Moved warnings and chipload to prop class in info panel 2023-04-26 10:12:57 +02:00
Alain Pelletier 1c65e51481
Merge pull request #95 from abosafia/master
bug fix
2023-04-25 09:00:20 -03:00
abosafia ab06a37387 bug fix 2023-04-25 04:37:47 +02:00
Alain Pelletier 5ef337dc12
Merge pull request #93 from migo1001/master
Moved all material Properties and Operators to a specific class next to the panel
2023-04-24 13:04:58 -03:00
migo101 27b4ec6e43 Put the name of radius_around_model back 2023-04-24 17:43:48 +02:00
migo101 5998fffb5a Fixes based on pull request #92 2023-04-24 17:15:36 +02:00
migo101 46b3c9e6e8 Solved conflict in ops.py 2023-04-24 17:05:45 +02:00
migo101 272dbc6109 Moved all material Properties and Operators to a specific class along with the Material Panel; Created a constants package 2023-04-24 16:43:56 +02:00
Alain Pelletier 84856fdc11
Merge pull request #91 from migo1001/master
Cleaned up code in UI: material.py
2023-04-24 08:53:50 -03:00
Alain Pelletier f7c270e843
Merge pull request #92 from abosafia/master
Eyedropper for selecting objects source
2023-04-24 08:22:18 -03:00
abosafia ac653a84c3 Edropper for selecting objects source 2023-04-24 09:44:16 +02:00
migo101 89c123d572 - Cleanup of ui_panels/optimisation.py
- Moved opencamlib check to utils
2023-04-23 18:54:52 +02:00
migo1001 fdc6a7f8ea
Merge branch 'pppalain:master' into master 2023-04-20 20:02:35 +02:00
migo101 5cbb538fc2 Display material even if active operation is invalid 2023-04-20 19:58:47 +02:00
migo101 ec9c66a174 Code cleanup of material UI 2023-04-20 19:57:30 +02:00
Alain Pelletier 85766a0e26
Merge pull request #90 from migo1001/master
Cleanup and a fix
2023-04-20 08:41:12 -03:00
Alain Pelletier af77b220ea
Merge pull request #89 from Invader-Zim/master
Preserve length_unit in addMachineAreaObject
2023-04-20 08:34:18 -03:00
migo101 b18e0fdd3c Merge branch 'master' of https://github.com/migo1001/blendercam 2023-04-20 11:57:24 +02:00
migo101 0a1c4326f5 Code cleanup 2023-04-20 11:52:45 +02:00
migo101 fb2c2f227a Do not create operation if the object is missing or invalid 2023-04-20 11:50:59 +02:00
Stephen Rakonza 3f365bc4d9 Preserve length_unit in addMachineAreaObject 2023-04-19 16:58:53 -07:00
21 zmienionych plików z 653 dodań i 465 usunięć

Wyświetl plik

@ -49,7 +49,7 @@ from cam import ui, ops, curvecamtools, curvecamequation, curvecamcreate, utils,
from mathutils import *
from shapely import geometry as sgeometry
from cam.ui import *
bl_info = {
"name": "CAM - gcode generation tools",
@ -63,7 +63,7 @@ bl_info = {
"tracker_url": "",
"category": "Scene"}
PRECISION = 5
import cam.constants
was_hidden_dict = {}
@ -163,20 +163,20 @@ class machineSettings(bpy.types.PropertyGroup):
"toolchange, ending position",
default=False)
starting_position: bpy.props.FloatVectorProperty(name='Start position', default=(0, 0, 0), unit='LENGTH',
precision=PRECISION, subtype="XYZ", update=updateMachine)
precision=cam.constants.PRECISION, subtype="XYZ", update=updateMachine)
mtc_position: bpy.props.FloatVectorProperty(name='MTC position', default=(0, 0, 0), unit='LENGTH',
precision=PRECISION, subtype="XYZ", update=updateMachine)
precision=cam.constants.PRECISION, subtype="XYZ", update=updateMachine)
ending_position: bpy.props.FloatVectorProperty(name='End position', default=(0, 0, 0), unit='LENGTH',
precision=PRECISION, subtype="XYZ", update=updateMachine)
precision=cam.constants.PRECISION, subtype="XYZ", update=updateMachine)
working_area: bpy.props.FloatVectorProperty(name='Work Area', default=(0.500, 0.500, 0.100), unit='LENGTH',
precision=PRECISION, subtype="XYZ", update=updateMachine)
precision=cam.constants.PRECISION, subtype="XYZ", update=updateMachine)
feedrate_min: bpy.props.FloatProperty(name="Feedrate minimum /min", default=0.0, min=0.00001, max=320000,
precision=PRECISION, unit='LENGTH')
precision=cam.constants.PRECISION, unit='LENGTH')
feedrate_max: bpy.props.FloatProperty(name="Feedrate maximum /min", default=2, min=0.00001, max=320000,
precision=PRECISION, unit='LENGTH')
precision=cam.constants.PRECISION, unit='LENGTH')
feedrate_default: bpy.props.FloatProperty(name="Feedrate default /min", default=1.5, min=0.00001, max=320000,
precision=PRECISION, unit='LENGTH')
precision=cam.constants.PRECISION, unit='LENGTH')
hourly_rate: bpy.props.FloatProperty(name="Price per hour", default=100, min=0.005, precision=2)
# UNSUPPORTED:
@ -211,7 +211,7 @@ class machineSettings(bpy.types.PropertyGroup):
# default='X', update = updateOffsetImage)
collet_size: bpy.props.FloatProperty(name="#Collet size", description="Collet size for collision detection",
default=33, min=0.00001, max=320000, precision=PRECISION, unit="LENGTH")
default=33, min=0.00001, max=320000, precision=cam.constants.PRECISION, unit="LENGTH")
# exporter_start = bpy.props.StringProperty(name="exporter start", default="%")
# post processor options
@ -244,15 +244,15 @@ class PackObjectsSettings(bpy.types.PropertyGroup):
description='Fill direction of the packer algorithm',
default='Y')
sheet_x: FloatProperty(name="X size", description="Sheet size", min=0.001, max=10, default=0.5,
precision=PRECISION, unit="LENGTH")
precision=cam.constants.PRECISION, unit="LENGTH")
sheet_y: FloatProperty(name="Y size", description="Sheet size", min=0.001, max=10, default=0.5,
precision=PRECISION, unit="LENGTH")
precision=cam.constants.PRECISION, unit="LENGTH")
distance: FloatProperty(name="Minimum distance",
description="minimum distance between objects(should be at least cutter diameter!)",
min=0.001, max=10, default=0.01, precision=PRECISION, unit="LENGTH")
min=0.001, max=10, default=0.01, precision=cam.constants.PRECISION, unit="LENGTH")
tolerance: FloatProperty(name="Placement Tolerance",
description="Tolerance for placement: smaller value slower placemant",
min=0.001, max=0.02, default=0.005, precision=PRECISION, unit="LENGTH")
min=0.001, max=0.02, default=0.005, precision=cam.constants.PRECISION, unit="LENGTH")
rotate: bpy.props.BoolProperty(name="enable rotation", description="Enable rotation of elements", default=True)
rotate_angle: FloatProperty(name="Placement Angle rotation step",
description="bigger rotation angle,faster placemant", default=0.19635 * 4,
@ -265,7 +265,7 @@ class SliceObjectsSettings(bpy.types.PropertyGroup):
"""stores all data for machines"""
slice_distance: FloatProperty(name="Slicing distance",
description="slices distance in z, should be most often thickness of plywood sheet.",
min=0.001, max=10, default=0.005, precision=PRECISION, unit="LENGTH")
min=0.001, max=10, default=0.005, precision=cam.constants.PRECISION, unit="LENGTH")
slice_above0: bpy.props.BoolProperty(name="Slice above 0", description="only slice model above 0", default=False)
slice_3d: bpy.props.BoolProperty(name="3d slice", description="for 3d carving", default=False)
indexes: bpy.props.BoolProperty(name="add indexes", description="adds index text of layer + index", default=True)
@ -288,26 +288,26 @@ def operationValid(self, context):
o.changed = True
o.valid = True
invalidmsg = "Operation has no valid data input\n"
o.warnings = ""
o.info.warnings = ""
o = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]
if o.geometry_source == 'OBJECT':
if o.object_name not in bpy.data.objects:
o.valid = False
o.warnings = invalidmsg
o.info.warnings = invalidmsg
if o.geometry_source == 'COLLECTION':
if o.collection_name not in bpy.data.collections:
o.valid = False
o.warnings = invalidmsg
o.info.warnings = invalidmsg
elif len(bpy.data.collections[o.collection_name].objects) == 0:
o.valid = False
o.warnings = invalidmsg
o.info.warnings = invalidmsg
if o.geometry_source == 'IMAGE':
if o.source_image_name not in bpy.data.images:
o.valid = False
o.warnings = invalidmsg
o.info.warnings = invalidmsg
o.use_exact = False
o.optimisation.use_exact = False
o.update_offsetimage_tag = True
o.update_zbufferimage_tag = True
print('validity ')
@ -326,7 +326,7 @@ def updateChipload(self, context):
print('update chipload ')
o = self
# Old chipload
o.chipload = (o.feedrate / (o.spindle_rpm * o.cutter_flutes))
o.info.chipload = (o.feedrate / (o.spindle_rpm * o.cutter_flutes))
# New chipload with chip thining compensation.
# I have tried to combine these 2 formulas to compinsate for the phenomenon of chip thinning when cutting at less
# than 50% cutter engagement with cylindrical end mills. formula 1 Nominal Chipload is
@ -340,7 +340,7 @@ def updateChipload(self, context):
# we will be one tiny step on the way to a slightly better chipload calculating function.
# self.chipload = ((0.5*(o.cutter_diameter/o.dist_between_paths))/(math.sqrt((o.feedrate*1000)/(o.spindle_rpm*o.cutter_diameter*o.cutter_flutes)*(o.cutter_diameter/o.dist_between_paths)-1)))
print(o.chipload)
print(o.info.chipload)
def updateOffsetImage(self, context):
@ -376,28 +376,27 @@ def updateStrategy(o, context):
def updateCutout(o, context):
pass
def updateExact(o, context):
print('update exact ')
o.changed = True
o.update_zbufferimage_tag = True
o.update_offsetimage_tag = True
if o.use_exact and (
o.strategy == 'POCKET' or o.strategy == 'MEDIAL_AXIS' or o.inverse):
# o.use_exact = False
o.use_opencamlib = False
print(' pocket cannot use opencamlib')
if o.optimisation.use_exact:
if o.strategy == 'POCKET' or o.strategy == 'MEDIAL_AXIS' or o.inverse:
o.optimisation.use_opencamlib = False
print('Current operation cannot use exact mode')
else:
o.optimisation.use_opencamlib = False
def updateOpencamlib(o, context):
print('update opencamlib ')
o.changed = True
if o.use_opencamlib and (
if o.optimisation.use_opencamlib and (
o.strategy == 'POCKET' or o.strategy == 'MEDIAL_AXIS'):
o.use_exact = False
o.use_opencamlib = False
print('pocket cannot use opencamlib')
o.optimisation.use_exact = False
o.optimisation.use_opencamlib = False
print('Current operation cannot use opencamlib')
def updateBridges(o, context):
print('update bridges ')
@ -466,6 +465,12 @@ def getStrategyList(scene, context):
class camOperation(bpy.types.PropertyGroup):
material: bpy.props.PointerProperty(type=CAM_MATERIAL_Properties)
info: bpy.props.PointerProperty(type=CAM_INFO_Properties)
optimisation: bpy.props.PointerProperty(type=CAM_OPTIMISATION_Properties)
name: bpy.props.StringProperty(name="Operation Name", default="Operation", update=updateRest)
filename: bpy.props.StringProperty(name="File name", default="Operation", update=updateRest)
auto_export: bpy.props.BoolProperty(name="Auto export",
@ -586,7 +591,7 @@ class camOperation(bpy.types.PropertyGroup):
update=updateStrategy)
skin: FloatProperty(name="Skin", description="Material to leave when roughing ", min=0.0, max=1.0, default=0.0,
precision=PRECISION, unit="LENGTH", update=updateOffsetImage)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateOffsetImage)
inverse: bpy.props.BoolProperty(name="Inverse milling", description="Male to female model conversion",
default=False, update=updateOffsetImage)
array: bpy.props.BoolProperty(name="Use array",
@ -597,9 +602,9 @@ class camOperation(bpy.types.PropertyGroup):
array_y_count: bpy.props.IntProperty(name="Y count", description="Y count", default=1, min=1, max=32000,
update=updateRest)
array_x_distance: FloatProperty(name="X distance", description="distance between operation origins", min=0.00001,
max=1.0, default=0.01, precision=PRECISION, unit="LENGTH", update=updateRest)
max=1.0, default=0.01, precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
array_y_distance: FloatProperty(name="Y distance", description="distance between operation origins", min=0.00001,
max=1.0, default=0.01, precision=PRECISION, unit="LENGTH", update=updateRest)
max=1.0, default=0.01, precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
# pocket options
pocket_option: EnumProperty(name='Start Position', items=(
@ -621,23 +626,23 @@ class camOperation(bpy.types.PropertyGroup):
cutter_id: IntProperty(name="Tool number", description="For machines which support tool change based on tool id",
min=0, max=10000, default=1, update=updateRest)
cutter_diameter: FloatProperty(name="Cutter diameter", description="Cutter diameter = 2x cutter radius",
min=0.000001, max=10, default=0.003, precision=PRECISION, unit="LENGTH",
min=0.000001, max=10, default=0.003, precision=cam.constants.PRECISION, unit="LENGTH",
update=updateOffsetImage)
cylcone_diameter: FloatProperty(name="Bottom Diameter", description="Bottom diameter",
min=0.000001, max=10, default=0.003, precision=PRECISION, unit="LENGTH",
min=0.000001, max=10, default=0.003, precision=cam.constants.PRECISION, unit="LENGTH",
update=updateOffsetImage)
cutter_length: FloatProperty(name="#Cutter length", description="#not supported#Cutter length", min=0.0, max=100.0,
default=25.0, precision=PRECISION, unit="LENGTH", update=updateOffsetImage)
default=25.0, precision=cam.constants.PRECISION, unit="LENGTH", update=updateOffsetImage)
cutter_flutes: IntProperty(name="Cutter flutes", description="Cutter flutes", min=1, max=20, default=2,
update=updateChipload)
cutter_tip_angle: FloatProperty(name="Cutter v-carve angle", description="Cutter v-carve angle", min=0.0,
max=180.0, default=60.0, precision=PRECISION, update=updateOffsetImage)
max=180.0, default=60.0, precision=cam.constants.PRECISION, update=updateOffsetImage)
ball_radius: FloatProperty(name="Ball radius", description="Radius of", min=0.0,
max=0.035, default=0.001, unit="LENGTH", precision=PRECISION, update=updateOffsetImage)
max=0.035, default=0.001, unit="LENGTH", precision=cam.constants.PRECISION, update=updateOffsetImage)
# ball_cone_flute: FloatProperty(name="BallCone Flute Length", description="length of flute", min=0.0,
# max=0.1, default=0.017, unit="LENGTH", precision=PRECISION, update=updateOffsetImage)
# max=0.1, default=0.017, unit="LENGTH", precision=cam.constants.PRECISION, update=updateOffsetImage)
bull_corner_radius: FloatProperty(name="Bull Corner Radius", description="Radius tool bit corner", min=0.0,
max=0.035, default=0.005, unit="LENGTH", precision=PRECISION,
max=0.035, default=0.005, unit="LENGTH", precision=cam.constants.PRECISION,
update=updateOffsetImage)
cutter_description: StringProperty(name="Tool Description", default="", update=updateOffsetImage)
@ -658,9 +663,9 @@ class camOperation(bpy.types.PropertyGroup):
# steps
dist_between_paths: bpy.props.FloatProperty(name="Distance between toolpaths", default=0.001, min=0.00001, max=32,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
dist_along_paths: bpy.props.FloatProperty(name="Distance along toolpaths", default=0.0002, min=0.00001, max=32,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
parallel_angle: bpy.props.FloatProperty(name="Angle of paths", default=0, min=-360, max=360, precision=0,
subtype="ANGLE", unit="ROTATION", update=updateRest)
old_rotation_A: bpy.props.FloatProperty(name="A axis angle",
@ -687,7 +692,7 @@ class camOperation(bpy.types.PropertyGroup):
update=updateRest)
# carve only
carve_depth: bpy.props.FloatProperty(name="Carve depth", default=0.001, min=-.100, max=32, precision=PRECISION,
carve_depth: bpy.props.FloatProperty(name="Carve depth", default=0.001, min=-.100, max=32, precision=cam.constants.PRECISION,
unit="LENGTH", update=updateRest)
# drill only
@ -697,7 +702,7 @@ class camOperation(bpy.types.PropertyGroup):
default='MIDDLE_SYMETRIC', update=updateRest)
# waterline only
slice_detail: bpy.props.FloatProperty(name="Distance betwen slices", default=0.001, min=0.00001, max=32,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
waterline_fill: bpy.props.BoolProperty(name="Fill areas between slices",
description="Fill areas between slices in waterline mode", default=True,
update=updateRest)
@ -708,7 +713,7 @@ class camOperation(bpy.types.PropertyGroup):
# movement and ramps
use_layers: bpy.props.BoolProperty(name="Use Layers", description="Use layers for roughing", default=True,
update=updateRest)
stepdown: bpy.props.FloatProperty(name="", description="Layer height", default=0.01, min=0.00001, max=32, precision=PRECISION,
stepdown: bpy.props.FloatProperty(name="", description="Layer height", default=0.01, min=0.00001, max=32, precision=cam.constants.PRECISION,
unit="LENGTH", update=updateRest)
first_down: bpy.props.BoolProperty(name="First down",
description="First go down on a contour, then go to the next one",
@ -727,10 +732,10 @@ class camOperation(bpy.types.PropertyGroup):
default=False, update=updateRest)
lead_in: bpy.props.FloatProperty(name="Lead in radius",
description="Lead out radius for torch or laser to turn off",
min=0.00, max=1, default=0.0, precision=PRECISION, unit="LENGTH")
min=0.00, max=1, default=0.0, precision=cam.constants.PRECISION, unit="LENGTH")
lead_out: bpy.props.FloatProperty(name="Lead out radius",
description="Lead out radius for torch or laser to turn off",
min=0.00, max=1, default=0.0, precision=PRECISION, unit="LENGTH")
min=0.00, max=1, default=0.0, precision=cam.constants.PRECISION, unit="LENGTH")
profile_start: bpy.props.IntProperty(name="Start point", description="Start point offset", min=0, default=0,
update=updateRest)
@ -741,16 +746,16 @@ class camOperation(bpy.types.PropertyGroup):
description="Retract from material in circular motion", default=False,
update=updateRest)
retract_radius: bpy.props.FloatProperty(name='Retract arc radius', default=0.001, min=0.000001, max=100,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
retract_height: bpy.props.FloatProperty(name='Retract arc height', default=0.001, min=0.00000, max=100,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
minz_from_ob: bpy.props.BoolProperty(name="Depth from object", description="Operation ending depth from object",
default=True, update=updateRest)
minz_from_material: bpy.props.BoolProperty(name="Depth from material",
description="Operation ending depth from material",
default=False, update=updateRest)
minz: bpy.props.FloatProperty(name="Operation depth end", default=-0.01, min=-3, max=3, precision=PRECISION,
minz: bpy.props.FloatProperty(name="Operation depth end", default=-0.01, min=-3, max=3, precision=cam.constants.PRECISION,
unit="LENGTH",
update=updateRest) # this is input minz. True minimum z can be something else, depending on material e.t.c.
start_type: bpy.props.EnumProperty(name='Start type',
@ -764,7 +769,7 @@ class camOperation(bpy.types.PropertyGroup):
update=updateStrategy)
maxz: bpy.props.FloatProperty(name="Operation depth start", description='operation starting depth', default=0,
min=-3, max=10, precision=PRECISION, unit="LENGTH",
min=-3, max=10, precision=cam.constants.PRECISION, unit="LENGTH",
update=updateRest) # EXPERIMENTAL
#######################################################
@ -772,25 +777,25 @@ class camOperation(bpy.types.PropertyGroup):
####################################################
source_image_scale_z: bpy.props.FloatProperty(name="Image source depth scale", default=0.01, min=-1, max=1,
precision=PRECISION, unit="LENGTH", update=updateZbufferImage)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateZbufferImage)
source_image_size_x: bpy.props.FloatProperty(name="Image source x size", default=0.1, min=-10, max=10,
precision=PRECISION, unit="LENGTH", update=updateZbufferImage)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateZbufferImage)
source_image_offset: bpy.props.FloatVectorProperty(name='Image offset', default=(0, 0, 0), unit='LENGTH',
precision=PRECISION, subtype="XYZ", update=updateZbufferImage)
precision=cam.constants.PRECISION, subtype="XYZ", update=updateZbufferImage)
source_image_crop: bpy.props.BoolProperty(name="Crop source image",
description="Crop source image - the position of the sub-rectangle is relative to the whole image, so it can be used for e.g. finishing just a part of an image",
default=False, update=updateZbufferImage)
source_image_crop_start_x: bpy.props.FloatProperty(name='crop start x', default=0, min=0, max=100,
precision=PRECISION, subtype='PERCENTAGE',
precision=cam.constants.PRECISION, subtype='PERCENTAGE',
update=updateZbufferImage)
source_image_crop_start_y: bpy.props.FloatProperty(name='crop start y', default=0, min=0, max=100,
precision=PRECISION, subtype='PERCENTAGE',
precision=cam.constants.PRECISION, subtype='PERCENTAGE',
update=updateZbufferImage)
source_image_crop_end_x: bpy.props.FloatProperty(name='crop end x', default=100, min=0, max=100,
precision=PRECISION, subtype='PERCENTAGE',
precision=cam.constants.PRECISION, subtype='PERCENTAGE',
update=updateZbufferImage)
source_image_crop_end_y: bpy.props.FloatProperty(name='crop end y', default=100, min=0, max=100,
precision=PRECISION, subtype='PERCENTAGE',
precision=cam.constants.PRECISION, subtype='PERCENTAGE',
update=updateZbufferImage)
#########################################################
@ -810,7 +815,7 @@ class camOperation(bpy.types.PropertyGroup):
ambient_radius: FloatProperty(name="Ambient radius",
description="Radius around the part which will be milled if ambient is set to Around",
min=0.0, max=100.0, default=0.01, precision=PRECISION, unit="LENGTH",
min=0.0, max=100.0, default=0.01, precision=cam.constants.PRECISION, unit="LENGTH",
update=updateRest)
# ambient_cutter = EnumProperty(name='Borders',items=(('EXTRAFORCUTTER', 'Extra for cutter', "Extra space for cutter is cut around the segment"),('ONBORDER', "Cutter on edge", "Cutter goes exactly on edge of ambient with it's middle") ,('INSIDE', "Inside segment", 'Cutter stays within segment') ),description='handling of ambient and cutter size',default='INSIDE')
use_limit_curve: bpy.props.BoolProperty(name="Use limit curve", description="A curve limits the operation area",
@ -825,7 +830,7 @@ class camOperation(bpy.types.PropertyGroup):
# feeds
feedrate: FloatProperty(name="Feedrate", description="Feedrate", min=0.00005, max=50.0, default=1.0,
precision=PRECISION, unit="LENGTH", update=updateChipload)
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)
plunge_angle: bpy.props.FloatProperty(name="Plunge angle",
@ -844,13 +849,13 @@ class camOperation(bpy.types.PropertyGroup):
items=(('CW', 'Clock wise', 'a'), ('CCW', 'Counter clock wise', 'a')),
description='Spindle rotation direction', default='CW', update=updateRest)
free_movement_height: bpy.props.FloatProperty(name="Free movement height", default=0.01, min=0.0000, max=32,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
useG64: bpy.props.BoolProperty(name="G64 trajectory",
description='Use only if your machie supports G64 code. LinuxCNC and Mach3 do',
default=False, update=updateRest)
G64: bpy.props.FloatProperty(name="Path Control Mode with Optional Tolerance", default=0.0001, min=0.0000,
max=0.005,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
movement_insideout: EnumProperty(name='Direction',
items=(('INSIDEOUT', 'Inside out', 'a'), ('OUTSIDEIN', 'Outside in', 'a')),
description='approach to the piece', default='INSIDEOUT', update=updateRest)
@ -859,51 +864,29 @@ class camOperation(bpy.types.PropertyGroup):
default=False, update=updateRest)
stay_low: bpy.props.BoolProperty(name="Stay low if possible", default=True, update=updateRest)
merge_dist: bpy.props.FloatProperty(name="Merge distance - EXPERIMENTAL", default=0.0, min=0.0000, max=0.1,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
# optimization and performance
circle_detail: bpy.props.IntProperty(name="Detail of circles used for curve offsets", default=64, min=12, max=512,
update=updateRest)
use_exact: bpy.props.BoolProperty(name="Use exact mode",
description="Exact mode allows greater precision, but is slower with complex meshes",
default=True, update=updateExact)
exact_subdivide_edges: bpy.props.BoolProperty(name="Auto subdivide long edges",
description="This can avoid some collision issues when importing CAD models",
default=False, update=updateExact)
use_opencamlib: bpy.props.BoolProperty(name="Use OpenCAMLib",
description="Use OpenCAMLib to sample paths or get waterline shape",
default=False, update=updateOpencamlib)
pixsize: bpy.props.FloatProperty(name="sampling raster detail", default=0.0001, min=0.00001, max=0.1,
precision=PRECISION, unit="LENGTH", update=updateZbufferImage)
simulation_detail: bpy.props.FloatProperty(name="Simulation sampling raster detail", default=0.0002, min=0.00001,
max=0.01, precision=PRECISION, unit="LENGTH", update=updateRest)
do_simulation_feedrate: bpy.props.BoolProperty(name="Adjust feedrates with simulation EXPERIMENTAL",
description="Adjust feedrates with simulation", default=False,
update=updateRest)
imgres_limit: bpy.props.IntProperty(name="Maximum resolution in megapixels", default=16, min=1, max=512,
description="This property limits total memory usage and prevents crashes. Increase it if you know what are doing.",
update=updateZbufferImage)
optimize: bpy.props.BoolProperty(name="Reduce path points", description="Reduce path points", default=True,
update=updateRest)
optimize_threshold: bpy.props.FloatProperty(name="Reduction threshold in μm", default=.2, min=0.000000001,
max=1000, precision=20, update=updateRest)
dont_merge: bpy.props.BoolProperty(name="Dont merge outlines when cutting",
description="this is usefull when you want to cut around everything",
default=False, update=updateRest)
pencil_threshold: bpy.props.FloatProperty(name="Pencil threshold", default=0.00002, min=0.00000001, max=1,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
crazy_threshold1: bpy.props.FloatProperty(name="min engagement", default=0.02, min=0.00000001, max=100,
precision=PRECISION, update=updateRest)
precision=cam.constants.PRECISION, update=updateRest)
crazy_threshold5: bpy.props.FloatProperty(name="optimal engagement", default=0.3, min=0.00000001, max=100,
precision=PRECISION, update=updateRest)
precision=cam.constants.PRECISION, update=updateRest)
crazy_threshold2: bpy.props.FloatProperty(name="max engagement", default=0.5, min=0.00000001, max=100,
precision=PRECISION, update=updateRest)
precision=cam.constants.PRECISION, update=updateRest)
crazy_threshold3: bpy.props.FloatProperty(name="max angle", default=2, min=0.00000001, max=100,
precision=PRECISION, update=updateRest)
precision=cam.constants.PRECISION, update=updateRest)
crazy_threshold4: bpy.props.FloatProperty(name="test angle step", default=0.05, min=0.00000001, max=100,
precision=PRECISION, update=updateRest)
precision=cam.constants.PRECISION, update=updateRest)
# Add pocket operation to medial axis
add_pocket_for_medial: bpy.props.BoolProperty(name="Add pocket operation",
description="clean unremoved material after medial axis",
@ -916,21 +899,19 @@ class camOperation(bpy.types.PropertyGroup):
update=updateRest)
####
medial_axis_threshold: bpy.props.FloatProperty(name="Long vector threshold", default=0.001, min=0.00000001,
max=100, precision=PRECISION, unit="LENGTH", update=updateRest)
max=100, precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
medial_axis_subdivision: bpy.props.FloatProperty(name="Fine subdivision", default=0.0002, min=0.00000001, max=100,
precision=PRECISION, unit="LENGTH", update=updateRest)
precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest)
# calculations
duration: bpy.props.FloatProperty(name="Estimated time", default=0.01, min=0.0000, max=3200000000,
precision=PRECISION, unit="TIME")
# chip_rate
# bridges
use_bridges: bpy.props.BoolProperty(name="Use bridges", description="use bridges in cutout", default=False,
update=updateBridges)
bridges_width: bpy.props.FloatProperty(name='width of bridges', default=0.002, unit='LENGTH', precision=PRECISION,
bridges_width: bpy.props.FloatProperty(name='width of bridges', default=0.002, unit='LENGTH', precision=cam.constants.PRECISION,
update=updateBridges)
bridges_height: bpy.props.FloatProperty(name='height of bridges',
description="Height from the bottom of the cutting operation",
default=0.0005, unit='LENGTH', precision=PRECISION, update=updateBridges)
default=0.0005, unit='LENGTH', precision=cam.constants.PRECISION, update=updateBridges)
bridges_collection_name: bpy.props.StringProperty(name='Bridges Collection',
description='Collection of curves used as bridges',
update=operationValid)
@ -949,7 +930,7 @@ class camOperation(bpy.types.PropertyGroup):
# update = updateStrategy)
#
# bridges_per_curve = bpy.props.IntProperty(name="minimum bridges per curve", description="", default=4, min=1, max=512, update = updateBridges)
# bridges_max_distance = bpy.props.FloatProperty(name = 'Maximum distance between bridges', default=0.08, unit='LENGTH', precision=PRECISION, update = updateBridges)
# bridges_max_distance = bpy.props.FloatProperty(name = 'Maximum distance between bridges', default=0.08, unit='LENGTH', precision=cam.constants.PRECISION, update = updateBridges)
use_modifiers: BoolProperty(name="use mesh modifiers",
description="include mesh modifiers using render level when calculating operation, does not effect original mesh",
@ -957,34 +938,21 @@ class camOperation(bpy.types.PropertyGroup):
# optimisation panel
# material settings
material_from_model: bpy.props.BoolProperty(name="Estimate from model",
description="Estimate material size from model", default=True,
update=updateMaterial)
material_radius_around_model: bpy.props.FloatProperty(name="radius around model",
description="How much to add to model size on all sides",
default=0.0, unit='LENGTH', precision=PRECISION,
update=updateMaterial)
material_center_x: bpy.props.BoolProperty(name="Center with X axis", description="Position model centered on X",
default=False, update=updateMaterial)
material_center_y: bpy.props.BoolProperty(name="Center with Y axis", description="Position model centered on Y",
default=False, update=updateMaterial)
material_Z: bpy.props.EnumProperty(name="Z placement", items=(
('ABOVE', 'Above', 'Place objec above 0'), ('BELOW', 'Below', 'Place object below 0'),
('CENTERED', 'Centered', 'Place object centered on 0')), description="Position below Zero", default='BELOW',
update=updateMaterial)
material_origin: bpy.props.FloatVectorProperty(name='Material origin', default=(0, 0, 0), unit='LENGTH',
precision=PRECISION, subtype="XYZ", update=updateMaterial)
material_size: bpy.props.FloatVectorProperty(name='Material size', default=(0.200, 0.200, 0.100), min=0, unit='LENGTH',
precision=PRECISION, subtype="XYZ", update=updateMaterial)
min: bpy.props.FloatVectorProperty(name='Operation minimum', default=(0, 0, 0), unit='LENGTH', precision=PRECISION,
##############################################################################
# MATERIAL SETTINGS
min: bpy.props.FloatVectorProperty(
name='Operation minimum', default=(0, 0, 0), unit='LENGTH', precision=cam.constants.PRECISION,
subtype="XYZ")
max: bpy.props.FloatVectorProperty(name='Operation maximum', default=(0, 0, 0), unit='LENGTH', precision=PRECISION,
max: bpy.props.FloatVectorProperty(name='Operation maximum', default=(0, 0, 0), unit='LENGTH', precision=cam.constants.PRECISION,
subtype="XYZ")
warnings: bpy.props.StringProperty(name='warnings', description='warnings', default='', update=updateRest)
chipload: bpy.props.FloatProperty(name="chipload", description="Calculated chipload", default=0.0, unit='LENGTH',
precision=10)
# g-code options for operation
output_header: BoolProperty(name="output g-code header",
@ -1155,21 +1123,21 @@ class AddPresetCamOperation(bl_operators.presets.AddPresetBase, Operator):
preset_defines = ["o = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]"]
preset_values = ['o.use_layers', 'o.duration', 'o.chipload', 'o.material_from_model', 'o.stay_low', 'o.carve_depth',
'o.dist_along_paths', 'o.source_image_crop_end_x', 'o.source_image_crop_end_y', 'o.material_size',
'o.material_radius_around_model', 'o.use_limit_curve', 'o.cut_type', 'o.use_exact',
'o.exact_subdivide_edges', 'o.minz_from_ob', 'o.free_movement_height',
preset_values = ['o.use_layers', 'o.info.duration', 'o.info.chipload', 'o.material.estimate_from_model', 'o.stay_low', 'o.carve_depth',
'o.dist_along_paths', 'o.source_image_crop_end_x', 'o.source_image_crop_end_y', 'o.material.size',
'o.material.radius_around_model', 'o.use_limit_curve', 'o.cut_type', 'o.optimisation.use_exact',
'o.optimisation.exact_subdivide_edges', 'o.minz_from_ob', 'o.free_movement_height',
'o.source_image_crop_start_x', 'o.movement_insideout', 'o.spindle_rotation_direction', 'o.skin',
'o.source_image_crop_start_y', 'o.movement_type', 'o.source_image_crop', 'o.limit_curve',
'o.spindle_rpm', 'o.ambient_behaviour', 'o.cutter_type', 'o.source_image_scale_z',
'o.cutter_diameter', 'o.source_image_size_x', 'o.curve_object', 'o.curve_object1',
'o.cutter_flutes', 'o.ambient_radius', 'o.simulation_detail', 'o.update_offsetimage_tag',
'o.dist_between_paths', 'o.max', 'o.min', 'o.pixsize', 'o.slice_detail', 'o.parallel_step_back',
'o.cutter_flutes', 'o.ambient_radius', 'o.optimisation.simulation_detail', 'o.update_offsetimage_tag',
'o.dist_between_paths', 'o.max', 'o.min', 'o.optimisation.pixsize', 'o.slice_detail', 'o.parallel_step_back',
'o.drill_type', 'o.source_image_name', 'o.dont_merge', 'o.update_silhouete_tag',
'o.material_origin', 'o.inverse', 'o.waterline_fill', 'o.source_image_offset', 'o.circle_detail',
'o.material.origin', 'o.inverse', 'o.waterline_fill', 'o.source_image_offset', 'o.optimisation.circle_detail',
'o.strategy', 'o.update_zbufferimage_tag', 'o.stepdown', 'o.feedrate', 'o.cutter_tip_angle',
'o.cutter_id', 'o.path_object_name', 'o.pencil_threshold', 'o.geometry_source',
'o.optimize_threshold', 'o.protect_vertical', 'o.plunge_feedrate', 'o.minz', 'o.warnings',
'o.optimize_threshold', 'o.protect_vertical', 'o.plunge_feedrate', 'o.minz', 'o.info.warnings',
'o.object_name', 'o.optimize', 'o.parallel_angle', 'o.cutter_length',
'o.output_header', 'o.gcode_header', 'o.output_trailer', 'o.gcode_trailer', 'o.use_modifiers',
'o.minz_from_material', 'o.useG64',
@ -1225,7 +1193,6 @@ def get_panels(): # convenience function for bot register and unregister functi
ui.CAM_UL_operations,
# ui.CAM_UL_orientations,
ui.CAM_UL_chains,
camOperation,
opReference,
camChain,
machineSettings,
@ -1307,6 +1274,7 @@ def get_panels(): # convenience function for bot register and unregister functi
# pack module:
PackObjectsSettings,
SliceObjectsSettings,
camOperation,
)
@ -1426,7 +1394,6 @@ def compatible_panels():
classes = [
ui.CAM_UL_operations,
ui.CAM_UL_chains,
camOperation,
opReference,
camChain,
machineSettings,
@ -1435,10 +1402,14 @@ classes = [
ui.CAM_CHAINS_Panel,
ui.CAM_OPERATIONS_Panel,
ui.CAM_INFO_Properties,
ui.CAM_INFO_Panel,
ui.CAM_MATERIAL_Panel,
ui.CAM_MATERIAL_Properties,
ui.CAM_MATERIAL_PositionObject,
ui.CAM_OPERATION_PROPERTIES_Panel,
ui.CAM_OPTIMISATION_Panel,
ui.CAM_OPTIMISATION_Properties,
ui.CAM_AREA_Panel,
ui.CAM_MOVEMENT_Panel,
ui.CAM_FEEDRATE_Panel,
@ -1459,7 +1430,6 @@ classes = [
ops.PathExportChain,
ops.PathsAll,
ops.PathExport,
ops.CAMPositionObject,
ops.CAMSimulate,
ops.CAMSimulateChain,
ops.CamChainAdd,
@ -1516,10 +1486,13 @@ classes = [
# pack module:
PackObjectsSettings,
SliceObjectsSettings,
camOperation,
]
def register():
for p in classes:
bpy.utils.register_class(p)

Wyświetl plik

@ -27,7 +27,7 @@ def getCachePath(o):
fn=bpy.data.filepath
l=len(bpy.path.basename(fn))
bn=bpy.path.basename(fn)[:-6]
try:
os.mkdir(fn[:-l]+'temp_cam')
except:
@ -45,10 +45,10 @@ def calculatePath(op):
picklepath=getCachePath(o)+'.pickle'
f=open(picklepath,'wb')
d={}
d['duration']=o.duration
d['warnings']=o.warnings
d['duration']=o.info.duration
d['warnings']=o.info.warnings
#pickle path...
oname="cam_path_"+o.name
if oname in s.objects:
@ -73,7 +73,7 @@ def calculatePath(op):
time.sleep(1)
sys.stdout.write('progress{%s}\n' % ('finished'))
sys.stdout.flush()
#parse arguments here
argv = sys.argv

Wyświetl plik

@ -635,7 +635,7 @@ def optimizeChunk(chunk, operation):
protect_vertical = operation.protect_vertical and operation.machine_axes == '3'
for vi in range(0, len(points) - 1):
if not compare(chunk.points[-1], points[vi + 1], points[vi], operation.optimize_threshold * 0.000001):
if not compare(chunk.points[-1], points[vi + 1], points[vi], operation.optimisation.optimize_threshold * 0.000001):
if naxispoints:
chunk.append(points[vi], startpoints[vi], endpoints[vi], rotations[vi])
else:

Wyświetl plik

@ -65,7 +65,7 @@ def getCutterBullet(o):
cutter = bpy.context.active_object
cutter.scale *= BULLET_SCALE
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
bpy.ops.rigidbody.object_add(type='ACTIVE')
cutter = bpy.context.active_object
#cutter.dimensions.z = 0.2 * BULLET_SCALE # should be sufficient for now... 20 cm.
@ -84,7 +84,7 @@ def getCutterBullet(o):
cutter = bpy.context.active_object
cutter.scale *= BULLET_SCALE
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
bpy.ops.rigidbody.object_add(type='ACTIVE')
cutter = bpy.context.active_object
cutter.rigid_body.collision_shape = 'CONE'
@ -134,7 +134,7 @@ def getCutterBullet(o):
ob_scr.merge_threshold = 0
ob_scr.use_merge_vertices = True
bpy.ops.object.modifier_apply(modifier='scr')
bpy.data.objects['BallConeTool'].select_set(True)
bpy.data.objects['BallConeTool'].select_set(True)
cutter = bpy.context.active_object
cutter.scale *= BULLET_SCALE
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
@ -247,7 +247,7 @@ def prepareBulletCollision(o):
bpy.data.meshes.remove(oldmesh)
# subdivide long edges here:
if o.exact_subdivide_edges:
if o.optimisation.exact_subdivide_edges:
subdivideLongEdges(collisionob, o.cutter_diameter * 2)
bpy.ops.rigidbody.object_add(type='ACTIVE')

Wyświetl plik

@ -0,0 +1,9 @@
# Package to store all constants of BlenderCAM
# PRECISION is used in most operations
PRECISION = 5
CHIPLOAD_PRECISION = 10
MAX_OPERATION_TIME = 3200000000 # seconds

Wyświetl plik

@ -19,7 +19,7 @@
#
# ***** END GPL LICENCE BLOCK *****
# here is the Gcode generaton
# here is the Gcode generaton
import bpy
import time
@ -496,7 +496,7 @@ def exportGcodePath(filename, vertslist, operations):
for aline in lines:
c.write(aline + '\n')
o.duration = duration * unitcorr
o.info.duration = duration * unitcorr
if enable_dust:
c.write(stop_dust + '\n')
if enable_hold:
@ -532,7 +532,7 @@ def getPath(context, operation): # should do all path calculations.
utils.getOperationSources(operation)
operation.warnings = ''
operation.info.warnings = ''
checkMemoryLimit(operation)
print(operation.machine_axes)
@ -585,16 +585,16 @@ def checkMemoryLimit(o):
# utils.getBounds(o)
sx = o.max.x - o.min.x
sy = o.max.y - o.min.y
resx = sx / o.pixsize
resy = sy / o.pixsize
resx = sx / o.optimisation.pixsize
resy = sy / o.optimisation.pixsize
res = resx * resy
limit = o.imgres_limit * 1000000
limit = o.optimisation.imgres_limit * 1000000
# print('co se to deje')
if res > limit:
ratio = (res / limit)
o.pixsize = o.pixsize * math.sqrt(ratio)
o.warnings = o.warnings + 'sampling resolution had to be reduced!\n'
print('changing sampling resolution to %f' % o.pixsize)
o.optimisation.pixsize = o.optimisation.pixsize * math.sqrt(ratio)
o.info.warnings += f"Memory limit: sampling resolution reduced to {o.optimisation.pixsize}\n"
print('changing sampling resolution to %f' % o.optimisation.pixsize)
# this is the main function.
@ -688,7 +688,7 @@ def getPath3axis(context, operation):
strategy.chunksToMesh(chunks, o)
elif o.strategy == 'WATERLINE' and o.use_opencamlib:
elif o.strategy == 'WATERLINE' and o.optimisation.use_opencamlib:
utils.getAmbient(o)
chunks = []
oclGetWaterline(o, chunks)
@ -699,7 +699,7 @@ def getPath3axis(context, operation):
ch.points.reverse()
strategy.chunksToMesh(chunks, o)
elif o.strategy == 'WATERLINE' and not o.use_opencamlib:
elif o.strategy == 'WATERLINE' and not o.optimisation.use_opencamlib:
topdown = True
tw = time.time()
chunks = []
@ -766,7 +766,7 @@ def getPath3axis(context, operation):
o.inverse and not poly.is_empty and slicesfilled == 1): # first slice fill
restpoly = lastslice
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail)
fillz = z
i = 0
@ -783,7 +783,7 @@ def getPath3axis(context, operation):
parentChildDist(lastchunks, nchunks, o)
lastchunks = nchunks
# slicechunks.extend(polyToChunks(restpoly,z))
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail)
i += 1
# print(i)
@ -801,7 +801,7 @@ def getPath3axis(context, operation):
if o.inverse and poly.is_empty and slicesfilled > 0:
restpoly = bound_rectangle.difference(lastslice)
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail)
i = 0
while not restpoly.is_empty: # 'GeometryCollection':#len(restpoly.boundary.coords)>0:
@ -812,7 +812,7 @@ def getPath3axis(context, operation):
slicechunks.extend(nchunks)
parentChildDist(lastchunks, nchunks, o)
lastchunks = nchunks
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)
restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail)
i += 1
percent = int(h / nslices * 100)

Wyświetl plik

@ -142,7 +142,7 @@ def offsetArea(o, samples):
o.offset_image.fill(-10)
sourceArray = samples
cutterArray = simulation.getCutterArray(o, o.pixsize)
cutterArray = simulation.getCutterArray(o, o.optimisation.pixsize)
# progress('image size', sourceArray.shape)
@ -222,7 +222,7 @@ def getOffsetImageCavities(o, i): # for pencil operation mainly
def imageEdgeSearch_online(o, ar, zimage): # search edges for pencil strategy, another try.
minx, miny, minz, maxx, maxy, maxz = o.min.x, o.min.y, o.min.z, o.max.x, o.max.y, o.max.z
r = ceil((o.cutter_diameter/12)/o.pixsize) # was commented
r = ceil((o.cutter_diameter/12)/o.optimisation.pixsize) # was commented
coef = 0.75
maxarx = ar.shape[0]
maxary = ar.shape[1]
@ -340,8 +340,8 @@ def imageEdgeSearch_online(o, ar, zimage): # search edges for pencil strategy,
for ch in chunks:
ch = ch.points
for i in range(0, len(ch)):
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.pixsize + miny, ch[i][2])
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.optimisation.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.optimisation.pixsize + miny, ch[i][2])
return chunks
@ -352,13 +352,13 @@ def crazyPath(o):
sx = o.max.x - o.min.x
sy = o.max.y - o.min.y
resx = ceil(sx / o.simulation_detail) + 2 * o.borderwidth
resy = ceil(sy / o.simulation_detail) + 2 * o.borderwidth
resx = ceil(sx / o.optimisation.simulation_detail) + 2 * o.borderwidth
resy = ceil(sy / o.optimisation.simulation_detail) + 2 * o.borderwidth
o.millimage = numpy.array((0.1), dtype=float)
o.millimage.resize(resx, resy)
o.millimage.fill(0)
o.cutterArray = -simulation.getCutterArray(o, o.simulation_detail) # getting inverted cutter
o.cutterArray = -simulation.getCutterArray(o, o.optimisation.simulation_detail) # getting inverted cutter
def buildStroke(start, end, cutterArray):
@ -396,7 +396,7 @@ def crazyStrokeImage(o):
# this surprisingly works, and can be used as a basis for something similar to adaptive milling strategy.
minx, miny, minz, maxx, maxy, maxz = o.min.x, o.min.y, o.min.z, o.max.x, o.max.y, o.max.z
r = int((o.cutter_diameter / 2.0) / o.pixsize) # ceil((o.cutter_diameter/12)/o.pixsize)
r = int((o.cutter_diameter / 2.0) / o.optimisation.pixsize) # ceil((o.cutter_diameter/12)/o.optimisation.pixsize)
d = 2 * r
coef = 0.75
@ -562,8 +562,8 @@ def crazyStrokeImage(o):
for ch in chunks:
ch = ch.points
for i in range(0, len(ch)):
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.pixsize + miny, 0)
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.optimisation.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.optimisation.pixsize + miny, 0)
return chunks
@ -581,7 +581,7 @@ def crazyStrokeImageBinary(o, ar, avoidar):
ar[:, :o.borderwidth] = 0
ar[:, -o.borderwidth:] = 0
r = int((o.cutter_diameter / 2.0) / o.pixsize) # ceil((o.cutter_diameter/12)/o.pixsize)
r = int((o.cutter_diameter / 2.0) / o.optimisation.pixsize) # ceil((o.cutter_diameter/12)/o.optimisation.pixsize)
d = 2 * r
coef = 0.75
maxarx = ar.shape[0]
@ -821,8 +821,8 @@ def crazyStrokeImageBinary(o, ar, avoidar):
for ch in chunks:
ch = ch.points
for i in range(0, len(ch)):
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.pixsize + miny, o.minz)
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.optimisation.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.optimisation.pixsize + miny, o.minz)
return chunks
@ -830,7 +830,7 @@ def crazyStrokeImageBinary(o, ar, avoidar):
def imageToChunks(o, image, with_border=False):
t = time.time()
minx, miny, minz, maxx, maxy, maxz = o.min.x, o.min.y, o.min.z, o.max.x, o.max.y, o.max.z
pixsize = o.pixsize
pixsize = o.optimisation.pixsize
image = image.astype(numpy.uint8)
@ -840,7 +840,7 @@ def imageToChunks(o, image, with_border=False):
indices1 = ar.nonzero()
borderspread = 2
# o.cutter_diameter/o.pixsize#when the border was excluded precisely, sometimes it did remove some silhouette parts
# o.cutter_diameter/o.optimisation.pixsize#when the border was excluded precisely, sometimes it did remove some silhouette parts
r = o.borderwidth - borderspread
# to prevent outline of the border was 3 before and also (o.cutter_diameter/2)/pixsize+o.borderwidth
if with_border:
@ -980,7 +980,7 @@ def imageToChunks(o, image, with_border=False):
# print('directsimplify')
reduxratio = 1.25 # was 1.25
soptions = ['distance', 'distance', o.pixsize * reduxratio, 5, o.pixsize * reduxratio]
soptions = ['distance', 'distance', o.optimisation.pixsize * reduxratio, 5, o.optimisation.pixsize * reduxratio]
nchunks = []
for i, ch in enumerate(vecchunks):
@ -1030,8 +1030,8 @@ def getResolution(o):
sx = o.max.x - o.min.x
sy = o.max.y - o.min.y
resx = ceil(sx / o.pixsize) + 2 * o.borderwidth
resy = ceil(sy / o.pixsize) + 2 * o.borderwidth
resx = ceil(sx / o.optimisation.pixsize) + 2 * o.borderwidth
resy = ceil(sy / o.optimisation.pixsize) + 2 * o.borderwidth
# this basically renders blender zbuffer and makes it accessible by saving & loading it again.
# that's because blender doesn't allow accessing pixels in render :(
@ -1043,13 +1043,13 @@ def renderSampleImage(o):
# print(o.zbuffer_image)
if o.geometry_source == 'OBJECT' or o.geometry_source == 'COLLECTION':
pixsize = o.pixsize
pixsize = o.optimisation.pixsize
sx = o.max.x - o.min.x
sy = o.max.y - o.min.y
resx = math.ceil(sx / o.pixsize) + 2 * o.borderwidth
resy = math.ceil(sy / o.pixsize) + 2 * o.borderwidth
resx = math.ceil(sx / o.optimisation.pixsize) + 2 * o.borderwidth
resy = math.ceil(sy / o.optimisation.pixsize) + 2 * o.borderwidth
if not o.update_zbufferimage_tag and len(o.zbuffer_image) == resx and len(o.zbuffer_image[0]) == resy:
# if we call this accidentally in more functions, which currently happens...
@ -1106,7 +1106,7 @@ def renderSampleImage(o):
bpy.context.scene.camera = camera
camera.data.type = 'ORTHO'
camera.data.ortho_scale = max(resx * o.pixsize, resy * o.pixsize)
camera.data.ortho_scale = max(resx * o.optimisation.pixsize, resy * o.optimisation.pixsize)
camera.location = (o.min.x + sx / 2, o.min.y + sy / 2, 1)
camera.rotation_euler = (0, 0, 0)
# if not o.render_all:#removed in 0.3
@ -1160,8 +1160,8 @@ def renderSampleImage(o):
o.offset_image.resize(ex - sx + 2 * o.borderwidth, ey - sy + 2 * o.borderwidth)
o.pixsize = o.source_image_size_x / i.size[0]
simple.progress('pixel size in the image source', o.pixsize)
o.optimisation.pixsize = o.source_image_size_x / i.size[0]
simple.progress('pixel size in the image source', o.optimisation.pixsize)
rawimage = imagetonumpy(i)
maxa = numpy.max(rawimage)

Wyświetl plik

@ -51,29 +51,6 @@ def threadread(tcom):
e = inline.find('}')
tcom.outtext = inline[s + 9:e]
class CAMPositionObject(bpy.types.Operator):
"""position object for CAM operation. Tests object bounds and places them so the object
is aligned to be positive from x and y and negative from z."""
bl_idname = "object.cam_position"
bl_label = "position object for CAM operation"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
s = bpy.context.scene
operation = s.cam_operations[s.cam_active_operation]
if operation.object_name in bpy.data.objects:
utils.positionObject(operation)
else:
print('no object assigned')
return {'FINISHED'}
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.prop_search(self, "operation", bpy.context.scene, "cam_operations")
@bpy.app.handlers.persistent
def timer_update(context):
"""monitoring of background processes"""
@ -568,14 +545,14 @@ def Add_Pocket(self, maxdepth, sname, new_cutter_diameter):
if not mpocket_exists: # create a pocket operation if it does not exist already
s.cam_operations.add()
o = s.cam_operations[-1]
o.object_name = 'medial_pocket'
o.object_name= 'medial_pocket'
s.cam_active_operation = len(s.cam_operations) - 1
o.name = 'MedialPocket'
o.filename = o.name
o.strategy = 'POCKET'
o.use_layers = False
o.material_from_model = False
o.material_size[2] = -maxdepth
o.material.estimate_from_model = False
o.material.size[2] = -maxdepth
o.minz_from_ob = False
o.minz_from_material = True
@ -594,22 +571,18 @@ class CamOperationAdd(bpy.types.Operator):
s = bpy.context.scene
fixUnits()
ob = bpy.context.active_object
if ob is None: raise CamException("No object selected")
minx, miny, minz, maxx, maxy, maxz = utils.getBoundsWorldspace([ob])
s.cam_operations.add()
o = s.cam_operations[-1]
ob = bpy.context.active_object
if ob is not None:
o.object_name = ob.name
minx, miny, minz, maxx, maxy, maxz = utils.getBoundsWorldspace([ob])
o.minz = minz
else:
# FIXME Creating an operation without any object selected
# This should actually display a modal dialog
# and cancel the operation creation
o.object_name = "none"
o.minz = 0
o.object_name = ob.name
o.minz = minz
s.cam_active_operation = len(s.cam_operations) - 1
o.name = f"Op_{o.object_name}_{s.cam_active_operation + 1}"
o.name = f"Op_{ob.name}_{s.cam_active_operation + 1}"
o.filename = o.name
if s.objects.get('CAM_machine') is None:

Wyświetl plik

@ -320,7 +320,7 @@ def getPathPattern(operation):
pathchunks = []
chunks = []
for p in polys:
p = p.buffer(-o.dist_between_paths / 10, o.circle_detail)
p = p.buffer(-o.dist_between_paths / 10, o.optimisation.circle_detail)
# first, move a bit inside, because otherwise the border samples go crazy very often changin between
# hit/non hit and making too many jumps in the path.
chunks.extend(shapelyToChunks(p, 0))
@ -335,7 +335,7 @@ def getPathPattern(operation):
for porig in polys:
p = porig
while not p.is_empty:
p = p.buffer(-o.dist_between_paths, o.circle_detail)
p = p.buffer(-o.dist_between_paths, o.optimisation.circle_detail)
if not p.is_empty:
nchunks = shapelyToChunks(p, zlevel)
@ -358,13 +358,13 @@ def getPathPattern(operation):
for a in range(0, int(steps)):
dist = d
if a == int(o.cutter_diameter / 2 / o.dist_between_paths):
if o.use_exact:
dist += o.pixsize * 0.85
if o.optimisation.use_exact:
dist += o.optimisation.pixsize * 0.85
# this is here only because silhouette is still done with zbuffer method,
# even if we use bullet collisions.
else:
dist += o.pixsize * 2.5
p = p.buffer(dist, o.circle_detail)
dist += o.optimisation.pixsize * 2.5
p = p.buffer(dist, o.optimisation.circle_detail)
if not p.is_empty:
nchunks = shapelyToChunks(p, zlevel)
if o.movement_insideout == 'INSIDEOUT':

Wyświetl plik

@ -113,7 +113,7 @@ def generateSimulationImage(operations, limits):
sy = maxy - miny
o = operations[0] # getting sim detail and others from first op.
simulation_detail = o.simulation_detail
simulation_detail = o.optimisation.simulation_detail
borderwidth = o.borderwidth
resx = math.ceil(sx / simulation_detail) + 2 * borderwidth
resy = math.ceil(sy / simulation_detail) + 2 * borderwidth

Wyświetl plik

@ -101,7 +101,7 @@ def cutout(o):
if o.outlines_count > 1:
for i in range(1, o.outlines_count):
chunksFromCurve.extend(shapelyToChunks(p, -1))
p = p.buffer(distance=o.dist_between_paths * offset, resolution=o.circle_detail, join_style=join,
p = p.buffer(distance=o.dist_between_paths * offset, resolution=o.optimisation.circle_detail, join_style=join,
mitre_limit=2)
chunksFromCurve.extend(shapelyToChunks(p, -1))
@ -205,7 +205,7 @@ def curve(o):
pathSamples = []
utils.getOperationSources(o)
if not o.onlycurves:
o.warnings += 'at least one of assigned objects is not a curve\n'
o.info.warnings += 'at least one of assigned objects is not a curve\n'
for ob in o.objects:
pathSamples.extend(curveToChunks(ob)) # make the chunks from curve here
@ -253,7 +253,7 @@ def proj_curve(s, o):
from cam import chunk
if targetCurve.type != 'CURVE':
o.warnings = o.warnings + 'Projection target and source have to be curve objects!\n '
o.info.warnings += 'Projection target and source have to be curve objects!\n '
return
if 1:
@ -322,17 +322,17 @@ def pocket(o):
lastchunks = []
centers = None
firstoutline = p # for testing in the end.
prest = p.buffer(-c_offset, o.circle_detail)
prest = p.buffer(-c_offset, o.optimisation.circle_detail)
while not p.is_empty:
if o.pocketToCurve:
polygon_utils_cam.shapelyToCurve('3dpocket', p, 0.0) # make a curve starting with _3dpocket
nchunks = shapelyToChunks(p, o.min.z)
# print("nchunks")
pnew = p.buffer(-o.dist_between_paths, o.circle_detail)
pnew = p.buffer(-o.dist_between_paths, o.optimisation.circle_detail)
if pnew.is_empty:
pt = p.buffer(-c_offset, o.circle_detail) # test if the last curve will leave material
pt = p.buffer(-c_offset, o.optimisation.circle_detail) # test if the last curve will leave material
if not pt.is_empty:
pnew = pt
# print("pnew")
@ -376,7 +376,7 @@ def pocket(o):
if not chunksFromCurve[chi].children:
p = ch.points[0] # TODO:intercept closest next point when it should stay low
# first thing to do is to check if helix enter can really enter.
checkc = Circle(helix_radius + c_offset, o.circle_detail)
checkc = Circle(helix_radius + c_offset, o.optimisation.circle_detail)
checkc = affinity.translate(checkc, p[0], p[1])
covers = False
for poly in o.silhouete:
@ -387,7 +387,7 @@ def pocket(o):
if covers:
revolutions = (l[0] - p[2]) / revheight
# print(revolutions)
h = Helix(helix_radius, o.circle_detail, l[0], p, revolutions)
h = Helix(helix_radius, o.optimisation.circle_detail, l[0], p, revolutions)
# invert helix if not the typical direction
if (o.movement_type == 'CONVENTIONAL' and o.spindle_rotation_direction == 'CW') or (
o.movement_type == 'CLIMB' and o.spindle_rotation_direction == 'CCW'):
@ -397,7 +397,7 @@ def pocket(o):
h = nhelix
ch.points = h + ch.points
else:
o.warnings = o.warnings + 'Helix entry did not fit! \n '
o.info.warnings += 'Helix entry did not fit! \n '
ch.closed = True
ch.rampZigZag(l[0], l[1], o)
# Arc retract here first try:
@ -427,7 +427,7 @@ def pocket(o):
p = (p.x, p.y, p.z)
# progress(str((v1,v,p)))
h = Helix(o.retract_radius, o.circle_detail, p[2] + o.retract_height, p, revolutions)
h = Helix(o.retract_radius, o.optimisation.circle_detail, p[2] + o.retract_height, p, revolutions)
e = Euler((0, 0, rotangle + pi)) # angle to rotate whole retract move
rothelix = []
@ -445,7 +445,7 @@ def pocket(o):
c = sgeometry.Polygon(c)
# print('çoutline')
# print(c)
coutline = c.buffer(c_offset, o.circle_detail)
coutline = c.buffer(c_offset, o.optimisation.circle_detail)
# print(h)
# print('çoutline')
# print(coutline)
@ -585,7 +585,7 @@ def medial_axis(o):
slope = math.tan(math.pi * (90 - angle / 2) / 180) # angle in degrees
# slope = math.tan((math.pi-angle)/2) #angle in radian
new_cutter_diameter = o.cutter_diameter
m_o_name = o.object_name
m_o_ob = o.object_name
if o.cutter_type == 'VCARVE':
angle = o.cutter_tip_angle
# start the max depth calc from the "start depth" of the operation.
@ -601,7 +601,7 @@ def medial_axis(o):
elif o.cutter_type == 'BALLNOSE':
maxdepth = - new_cutter_diameter / 2
else:
o.warnings += 'Only Ballnose, Ball and V-carve cutters\n are supported'
o.info.warnings += 'Only Ballnose, Ball and V-carve cutters\n are supported'
return
# remember resolutions of curves, to refine them,
# otherwise medial axis computation yields too many branches in curved parts
@ -755,7 +755,7 @@ def medial_axis(o):
if o.add_pocket_for_medial:
# o.add_pocket_for_medial = False
# export medial axis parameter to pocket op
ops.Add_Pocket(None, maxdepth, m_o_name, new_cutter_diameter)
ops.Add_Pocket(None, maxdepth, m_o_ob, new_cutter_diameter)
def getLayers(operation, startdepth, enddepth):
@ -828,7 +828,7 @@ def chunksToMesh(chunks, o):
if len(ch.points) > 0: # TODO: there is a case where parallel+layers+zigzag ramps send empty chunks here...
# print(len(ch.points))
nverts = []
if o.optimize:
if o.optimisation.optimize:
ch = optimizeChunk(ch, o)
# lift and drop
@ -874,7 +874,7 @@ def chunksToMesh(chunks, o):
verts.append(v)
lifted = lift
# print(verts_rotations)
if o.use_exact and not o.use_opencamlib:
if o.optimisation.use_exact and not o.optimisation.use_opencamlib:
cleanupBulletCollision(o)
print(time.time() - t)
t = time.time()

Wyświetl plik

@ -101,7 +101,7 @@ def testParallel(pos):
bpy.ops.scene.cam_operation_add()
o = bpy.context.scene.cam_operations[-1]
o.ambient_behaviour = 'AROUND'
o.material_radius_around_model = 0.01
o.material.radius_around_model = 0.01
bpy.ops.object.calculate_cam_path()
@ -110,7 +110,7 @@ def testWaterline(pos):
bpy.ops.scene.cam_operation_add()
o = bpy.context.scene.cam_operations[-1]
o.strategy = 'WATERLINE'
o.pixsize = .0002
o.optimisation.pixsize = .0002
# o.ambient_behaviour='AROUND'
# o.material_radius_around_model=0.01

Wyświetl plik

@ -18,6 +18,7 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENCE BLOCK *****
import sys
import bpy
from bpy.types import UIList, Operator
@ -34,16 +35,16 @@ from cam import gcodeimportparser, simple
from cam.simple import *
from cam.ui_panels.buttons_panel import CAMButtonsPanel
from cam.ui_panels.info import CAM_INFO_Panel
from cam.ui_panels.info import *
from cam.ui_panels.operations import CAM_OPERATIONS_Panel
from cam.ui_panels.cutter import CAM_CUTTER_Panel
from cam.ui_panels.machine import CAM_MACHINE_Panel
from cam.ui_panels.material import CAM_MATERIAL_Panel
from cam.ui_panels.material import *
from cam.ui_panels.chains import CAM_UL_operations, CAM_UL_chains, CAM_CHAINS_Panel
from cam.ui_panels.op_properties import CAM_OPERATION_PROPERTIES_Panel
from cam.ui_panels.movement import CAM_MOVEMENT_Panel
from cam.ui_panels.feedrate import CAM_FEEDRATE_Panel
from cam.ui_panels.optimisation import CAM_OPTIMISATION_Panel
from cam.ui_panels.optimisation import *
from cam.ui_panels.area import CAM_AREA_Panel

Wyświetl plik

@ -1,5 +1,4 @@
import bpy
import sys
# Panel definitions
class CAMButtonsPanel:
@ -11,38 +10,27 @@ class CAMButtonsPanel:
@classmethod
def poll(cls, context):
rd = context.scene.render
rd = bpy.context.scene.render
return rd.engine in cls.COMPAT_ENGINES
def __init__(self):
self.scene = bpy.context.scene
self.active_op = self.active_operation()
def active_operation_index(self):
return(self.scene.cam_active_operation)
return(bpy.context.scene.cam_active_operation)
def active_operation(self):
active_op = None
try:
active_op = self.scene.cam_operations[self.active_operation_index()]
active_op = bpy.context.scene.cam_operations[self.active_operation_index()]
except IndexError:
print(f"Invalid operation index {self.active_operation_index()}")
pass
return(active_op)
def operations_count(self):
return(len(self.scene.cam_operations))
return(len(bpy.context.scene.cam_operations))
def has_operations(self):
return (self.operations_count() > 0)
def opencamlib_version(self):
try:
import ocl
except ImportError:
try:
import opencamlib as ocl
except ImportError as e:
return
return(ocl.version())

Wyświetl plik

@ -79,7 +79,7 @@ class CAM_CUTTER_Panel(CAMButtonsPanel, bpy.types.Panel):
layout.prop(ao, 'lead_out')
if ao.cutter_type == 'CUSTOM':
if ao.use_exact:
if ao.optimisation.use_exact:
layout.label(text='Warning - only convex shapes are supported. ', icon='COLOR_RED')
layout.label(text='If your custom cutter is concave,')
layout.label(text='switch exact mode off.')

Wyświetl plik

@ -1,12 +1,32 @@
import sys
import bpy
from cam.simple import strInUnits
from cam.ui_panels.buttons_panel import CAMButtonsPanel
import cam.utils
import cam.constants
# Info panel
# This panel gives general information about the current operation
class CAM_INFO_Properties(bpy.types.PropertyGroup):
warnings: bpy.props.StringProperty(
name='warnings',
description='warnings',
default='',
update=cam.utils.update_operation)
chipload: bpy.props.FloatProperty(
name="chipload", description="Calculated chipload",
default=0.0, unit='LENGTH',
precision=cam.constants.CHIPLOAD_PRECISION)
duration: bpy.props.FloatProperty(
name="Estimated time", default=0.01, min=0.0000,
max=cam.constants.MAX_OPERATION_TIME,
precision=cam.constants.PRECISION, unit="TIME")
class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel):
"""CAM info panel"""
bl_label = "CAM info & warnings"
@ -14,60 +34,78 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel):
COMPAT_ENGINES = {'BLENDERCAM_RENDER'}
# Display the Info Panel
def draw(self, context):
self.draw_opencamlib_version()
if self.has_operations():
self.draw_active_op_warnings()
self.draw_active_op_data()
self.draw_active_op_time()
self.draw_active_op_money_cost()
else:
self.layout.label(text='No CAM operation created')
# Display the OpenCamLib version
def draw_opencamlib_version(self):
opencamlib_version = self.opencamlib_version()
opencamlib_version = cam.utils.opencamlib_version()
if opencamlib_version is None:
self.layout.label(text = "Opencamlib is not installed")
self.layout.label(text="Opencamlib is not installed")
else:
self.layout.label(text = f"Opencamlib v{opencamlib_version} installed")
self.layout.label(
text=f"Opencamlib v{opencamlib_version} installed")
# Display warnings related to the current operation
def draw_active_op_warnings(self):
active_op = self.active_operation()
if active_op is None: return
if self.active_op is None:
return
for line in active_op.warnings.rstrip("\n").split("\n"):
if len(line) > 0 :
for line in self.active_op.info.warnings.rstrip("\n").split("\n"):
if len(line) > 0:
self.layout.label(text=line, icon='ERROR')
def draw_active_op_data(self):
active_op = self.active_operation()
if active_op is None: return
if not active_op.valid: return
if not int(active_op.duration*60) > 0: return
# Display the time estimation for the current operation
def draw_active_op_time(self):
if self.active_op is None:
return
if not self.active_op.valid:
return
if not int(self.active_op.info.duration * 60) > 0:
return
active_op_time_text = "Operation Time: %d s " % int(active_op.duration*60)
if active_op.duration > 60:
active_op_time_text += " (%dh %dmin)" % (int(active_op.duration / 60), round(active_op.duration % 60))
elif active_op.duration > 1:
active_op_time_text += " (%dmin)" % round(active_op.duration % 60)
time_estimate = f"Operation Time: {int(self.active_op.info.duration*60)}s "
if self.active_op.info.duration > 60:
time_estimate += f" ({int(self.active_op.info.duration / 60)}h"
time_estimate += f" {round(self.active_op.info.duration % 60)}min)"
elif self.active_op.info.duration > 1:
time_estimate += f" ({round(self.active_op.info.duration % 60)}min)"
self.layout.label(text = active_op_time_text)
self.layout.label(text=time_estimate)
self.layout.label(text="Chipload: %s/tooth" % strInUnits(active_op.chipload, 4))
# Display the chipload (does this work ?)
def draw_active_op_chipload(self):
if self.active_op is None:
return
if not self.active_op.valid:
return
if not self.active_op.info.chipload > 0:
return
chipload = f"Chipload: {strInUnits(self.active_op.info.chipload, 4)}/tooth"
self.layout.label(text=chipload)
# Display the current operation money cost
def draw_active_op_money_cost(self):
active_op = self.active_operation()
if active_op is None: return
if not active_op.valid: return
if not int(active_op.duration*60) > 0: return
if self.active_op is None or not self.active_op.valid:
return
if not int(self.active_op.info.duration * 60) > 0:
return
# TODO: the hourly_rate button properties should be moved here (UI related only)
# Right now, trying to do so causes an error
self.layout.prop(self.scene.cam_machine, 'hourly_rate')
row = self.layout.row()
row.label(text='Hourly Rate')
row.prop(bpy.context.scene.cam_machine, 'hourly_rate', text='')
if float(self.scene.cam_machine.hourly_rate) < 0.01: return
if float(bpy.context.scene.cam_machine.hourly_rate) < 0.01:
return
cost_per_second = self.scene.cam_machine.hourly_rate / 3600
self.layout.label(text = "Operation cost: $%.2f (%.2f $/s)"
% (active_op.duration * 60 * cost_per_second, cost_per_second)
)
cost_per_second = bpy.context.scene.cam_machine.hourly_rate / 3600
total_cost = self.active_op.info.duration * 60 * cost_per_second
op_cost = f"Operation cost: ${total_cost:.2f} (${cost_per_second:.2f}/s)"
self.layout.label(text=op_cost)

Wyświetl plik

@ -1,36 +1,127 @@
import bpy
from cam.ui_panels.buttons_panel import CAMButtonsPanel
import cam.utils
import cam.constants
class CAM_MATERIAL_Properties(bpy.types.PropertyGroup):
estimate_from_model: bpy.props.BoolProperty(
name="Estimate cut area from model",
description="Estimate cut area based on model geometry",
default=True,
update=cam.utils.update_material
)
radius_around_model: bpy.props.FloatProperty(
name='Radius around model',
description="Increase cut area around the model on X and Y by this amount",
default=0.0, unit='LENGTH', precision=cam.constants.PRECISION,
update=cam.utils.update_material
)
center_x: bpy.props.BoolProperty(
name="Center on X axis",
description="Position model centered on X",
default=False, update=cam.utils.update_material
)
center_y: bpy.props.BoolProperty(
name="Center on Y axis",
description="Position model centered on Y",
default=False, update=cam.utils.update_material
)
z_position: bpy.props.EnumProperty(
name="Z placement", items=(
('ABOVE', 'Above', 'Place object vertically above the XY plane'),
('BELOW', 'Below', 'Place object vertically below the XY plane'),
('CENTERED', 'Centered', 'Place object vertically centered on the XY plane')),
description="Position below Zero", default='BELOW',
update=cam.utils.update_material
)
# material_origin
origin: bpy.props.FloatVectorProperty(
name='Material origin', default=(0, 0, 0), unit='LENGTH',
precision=cam.constants.PRECISION, subtype="XYZ",
update=cam.utils.update_material
)
# material_size
size: bpy.props.FloatVectorProperty(
name='Material size', default=(0.200, 0.200, 0.100), min=0, unit='LENGTH',
precision=cam.constants.PRECISION, subtype="XYZ",
update=cam.utils.update_material
)
# Position object for CAM operation. Tests object bounds and places them so the object
# is aligned to be positive from x and y and negative from z."""
class CAM_MATERIAL_PositionObject(bpy.types.Operator):
bl_idname = "object.material_cam_position"
bl_label = "position object for CAM operation"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
s = bpy.context.scene
operation = s.cam_operations[s.cam_active_operation]
if operation.object_name in bpy.data.objects:
cam.utils.positionObject(operation)
else:
print('no object assigned')
return {'FINISHED'}
def draw(self, context):
self.layout.prop_search(self, "operation", bpy.context.scene, "cam_operations")
class CAM_MATERIAL_Panel(CAMButtonsPanel, bpy.types.Panel):
"""CAM material panel"""
bl_label = "CAM Material size and position"
bl_idname = "WORLD_PT_CAM_MATERIAL"
COMPAT_ENGINES = {'BLENDERCAM_RENDER'}
def draw(self, context):
layout = self.layout
scene = bpy.context.scene
if len(scene.cam_operations) == 0:
layout.label(text='Add operation first')
if len(scene.cam_operations) > 0:
ao = scene.cam_operations[scene.cam_active_operation]
if ao:
layout.template_running_jobs()
if ao.geometry_source in ['OBJECT', 'COLLECTION']:
layout.prop(ao, 'material_from_model')
if self.active_op is None:
return
if ao.material_from_model:
layout.prop(ao, 'material_radius_around_model')
else:
layout.prop(ao, 'material_origin')
layout.prop(ao, 'material_size')
# FIXME: This function displays the progression of a job with a progress bar
# Commenting because it makes no sense here
# Consider removing it entirely
# self.layout.template_running_jobs()
layout.prop(ao, 'material_center_x')
layout.prop(ao, 'material_center_y')
layout.prop(ao, 'material_Z')
layout.operator("object.cam_position", text="Position object")
else:
layout.label(text='Estimated from image')
if self.active_op.geometry_source not in ['OBJECT', 'COLLECTION']:
self.layout.label(text='Estimated from image')
return
self.layout.prop(self.active_op.material, 'estimate_from_model')
if self.active_op.material.estimate_from_model:
self.draw_estimate_material_from_model()
else:
self.draw_custom_material_size_and_origin()
self.draw_axis_alignment()
# Display section selecting the radius around the model
def draw_estimate_material_from_model(self):
row_radius = self.layout.row()
row_radius.label(text="Additional radius")
row_radius.prop(self.active_op.material, 'radius_around_model', text='')
# Display section showing custom material size
def draw_custom_material_size_and_origin(self):
self.layout.prop(self.active_op.material, 'origin')
self.layout.prop(self.active_op.material, 'size')
# Display Axis alignment section
def draw_axis_alignment(self):
row_axis = self.layout.row()
row_axis.prop(self.active_op.material, 'center_x')
row_axis.prop(self.active_op.material, 'center_y')
self.layout.prop(self.active_op.material, 'z_position')
self.layout.operator("object.material_cam_position", text="Position object")

Wyświetl plik

@ -90,7 +90,7 @@ class CAM_OPERATION_PROPERTIES_Panel(CAMButtonsPanel, bpy.types.Panel):
elif ao.strategy == 'WATERLINE':
layout.label(text="OCL doesn't support fill areas")
if not ao.use_opencamlib:
if not ao.optimisation.use_opencamlib:
layout.prop(ao, 'slice_detail')
layout.prop(ao, 'waterline_fill')
if ao.waterline_fill:

Wyświetl plik

@ -21,27 +21,23 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel):
# Main draw function
def draw(self, context):
self.context = context
self.draw_operations_list()
# FIXME: is this ever used ?
use_experimental = bpy.context.preferences.addons['cam'].preferences.experimental
if (not self.has_operations()): return
ao = self.active_operation()
if ao is None: return
if self.active_op is None: return
self.draw_presets()
self.draw_output_buttons()
layout = self.layout
sub = layout.column()
sub.active = not ao.computing
sub = self.layout.column()
sub.active = not self.active_op.computing
# Draw operation name and filename
sub.prop(ao, 'name')
sub.prop(ao, 'filename')
sub.prop(self.active_op, 'name')
sub.prop(self.active_op, 'filename')
self.draw_operation_source()
self.draw_operation_options()
@ -51,7 +47,7 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel):
# create, delete, duplicate, reorder
def draw_operations_list(self):
row = self.layout.row()
row.template_list("CAM_UL_operations", '', self.scene, "cam_operations", self.scene, 'cam_active_operation')
row.template_list("CAM_UL_operations", '', bpy.context.scene, "cam_operations", bpy.context.scene, 'cam_active_operation')
col = row.column(align=True)
col.operator("scene.cam_operation_add", icon='ADD', text="")
col.operator("scene.cam_operation_copy", icon='COPYDOWN', text="")
@ -71,58 +67,53 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel):
# Draw buttons "Calculate path & export Gcode", "Export Gcode ", and "Simulate this operation"
def draw_output_buttons(self):
layout = self.layout
ao = self.active_operation()
# FIXME This does not seem to work - there is never a "Computing" label displayed
# while an operation is being calculated
if ao.computing:
row = layout.row(align=True)
if self.active_op.computing:
row = self.layout.row(align=True)
row.label(text='computing')
row.operator('object.kill_calculate_cam_paths_background', text="", icon='CANCEL')
else:
if ao.valid:
layout.operator("object.calculate_cam_path", text="Calculate path & export Gcode")
if ao.name is not None:
name = "cam_path_{}".format(ao.name)
if self.scene.objects.get(name) is not None:
layout.operator("object.cam_export", text="Export Gcode ")
layout.operator("object.cam_simulate", text="Simulate this operation")
if self.active_op.valid:
self.layout.operator("object.calculate_cam_path", text="Calculate path & export Gcode")
if self.active_op.name is not None:
name = "cam_path_{}".format(self.active_op.name)
if bpy.context.scene.objects.get(name) is not None:
self.layout.operator("object.cam_export", text="Export Gcode ")
self.layout.operator("object.cam_simulate", text="Simulate this operation")
else:
layout.label(text="operation invalid, can't compute")
self.layout.label(text="operation invalid, can't compute")
# Draw a list of objects which will be used as the source of the operation
# FIXME Right now, cameras or lights may be used, which crashes
# The user should only be able to choose meshes and curves
def draw_operation_source(self):
layout = self.layout
ao = self.active_operation()
layout.prop(ao, 'geometry_source')
self.layout.prop(self.active_op, 'geometry_source')
if ao.strategy == 'CURVE':
if ao.geometry_source == 'OBJECT':
layout.prop_search(ao, "object_name", bpy.data, "objects")
elif ao.geometry_source == 'COLLECTION':
layout.prop_search(ao, "collection_name", bpy.data, "collections")
if self.active_op.strategy == 'CURVE':
if self.active_op.geometry_source == 'OBJECT':
self.layout.prop_search(self.active_op, "object_name", bpy.data, "objects")
elif self.active_op.geometry_source == 'COLLECTION':
self.layout.prop_search(self.active_op, "collection_name", bpy.data, "collections")
else:
if ao.geometry_source == 'OBJECT':
layout.prop_search(ao, "object_name", bpy.data, "objects")
if ao.enable_A:
layout.prop(ao, 'rotation_A')
if ao.enable_B:
layout.prop(ao, 'rotation_B')
if self.active_op.geometry_source == 'OBJECT':
self.layout.prop_search(self.active_op, "object_name", bpy.data, "objects")
if self.active_op.enable_A:
self.layout.prop(self.active_op, 'rotation_A')
if self.active_op.enable_B:
self.layout.prop(self.active_op, 'rotation_B')
elif ao.geometry_source == 'COLLECTION':
layout.prop_search(ao, "collection_name", bpy.data, "collections")
elif self.active_op.geometry_source == 'COLLECTION':
self.layout.prop_search(self.active_op, "collection_name", bpy.data, "collections")
else:
layout.prop_search(ao, "source_image_name", bpy.data, "images")
self.layout.prop_search(self.active_op, "source_image_name", bpy.data, "images")
if ao.strategy in ['CARVE', 'PROJECTED_CURVE']:
layout.prop_search(ao, "curve_object", bpy.data, "objects")
if ao.strategy == 'PROJECTED_CURVE':
layout.prop_search(ao, "curve_object1", bpy.data, "objects")
if self.active_op.strategy in ['CARVE', 'PROJECTED_CURVE']:
self.layout.prop_search(self.active_op, "curve_object", bpy.data, "objects")
if self.active_op.strategy == 'PROJECTED_CURVE':
self.layout.prop_search(self.active_op, "curve_object1", bpy.data, "objects")
# Draw Operation options:
# Remove redundant points (optimizes operation)
@ -131,20 +122,18 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel):
# Parent path to object (?)
def draw_operation_options(self):
layout = self.layout
ao = self.active_operation()
# TODO This should be in some optimization menu
if ao.strategy != 'DRILL':
layout.prop(ao, 'remove_redundant_points')
if self.active_op.strategy != 'DRILL':
self.layout.prop(self.active_op, 'remove_redundant_points')
if ao.remove_redundant_points:
layout.label(text='Revise your Code before running!')
layout.label(text='Quality will suffer if tolerance')
layout.label(text='is high')
layout.prop(ao, 'simplify_tol')
if self.active_op.remove_redundant_points:
self.layout.label(text='Revise your Code before running!')
self.layout.label(text='Quality will suffer if tolerance')
self.layout.label(text='is high')
self.layout.prop(self.active_op, 'simplify_tol')
if ao.geometry_source in ['OBJECT', 'COLLECTION']:
layout.prop(ao, 'use_modifiers')
layout.prop(ao, 'hide_all_others')
layout.prop(ao, 'parent_path_to_object')
if self.active_op.geometry_source in ['OBJECT', 'COLLECTION']:
self.layout.prop(self.active_op, 'use_modifiers')
self.layout.prop(self.active_op, 'hide_all_others')
self.layout.prop(self.active_op, 'parent_path_to_object')

Wyświetl plik

@ -1,5 +1,51 @@
import bpy
from cam.ui_panels.buttons_panel import CAMButtonsPanel
import cam.utils
import cam.constants
class CAM_OPTIMISATION_Properties(bpy.types.PropertyGroup):
optimize: bpy.props.BoolProperty(
name="Reduce path points", description="Reduce path points", default=True,
update=cam.utils.update_operation)
optimize_threshold: bpy.props.FloatProperty(
name="Reduction threshold in μm", default=.2, min=0.000000001,
max=1000, precision=20, update=cam.utils.update_operation)
use_exact: bpy.props.BoolProperty(
name="Use exact mode",
description="Exact mode allows greater precision, but is slower with complex meshes",
default=True, update=cam.utils.update_exact_mode)
imgres_limit: bpy.props.IntProperty(
name="Maximum resolution in megapixels", default=16, min=1, max=512,
description="Limits total memory usage and prevents crashes. Increase it if you know what are doing",
update=cam.utils.update_zbuffer_image)
pixsize: bpy.props.FloatProperty(
name="sampling raster detail", default=0.0001, min=0.00001, max=0.1,
precision=cam.constants.PRECISION, unit="LENGTH", update=cam.utils.update_zbuffer_image)
use_opencamlib: bpy.props.BoolProperty(
name="Use OpenCAMLib",
description="Use OpenCAMLib to sample paths or get waterline shape",
default=False, update=cam.utils.update_opencamlib)
exact_subdivide_edges: bpy.props.BoolProperty(
name="Auto subdivide long edges",
description="This can avoid some collision issues when importing CAD models",
default=False, update=cam.utils.update_exact_mode)
circle_detail: bpy.props.IntProperty(
name="Detail of circles used for curve offsets", default=64, min=12, max=512,
update=cam.utils.update_operation)
simulation_detail: bpy.props.FloatProperty(
name="Simulation sampling raster detail", default=0.0002, min=0.00001,
max=0.01, precision=cam.constants.PRECISION, unit="LENGTH", update=cam.utils.update_operation)
class CAM_OPTIMISATION_Panel(CAMButtonsPanel, bpy.types.Panel):
"""CAM optimisation panel"""
@ -9,42 +55,77 @@ class CAM_OPTIMISATION_Panel(CAMButtonsPanel, bpy.types.Panel):
COMPAT_ENGINES = {'BLENDERCAM_RENDER'}
def draw(self, context):
layout = self.layout
scene = bpy.context.scene
if self.active_op is None:
return
if not self.active_op.valid:
return
if len(scene.cam_operations) == 0:
layout.label(text='Add operation first')
if len(scene.cam_operations) > 0:
ao = scene.cam_operations[scene.cam_active_operation]
if ao.valid:
layout.prop(ao, 'optimize')
if ao.optimize:
layout.prop(ao, 'optimize_threshold')
if ao.geometry_source == 'OBJECT' or ao.geometry_source == 'COLLECTION':
exclude_exact = ao.strategy in ['MEDIAL_AXIS', 'POCKET', 'CUTOUT', 'DRILL', 'PENCIL',
'CURVE']
if not exclude_exact:
layout.prop(ao, 'use_exact')
layout.label(text="Exact mode must be set for opencamlib to work ")
self.draw_optimize()
self.layout.separator()
self.draw_exact_mode()
self.draw_use_opencamlib()
self.layout.separator()
self.draw_simulation_detail()
opencamlib_version = self.opencamlib_version()
if opencamlib_version is None:
layout.label(text="Opencamlib is NOT available ")
layout.prop(ao, 'exact_subdivide_edges')
else:
layout.label(text=f"Opencamlib v{opencamlib_version} installed")
layout.prop(ao, 'use_opencamlib')
def draw_optimize(self):
if self.active_op is None:
return
if not self.active_op.valid:
return
if exclude_exact or not ao.use_exact:
layout.prop(ao, 'pixsize')
layout.prop(ao, 'imgres_limit')
self.layout.prop(self.active_op.optimisation, 'optimize')
if self.active_op.optimisation.optimize:
self.layout.prop(self.active_op.optimisation, 'optimize_threshold')
sx = ao.max.x - ao.min.x
sy = ao.max.y - ao.min.y
resx = int(sx / ao.pixsize)
resy = int(sy / ao.pixsize)
l = 'resolution: ' + str(resx) + ' x ' + str(resy)
layout.label(text=l)
def draw_exact_mode(self):
if self.active_op is None:
return
if not self.active_op.valid:
return
layout.prop(ao, 'simulation_detail')
layout.prop(ao, 'circle_detail')
if not self.active_op.geometry_source == 'OBJECT' or self.active_op.geometry_source == 'COLLECTION':
return
self.exact_possible = self.active_op.strategy not in [
'MEDIAL_AXIS', 'POCKET', 'CUTOUT', 'DRILL', 'PENCIL', 'CURVE']
if self.exact_possible:
self.layout.prop(self.active_op.optimisation, 'use_exact')
if not self.exact_possible or not self.active_op.optimisation.use_exact:
self.layout.prop(self.active_op.optimisation, 'pixsize')
self.layout.prop(self.active_op.optimisation, 'imgres_limit')
sx = self.active_op.max.x - self.active_op.min.x
sy = self.active_op.max.y - self.active_op.min.y
resx = int(sx / self.active_op.optimisation.pixsize)
resy = int(sy / self.active_op.optimisation.pixsize)
if resx > 0 and resy > 0:
resolution = 'Resolution: ' + str(resx) + ' x ' + str(resy)
self.layout.label(text=resolution)
def draw_use_opencamlib(self):
if self.active_op is None:
return
if not self.active_op.valid:
return
if not (self.exact_possible and self.active_op.optimisation.use_exact):
return
opencamlib_version = cam.utils.opencamlib_version()
if opencamlib_version is None:
self.layout.label(text="Opencamlib is not available ")
self.layout.prop(self.active_op.optimisation, 'exact_subdivide_edges')
else:
self.layout.prop(self.active_op.optimisation, 'use_opencamlib')
def draw_simulation_detail(self):
if self.active_op is None:
return
if not self.active_op.valid:
return
self.layout.prop(self.active_op.optimisation, 'simulation_detail')
self.layout.prop(self.active_op.optimisation, 'circle_detail')

Wyświetl plik

@ -1,3 +1,4 @@
# blender CAM utils.py (c) 2012 Vilem Novak
#
# ***** BEGIN GPL LICENSE BLOCK *****
@ -53,6 +54,49 @@ from shapely import geometry as sgeometry
SHAPELY = True
# The following functions are temporary
# until all content in __init__.py is cleaned up
def update_material(self, context):
addMaterialAreaObject()
def update_operation(self, context):
from . import updateRest
active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]
updateRest(active_op, bpy.context)
def update_exact_mode(self, context):
from . import updateExact
active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]
updateExact(active_op, bpy.context)
def update_opencamlib(self, context):
from . import updateOpencamlib
active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]
updateOpencamlib(active_op, bpy.context)
def update_zbuffer_image(self, context):
from . import updateZbufferImage
active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]
updateZbufferImage(active_op, bpy.context)
# Import OpencamLib
# Return available OpenCamLib version on success, None otherwise
def opencamlib_version():
try:
import ocl
except ImportError:
try:
import opencamlib as ocl
except ImportError as e:
return
return(ocl.version())
def positionObject(operation):
ob = bpy.data.objects[operation.object_name]
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
@ -63,21 +107,21 @@ def positionObject(operation):
totx = maxx - minx
toty = maxy - miny
totz = maxz - minz
if operation.material_center_x:
if operation.material.center_x:
ob.location.x -= minx + totx / 2
else:
ob.location.x -= minx
if operation.material_center_y:
if operation.material.center_y:
ob.location.y -= miny + toty / 2
else:
ob.location.y -= miny
if operation.material_Z == 'BELOW':
if operation.material.z_position == 'BELOW':
ob.location.z -= maxz
elif operation.material_Z == 'ABOVE':
elif operation.material.z_position == 'ABOVE':
ob.location.z -= minz
elif operation.material_Z == 'CENTERED':
elif operation.material.z_position == 'CENTERED':
ob.location.z -= minz + totz / 2
if ob.type != 'CURVE':
@ -225,7 +269,7 @@ def getOperationSources(o):
collection = bpy.data.collections[o.collection_name]
o.objects = collection.objects
elif o.geometry_source == 'IMAGE':
o.use_exact = False
o.optimisation.use_exact = False
if o.geometry_source == 'OBJECT' or o.geometry_source == 'COLLECTION':
o.onlycurves = True
@ -252,23 +296,23 @@ def getBounds(o):
o.min.z = o.minz # max(bb[0][2]+l.z,o.minz)#
print("not minz from object")
if o.material_from_model:
print("material_from_model")
if o.material.estimate_from_model:
print("Estimate material from model")
o.min.x = minx - o.material_radius_around_model
o.min.y = miny - o.material_radius_around_model
o.min.x = minx - o.material.radius_around_model
o.min.y = miny - o.material.radius_around_model
o.max.z = max(o.maxz, maxz)
o.max.x = maxx + o.material_radius_around_model
o.max.y = maxy + o.material_radius_around_model
o.max.x = maxx + o.material.radius_around_model
o.max.y = maxy + o.material.radius_around_model
else:
print("not material from model")
o.min.x = o.material_origin.x
o.min.y = o.material_origin.y
o.min.z = o.material_origin.z - o.material_size.z
o.max.x = o.min.x + o.material_size.x
o.max.y = o.min.y + o.material_size.y
o.max.z = o.material_origin.z
o.min.x = o.material.origin.x
o.min.y = o.material.origin.y
o.min.z = o.material.origin.z - o.material.size.z
o.max.x = o.min.x + o.material.size.x
o.max.y = o.min.y + o.material.size.y
o.max.z = o.material.origin.z
else:
i = bpy.data.images[o.source_image_name]
@ -283,19 +327,19 @@ def getBounds(o):
sy = 0
ey = i.size[1]
o.pixsize = o.source_image_size_x / i.size[0]
o.optimisation.pixsize = o.source_image_size_x / i.size[0]
o.min.x = o.source_image_offset.x + sx * o.pixsize
o.max.x = o.source_image_offset.x + ex * o.pixsize
o.min.y = o.source_image_offset.y + sy * o.pixsize
o.max.y = o.source_image_offset.y + ey * o.pixsize
o.min.x = o.source_image_offset.x + sx * o.optimisation.pixsize
o.max.x = o.source_image_offset.x + ex * o.optimisation.pixsize
o.min.y = o.source_image_offset.y + sy * o.optimisation.pixsize
o.max.y = o.source_image_offset.y + ey * o.optimisation.pixsize
o.min.z = o.source_image_offset.z + o.minz
o.max.z = o.source_image_offset.z
s = bpy.context.scene
m = s.cam_machine
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.warnings += 'Operation exceeds your machine limits\n'
o.info.warnings += 'Operation exceeds your machine limits\n'
def getBoundsMultiple(operations):
@ -333,10 +377,10 @@ def samplePathLow(o, ch1, ch2, dosample):
bpath.points.append([p.x, p.y, p.z])
# print('between path')
# print(len(bpath))
pixsize = o.pixsize
pixsize = o.optimisation.pixsize
if dosample:
if not (o.use_opencamlib and o.use_exact):
if o.use_exact:
if not (o.optimisation.use_opencamlib and o.optimisation.use_exact):
if o.optimisation.use_exact:
if o.update_bullet_collision_tag:
prepareBulletCollision(o)
o.update_bullet_collision_tag = False
@ -363,8 +407,8 @@ def sampleChunks(o, pathSamples, layers):
minx, miny, minz, maxx, maxy, maxz = o.min.x, o.min.y, o.min.z, o.max.x, o.max.y, o.max.z
getAmbient(o)
if o.use_exact: # prepare collision world
if o.use_opencamlib:
if o.optimisation.use_exact: # prepare collision world
if o.optimisation.use_opencamlib:
oclSample(o, pathSamples)
cutterdepth = 0
else:
@ -379,11 +423,11 @@ def sampleChunks(o, pathSamples, layers):
if o.strategy != 'WATERLINE': # or prepare offset image, but not in some strategies.
prepareArea(o)
pixsize = o.pixsize
pixsize = o.optimisation.pixsize
coordoffset = o.borderwidth + pixsize / 2 # -m
res = ceil(o.cutter_diameter / o.pixsize)
res = ceil(o.cutter_diameter / o.optimisation.pixsize)
m = res / 2
t = time.time()
@ -436,13 +480,13 @@ def sampleChunks(o, pathSamples, layers):
if not o.ambient.contains(sgeometry.Point(x, y)):
newsample = (x, y, 1)
else:
if o.use_opencamlib and o.use_exact:
if o.optimisation.use_opencamlib and o.optimisation.use_exact:
z = s[2]
if minz > z:
z = minz
newsample = (x, y, z)
# ampling
elif o.use_exact and not o.use_opencamlib:
elif o.optimisation.use_exact and not o.optimisation.use_opencamlib:
if lastsample is not None: # this is an optimalization,
# search only for near depths to the last sample. Saves about 30% of sampling time.
@ -985,7 +1029,7 @@ def connectChunksLow(chunks, o):
# print('addbetwee')
between = samplePathLow(o, lastch, ch,
False) # other paths either dont use sampling or are sorted before it.
if o.use_opencamlib and o.use_exact and (
if o.optimisation.use_opencamlib and o.optimisation.use_exact and (
o.strategy == 'PARALLEL' or o.strategy == 'CROSS' or o.strategy == 'PENCIL'):
chunks_to_resample.append(
(connectedchunks[-1], len(connectedchunks[-1].points), len(between.points)))
@ -997,7 +1041,7 @@ def connectChunksLow(chunks, o):
lastch = ch
pos = lastch.points[-1]
if o.use_opencamlib and o.use_exact and o.strategy != 'CUTOUT' and o.strategy != 'POCKET':
if o.optimisation.use_opencamlib and o.optimisation.use_exact and o.strategy != 'CUTOUT' and o.strategy != 'POCKET':
oclResampleChunks(o, chunks_to_resample)
return connectedchunks
@ -1296,7 +1340,7 @@ def getAmbient(o):
o.limit_poly = shapely.ops.unary_union(polys)
if o.ambient_cutter_restrict:
o.limit_poly = o.limit_poly.buffer(o.cutter_diameter / 2, resolution=o.circle_detail)
o.limit_poly = o.limit_poly.buffer(o.cutter_diameter / 2, resolution=o.optimisation.circle_detail)
o.ambient = o.ambient.intersection(o.limit_poly)
o.update_ambient_tag = False
@ -1330,7 +1374,7 @@ def getObjectOutline(radius, o, Offset): # FIXME: make this one operation indep
# print(p1.type, len(polygons))
i += 1
if radius > 0:
p1 = p1.buffer(radius * offset, resolution=o.circle_detail, join_style=join, mitre_limit=2)
p1 = p1.buffer(radius * offset, resolution=o.optimisation.circle_detail, join_style=join, mitre_limit=2)
outlines.append(p1)
# print(outlines)
@ -1405,6 +1449,7 @@ def addMachineAreaObject():
o = s.objects['CAM_machine']
else:
oldunits = s.unit_settings.system
oldLengthUnit = s.unit_settings.length_unit
# need to be in metric units when adding machine mesh object
# in order for location to work properly
s.unit_settings.system = 'METRIC'
@ -1431,6 +1476,7 @@ def addMachineAreaObject():
o.hide_select = True
# o.select = False
s.unit_settings.system = oldunits
s.unit_settings.length_unit = oldLengthUnit
# bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
@ -1440,7 +1486,6 @@ def addMachineAreaObject():
# else:
# bpy.context.scene.objects.active = None
def addMaterialAreaObject():
s = bpy.context.scene
operation = s.cam_operations[s.cam_active_operation]
@ -1670,8 +1715,8 @@ def reload_pathss(o):
# print('sleep')
# time.sleep(1)
o.warnings = d['warnings']
o.duration = d['duration']
o.info.warnings = d['warnings']
o.info.duration = d['duration']
verts = d['path']
edges = []