def test_extract_paths_inside_group_keeping_nested_transform(self): #pylint: disable=invalid-name """ Tests that extracted path from elements with nested transformation works correctly """ root = etree.Element("root") group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g", {'transform': "translate(10,20)"}) subgroup = etree.SubElement(group, "{http://www.w3.org/2000/svg}g", {'transform': "matrix(1,2,3,4,5,6)"}) etree.SubElement(subgroup, "{http://www.w3.org/2000/svg}path", {'d': "M 10,20 L 50,30 L 20,70"}) extractor = PathsExtractor([group], to_mm, "wId") extractor.extract() self.assertEqual(extractor.paths()[0], [(85.0, 126.0), (155.0, 246.0), (245.0, 346.0)])
def test_use_parent_transform_for_single_path_with_multiple_parents(self): #pylint: disable=invalid-name """ Tests that extracted path takes all the ancestors transformations into account """ root = etree.Element("root", {'transform': "scale(10)"}) parent = etree.SubElement(root, "{http://www.w3.org/2000/svg}g", {'transform': "translate(10,20)"}) element = etree.SubElement(parent, "{http://www.w3.org/2000/svg}path", { 'transform': "matrix(1,2,3,4,5,6)", 'd': "M 10,20 L 50,30 L 20,70" }) extractor = PathsExtractor([element], to_mm, "wId") extractor.extract() self.assertEqual(extractor.paths()[0], [(850.0, 1260.0), (1550.0, 2460.0), (2450.0, 3460.0)])
def effect(self): """ Main function """ # A function to convert to millimiters to_mm = lambda value: self.uutounit(value, 'mm') # A function to convert to user units. This must be used to write units in the svg to_uu = lambda value: self.unittouu(str(value) + "mm") # Draw the working area working_area_generator = WorkingAreaGenerator(to_uu, WORKING_AREA_ID) working_area_generator.set_size(self.options.dim_x, self.options.dim_y) working_area_generator.upsert(self.document.getroot()) if not self.options.ids: # print info and exit # Using error message even if this it not really an error... inkex.debug(_(("No path was seletect, only the working area was generated. Now draw a " "path inside the working area and select it to generate the g-code"))) else: # Extracting paths in machine coordinates paths_extractor = PathsExtractor(self.selected.values(), to_mm, WORKING_AREA_ID, FlattenBezier(FLATNESS)) paths_extractor.extract() # Generate tool positions and orientations tool_path_generator = EngravingToolPathsGenerator(paths_extractor.paths(), self.options.depth_z, MIN_DISTANCE, DISCRETIZATION_STEP) tool_path_generator.generate() # Generating g-code gcode_generator = EngravingGCodeGenerator(tool_path_generator.paths(), MM_PER_DEGREE, SAFE_Z, SMALL_DISTANCE, SMALL_ANGLE) gcode_generator.generate() # Writing to file filename = base_filename(self.options.filename, self.gcode_file_path) + ".gcode" write_file(filename, lambda f: f.write(gcode_generator.gcode())) inkex.debug(_("The generate g-code has been save to ") + filename)
def test_closed_paths_with_multiple_paths(self): #pylint: disable=invalid-name """ Tests that closed paths are extracted correctly also when multiple subpaths are present """ root = etree.Element("root") element = etree.SubElement( root, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,490 Z M 100,100 L 200,200"}) extractor = PathsExtractor([element], to_mm, "wId") extractor.extract() self.assertEqual(len(extractor.paths()), 2) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0), (70.0, 800.0)]) self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0)])
def test_extract_multiple_paths_when_lines_interrupted(self): #pylint: disable=invalid-name """ Tests that multiple paths are generated when svg path is not continuos """ root = etree.Element("root") element = etree.SubElement( root, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,490 M 100,100 L 200,200"}) extractor = PathsExtractor([element], to_mm, "wId") extractor.extract() self.assertEqual(len(extractor.paths()), 2) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)]) self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0)])
def test_extract_svg_path_with_only_straight_lines(self): #pylint: disable=invalid-name """ Tests that svg paths are correctly extracted """ root = etree.Element("root") element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,490"}) extractor = PathsExtractor([element], to_mm, "wId") extractor.extract() self.assertEqual(len(extractor.paths()), 1) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])
def test_closed_paths(self): """ Tests that closed paths are extracted correctly by repeating the first point at the end """ root = etree.Element("root") group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g") etree.SubElement(group, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,490 Z"}) extractor = PathsExtractor([group], to_mm, "wId") extractor.extract() self.assertEqual(len(extractor.paths()), 1) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0), (70.0, 800.0)])
def test_extract_path_inside_groups(self): #pylint: disable=invalid-name """ Tests that a path inside a group is correctly extracted """ root = etree.Element("root") group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g") etree.SubElement(group, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,490"}) extractor = PathsExtractor([group], to_mm, "wId") extractor.extract() self.assertEqual(len(extractor.paths()), 1) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])
def test_automatically_close_open_paths_does_nothing_if_paths_closed(self): #pylint: disable=invalid-name """ Tests that nothing changes if closing paths is requested but paths are closed """ root = etree.Element("root") element = etree.SubElement( root, "{http://www.w3.org/2000/svg}path", { 'd': ("M 70,800 l 125,-300 l 60,490 Z " "M 100,100 L 200,200 L 100,100") }) extractor = PathsExtractor([element], to_mm, "wId") extractor.extract() self.assertEqual(len(extractor.paths()), 2) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0), (70.0, 800.0)]) self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0), (100.0, 100.0)])
def test_extract_paths_inside_groups_and_outside(self): #pylint: disable=invalid-name """ Tests that a mix of paths inside groups and outside are correctly extracted """ root = etree.Element("root") element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,390"}) group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g") etree.SubElement(group, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,490"}) etree.SubElement(group, "{http://www.w3.org/2000/svg}path", {'d': "M 60,700 l 127,-500 l 70,900"}) extractor = PathsExtractor([element, group], to_mm, "wId") extractor.extract() self.assertEqual(len(extractor.paths()), 3) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 890.0)]) self.assertEqual(extractor.paths()[1], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)]) self.assertEqual(extractor.paths()[2], [(60.0, 700.0), (187.0, 200.0), (257.0, 1100.0)])
def test_automatically_close_open_paths(self): #pylint: disable=invalid-name """ Tests that open paths are automatically closed if requested """ root = etree.Element("root") element = etree.SubElement( root, "{http://www.w3.org/2000/svg}path", {'d': "M 70,800 l 125,-300 l 60,490 M 100,100 L 200,200"}) extractor = PathsExtractor([element], to_mm, "wId", auto_close_path=True) extractor.extract() self.assertEqual(len(extractor.paths()), 2) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0), (70.0, 800.0)]) self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0), (100.0, 100.0)])
def test_use_parent_transform_for_multiple_path(self): #pylint: disable=invalid-name """ Tests that extracted path takes the parent transformation into account """ root1 = etree.Element("root", {'transform': "translate(10,20)"}) element1 = etree.SubElement(root1, "{http://www.w3.org/2000/svg}path", { 'transform': "matrix(1,2,3,4,5,6)", 'd': "M 10,20 L 50,30 L 20,70" }) root2 = etree.Element("root", {'transform': "translate(5,10)"}) element2 = etree.SubElement(root2, "{http://www.w3.org/2000/svg}path", { 'transform': "matrix(1,2,3,4,5,6)", 'd': "M 10,20 L 50,30 L 20,70" }) extractor = PathsExtractor([element1, element2], to_mm, "wId") extractor.extract() self.assertEqual(extractor.paths()[0], [(85.0, 126.0), (155.0, 246.0), (245.0, 346.0)]) self.assertEqual(extractor.paths()[1], [(80.0, 116.0), (150.0, 236.0), (240.0, 336.0)])
def test_flatten_path_if_requested(self): """ Tests that path is flattened if the constructor flatten parameter is not None """ root = etree.Element("root") element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path", {'d': "dummy value to check"}) class FlattenMock: #pylint: disable=missing-docstring,too-few-public-methods def __init__(self, test): self.test = test def __call__(self, curve): #pylint: disable=missing-docstring self.test.assertEqual(curve, "dummy value to check") return [('M', (70.0, 800.0)), ('L', (195.0, 500.0)), ('L', (255.0, 990.0))] extractor = PathsExtractor([element], to_mm, "wId", FlattenMock(self)) extractor.extract() self.assertEqual(len(extractor.paths()), 1) self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])
def effect(self): """ Main function """ # First of all generating the machine instance and checking piece dimensions fit machine = machine_factory(self.options.machine_type) if machine: valid_dimensions = machine.piece_dimensions_allowed( self.options.dim_x, self.options.dim_y) if not valid_dimensions: raise InvalidWorkpieceDimensions(machine.working_area_width(), machine.working_area_height()) # A function to convert to millimiters to_mm = lambda value: self.uutounit(value, 'mm') # A function to convert to user units. This must be used to write units in the svg to_uu = lambda value: self.unittouu(str(value) + "mm") # Draw the working area working_area_generator = WorkingAreaGenerator(to_uu, WORKING_AREA_ID) working_area_generator.set_size(self.options.dim_x, self.options.dim_y) working_area_generator.upsert(self.document.getroot()) if not self.options.ids: # print info and exit inkex.debug( _(("No path was seletect, only the working area was generated. Now draw a " "path inside the working area and select it to generate the g-code" ))) else: # Extracting paths in machine coordinates paths_extractor = PathsExtractor( self.selected.values(), to_mm, WORKING_AREA_ID, FlattenBezier(self.options.flatness), self.options.auto_close_path) paths_extractor.extract() # The border to use. This is None if no border is requested. If border is present, also # draws it border = None if self.options.square: border = Border(paths_extractor.paths(), self.options.margin) painter = BorderPainter(border) painter.paint(working_area_generator) # Joining paths. This will also check that all paths are closed paths_joiner = PathsJoiner(paths_extractor.paths(), CLOSE_DISTANCE) paths_joiner.unite() # Generate tool positions tool_path_generator = CuttingToolPathsGenerator( paths_joiner.union_path(), CLOSE_DISTANCE, border) tool_path_generator.generate() # The object drawing the tool path painter = ToolPathPainter(tool_path_generator.path()) # Draw tool path on original svg if requested if self.options.draw_toolpath: painter.paint(working_area_generator.get_element(), working_area_generator.get_factor(), "255,0,0") # Generating g-code gcode_generator = CuttingGCodeGenerator(tool_path_generator.path(), self.options.speed) gcode_generator.generate() # Computing information about path generic_filename = base_filename(self.options.shapename, self.gcode_file_path) info = PathInfo(tool_path_generator.path(), self.options, generic_filename) # Writing gcode to file write_file( os.path.join(self.gcode_file_path, info.gcode_filename()), lambda f: f.write(gcode_generator.gcode())) # Writing svg to file doc = generate_path_svg(painter) write_file(os.path.join(self.gcode_file_path, info.svg_filename()), lambda f: doc.write(f)) # Writing metainfo to file write_file( os.path.join(self.gcode_file_path, info.metainfo_filename()), lambda f: f.write(json.dumps(info.metainfo(), indent=2))) message = (_("The generate g-code has been saved to ") + info.gcode_filename() + _(". Estimated working time: ") + str(info.working_time_min()) + _(" minutes")) if not info.is_path_inside_workpiece(): message += _( ". WARNING: some points are outside the workpiece") inkex.debug(message)