Beispiel #1
0
    def draw(self, vsk: vsketch.Vsketch) -> None:
        vsk.size("a6", landscape=False)

        doc = execute(
            "msrandom -ms -q 0.01mm -n 5 10 module_sets/ms_neon_v1 linemerge -t 0.5mm "
            "layout -m 2cm a6 ")

        for line in doc.layers[1]:

            line = vp.interpolate(line, step=0.1)

            if not self.liquify:
                vsk.polygon(line)
                continue

            perlin_x = vsk.noise(self.freq * line.real,
                                 self.freq * line.imag,
                                 grid_mode=False)
            perlin_y = vsk.noise(
                self.freq * line.real,
                self.freq * line.imag,
                1000 * np.ones_like(line.real),
                grid_mode=False,
            )
            line += self.ampl * 2.0 * ((perlin_x - 0.5) +
                                       (perlin_y - 0.5) * 1j)

            vsk.polygon(line)
            vsk.vpype("linesimplify -t 0.005mm")
Beispiel #2
0
def execute_single_line(pipeline: str, line: vp.LineLike) -> vp.LineCollection:
    """Execute a pipeline on a single line. The pipeline is expected to remain single layer.

    Returns:
        the layer 1's LineCollection
    """
    doc = execute(pipeline, vp.Document(vp.LineCollection([line])))
    assert len(doc.layers) == 1
    return doc.layers[1]
Beispiel #3
0
def test_ldelete_prob(big_doc):
    # test for a bunch of seeds without making the test fragile
    for seed in range(100):
        random.seed(seed)

        doc = copy.deepcopy(big_doc)
        doc = execute("ldelete --prob 0.5 1", doc)

        assert math.isclose(len(doc.layers[1]) / 1000, 0.5, abs_tol=0.1)
Beispiel #4
0
def test_text_command_wrap(font_name, options):
    doc = execute(f"text -f {font_name} -w 350 {options} '{LOREM}'")

    bounds = doc[1].bounds()
    assert bounds is not None
    assert -2.0 <= bounds[0] <= 3.0
    if options == "-j":
        assert bounds[2] == pytest.approx(350.0)
    else:
        assert bounds[2] <= 350.0
Beispiel #5
0
def test_lmove(big_doc):
    doc = execute("lmove 1 2", big_doc)

    assert 1 not in doc.layers
    assert len(doc.layers[2]) == 1000
Beispiel #6
0
def test_lcopy_prob_zero(big_doc):
    doc = execute("lcopy --prob 0. 1 2", big_doc)

    assert len(doc.layers[1]) == 1000
    assert 2 not in doc.layers
Beispiel #7
0
def test_lcopy_prob_one(big_doc):
    doc = execute("lcopy --prob 1. 1 2", big_doc)

    assert len(doc.layers[1]) == 1000
    assert len(doc.layers[2]) == 1000
Beispiel #8
0
def test_lcopy(big_doc):
    doc = execute("lcopy 1 2", big_doc)

    assert len(doc.layers[1]) == 1000
    assert len(doc.layers[2]) == 1000
Beispiel #9
0
def test_ldelete_prob_zero(big_doc):
    doc = execute("ldelete --prob 0. 1", big_doc)

    assert len(doc.layers[1]) == 1000
Beispiel #10
0
    def effect(self):
        lc = vpype.LineCollection() # create a new array of LineStrings consisting of Points. We convert selected paths to polylines and grab their points
        elementsToWork = [] # we make an array of all collected nodes to get the boundingbox of that array. We need it to place the vpype converted stuff to the correct XY coordinates
          
        applyTransformAvailable = False
        
        # at first we apply external extension
        try:
            sys.path.append("..") # add parent directory to path to allow importing applytransform (vpype extension is encapsulated in sub directory)
            import applytransform
            applyTransformAvailable = True
        except Exception as e:
            # inkex.utils.debug(e)
            inkex.utils.debug("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping this step")

        def flatten(node):
            path = node.path.to_superpath()
            bezier.cspsubdiv(path, self.options.flatness)
            newpath = []
            for subpath in path:
                first = True
                for csp in subpath:
                    cmd = 'L'
                    if first:
                        cmd = 'M'
                    first = False
                    newpath.append([cmd, [csp[1][0], csp[1][1]]])
            node.path = newpath

        # flatten the node's path to linearize, split up the path to it's subpaths (break apart) and add all points to the vpype lines collection
        def convertPath(node, nodes = None):
            if nodes is None:
                nodes = []
            if node.tag == inkex.addNS('path','svg'):
                nodes.append(node)
                if self.options.flattenbezier is True:
                    flatten(node)

                raw = node.path.to_arrays()
                subPaths, prev = [], 0
                for i in range(len(raw)): # Breaks compound paths into simple paths
                    if raw[i][0] == 'M' and i != 0:
                        subPaths.append(raw[prev:i])
                        prev = i
                subPaths.append(raw[prev:])
                for subPath in subPaths:
                    points = []
                    for csp in subPath:
                        if len(csp[1]) > 0: #we need exactly two points per straight line segment
                            points.append(Point(round(csp[1][0], self.options.decimals), round(csp[1][1], self.options.decimals)))
                    if  subPath[-1][0] == 'Z' or subPath[0][1] == subPath[-1][1]:  #check if path is closed by Z or first pont == last point
                        points.append(Point(round(subPath[0][1][0], self.options.decimals), round(subPath[0][1][1], self.options.decimals))) #if closed, we add the first point again
                    lc.append(LineString(points))
                      
            children = node.getchildren()
            if children is not None: 
                for child in children:
                    convertPath(child, nodes)
            return nodes

        doc = None #create a vpype document
        
        '''
        if 'paths' we process paths only. Objects like rectangles or strokes like polygon have to be converted before accessing them
        if 'layers' we can process all layers in the complete document
        '''
        if self.options.input_handling == "paths":
            # getting the bounding box of the current selection. We use to calculate the offset XY from top-left corner of the canvas. This helps us placing back the elements
            input_bbox = None
            if self.options.apply_transformations is True and applyTransformAvailable is True:
                '''
                we need to apply transfoms to the complete document even if there are only some single paths selected. 
                If we apply it to selected nodes only the parent groups still might contain transforms. 
                This messes with the coordinates and creates hardly controllable behaviour
                '''
                applytransform.ApplyTransform().recursiveFuseTransform(self.document.getroot())
            if len(self.svg.selected) == 0:
                elementsToWork = convertPath(self.document.getroot())
                for element in elementsToWork:
                    input_bbox += element.bounding_box()      
            else:
                elementsToWork = None
                for element in self.svg.selected.values():
                    elementsToWork = convertPath(element, elementsToWork)
                #input_bbox = inkex.elements._selected.ElementList.bounding_box(self.svg.selected) # get BoundingBox for selection
                input_bbox = self.svg.selection.bounding_box() # get BoundingBox for selection
            if len(lc) == 0:
                inkex.errormsg('Selection appears to be empty or does not contain any valid svg:path nodes. Try to cast your objects to paths using CTRL + SHIFT + C or strokes to paths using CTRL + ALT+ C')
                return  
            # find the first object in selection which has a style attribute (skips groups and other things which have no style)
            firstElementStyle = None
            for element in elementsToWork:
                if element.attrib.has_key('style'):
                    firstElementStyle = element.get('style')
            doc = vpype.Document(page_size=(input_bbox.width + input_bbox.left, input_bbox.height + input_bbox.top)) #create new vpype document     
            doc.add(lc, layer_id=None) # we add the lineCollection (converted selection) to the vpype document
            
        elif self.options.input_handling == "layers":
            doc = vpype.read_multilayer_svg(self.options.input_file, quantization = self.options.flatness, crop = False, simplify = self.options.simplify, parallel = self.options.parallel, default_width = self.document.getroot().get('width'), default_height = self.document.getroot().get('height'))

            for element in self.document.getroot().xpath("//svg:g", namespaces=inkex.NSS): #all groups/layers
                elementsToWork.append(element)

        tooling_length_before = doc.length()
        traveling_length_before = doc.pen_up_length()
        
        # build and execute the conversion command
        # the following code block is not intended to sum up the commands to build a series (pipe) of commands!
        ##########################################
        
        # Line Sorting
        if self.options.linesort is True:
            command = "linesort "
            if self.options.linesort_no_flip is True:
                command += " --no-flip"

        # Line Merging
        if self.options.linemerge is True:     
            command = "linemerge --tolerance " + str(self.options.linemerge_tolerance)
            if self.options.linemerge_no_flip is True:
                command += " --no-flip"
 
        # Trimming
        if self.options.trim is True:     
            command = "trim " + str(self.options.trim_x_margin) + " " + str(self.options.trim_y_margin)
 
        # Relooping
        if self.options.reloop is True:     
            command = "reloop --tolerance " + str(self.options.reloop_tolerance)
 
        # Multipass
        if self.options.multipass is True:     
            command = "multipass --count " + str(self.options.multipass_count)
 
        # Filter
        if self.options.filter is True:     
            command = "filter --tolerance " + str(self.options.filter_tolerance)
            if self.options.filter_min_length_enabled is True:
                command += " --min-length " + str(self.options.filter_min_length)
            if self.options.filter_max_length_enabled is True:
                command += " --max-length " + str(self.options.filter_max_length)
            if self.options.filter_closed is True and self.options.filter_not_closed is False:
                command += " --closed"
            if self.options.filter_not_closed is True and self.options.filter_closed is False:
                command += " --not-closed"
            if self.options.filter_closed is False and \
                self.options.filter_not_closed is False and \
                self.options.filter_min_length_enabled is False and \
                self.options.filter_max_length_enabled is False:
                inkex.errormsg('No filters to apply. Please select at least one filter.')
                return

        # Plugin Occult
        if self.options.plugin_occult is True:     
            command = "occult --tolerance " + str(self.options.plugin_occult_tolerance)
            if self.options.plugin_occult_keepseparatelayer is True:
                command += " --keep-occulted"

        # Split All
        if self.options.splitall is True:     
            command = " splitall"

        # Free Mode
        if self.options.freemode is True:
            command = ""
            if self.options.freemode_cmd1_enabled is True:
                command += " " + self.options.freemode_cmd1.strip()
            if self.options.freemode_cmd2_enabled is True:
                command += " " + self.options.freemode_cmd2.strip()
            if self.options.freemode_cmd3_enabled is True:
                command += " " + self.options.freemode_cmd3.strip()
            if self.options.freemode_cmd4_enabled is True:
                command += " " + self.options.freemode_cmd4.strip()
            if self.options.freemode_cmd5_enabled is True:
                command += " " + self.options.freemode_cmd5.strip()
            if self.options.freemode_cmd1_enabled is False and \
                self.options.freemode_cmd2_enabled is False and \
                self.options.freemode_cmd3_enabled is False and \
                self.options.freemode_cmd4_enabled is False and \
                self.options.freemode_cmd5_enabled is False:
                inkex.utils.debug("Warning: empty vpype pipeline. With this you are just getting read-write layerset/lineset.")
            else:
                if self.options.freemode_show_cmd is True:
                    inkex.utils.debug("Your command pipe will be the following:")
                    inkex.utils.debug(command)

        # inkex.utils.debug(command)
        try:
            doc = execute(command, doc)
        except Exception as e:
            inkex.utils.debug("Error in vpype:" + str(e))
            return

        ##########################################
        
        tooling_length_after = doc.length()
        traveling_length_after = doc.pen_up_length()        
        if tooling_length_before > 0:
            tooling_length_saving = (1.0 - tooling_length_after / tooling_length_before) * 100.0
        else:
            tooling_length_saving = 0.0            
        if traveling_length_before > 0:
            traveling_length_saving = (1.0 - traveling_length_after / traveling_length_before) * 100.0
        else:
            traveling_length_saving = 0.0  
        if self.options.output_stats is True:
            inkex.utils.debug('Total tooling length before vpype conversion: '   + str('{:0.2f}'.format(tooling_length_before))   + ' mm')
            inkex.utils.debug('Total traveling length before vpype conversion: ' + str('{:0.2f}'.format(traveling_length_before)) + ' mm')
            inkex.utils.debug('Total tooling length after vpype conversion: '    + str('{:0.2f}'.format(tooling_length_after))    + ' mm')
            inkex.utils.debug('Total traveling length after vpype conversion: '  + str('{:0.2f}'.format(traveling_length_after))  + ' mm')
            inkex.utils.debug('Total tooling length optimized: '   + str('{:0.2f}'.format(tooling_length_saving))   + ' %')
            inkex.utils.debug('Total traveling length optimized: ' + str('{:0.2f}'.format(traveling_length_saving)) + ' %')
         
        if tooling_length_after == 0:
            inkex.errormsg('No lines left after vpype conversion. Conversion result is empty. Cannot continue')
            return
         
        # show the vpype document visually
        if self.options.output_show:
            warnings.filterwarnings("ignore") # workaround to suppress annoying DeprecationWarning
            # vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, pen_width=0.1, pen_opacity=1.0, argv=None)
            vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, argv=None) # https://vpype.readthedocs.io/en/stable/api/vpype_viewer.ViewMode.html
            warnings.filterwarnings("default") # reset warning filter
            exit(0) #we leave the code loop because we only want to preview. We don't want to import the geometry
          
        # save the vpype document to new svg file and close it afterwards
        output_file = self.options.input_file + ".vpype.svg"
        output_fileIO = open(output_file, "w", encoding="utf-8")
        #vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer', single_path = True)       
        vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer')       
        #vpype.write_svg(output_fileIO, doc, page_size=(self.svg.unittouu(self.document.getroot().get('width')), self.svg.unittouu(self.document.getroot().get('height'))), center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer')       
        output_fileIO.close()
        
        # convert vpype polylines/lines/polygons to regular paths again. We need to use "--with-gui" to respond to "WARNING: ignoring verb FileSave - GUI required for this verb."
        if self.options.strokes_to_paths is True:
            cli_output = inkscape(output_file, "--with-gui", actions="EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit")
            if len(cli_output) > 0:
                self.debug(_("Inkscape returned the following output when trying to run the vpype object to path back-conversion:"))
                self.debug(cli_output)
        
        # this does not work because line, polyline and polygon have no base class to execute replace_with
        #if self.options.strokes_to_paths is True:
        #    for lineLayer in lineLayers:
        #        for element in lineLayer:
        #                element.replace_with(element.to_path_element())
               
        # parse the SVG file
        try:
            stream = open(output_file, 'r')
        except FileNotFoundError as e:
            inkex.utils.debug("There was no SVG output generated by vpype. Cannot continue")
            exit(1)
        p = etree.XMLParser(huge_tree=True)
        import_doc = etree.parse(stream, parser=etree.XMLParser(huge_tree=True))
        stream.close()
          
        # handle pen_up trajectories (travel lines)
        trajectoriesLayer = import_doc.getroot().xpath("//svg:g[@id='pen_up_trajectories']", namespaces=inkex.NSS)
        if self.options.output_trajectories is True:
            if len(trajectoriesLayer) > 0:
                trajectoriesLayer[0].set('style', 'stroke:#0000ff;stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.trajectories_stroke_width))
                trajectoriesLayer[0].attrib.pop('stroke') # remove unneccesary stroke attribute
                trajectoriesLayer[0].attrib.pop('fill') # remove unneccesary fill attribute
        else:
            if len(trajectoriesLayer) > 0:
                trajectoriesLayer[0].delete()
   
        lineLayers = import_doc.getroot().xpath("//svg:g[not(@id='pen_up_trajectories')]", namespaces=inkex.NSS) #all layer except the pen_up trajectories layer
        if self.options.use_style_of_first_element is True and self.options.input_handling == "paths" and firstElementStyle is not None:
            
            # if we remove the fill property and use "Use style of first element in layer" the conversion will just crash with an unknown reason
            #declarations = firstElementStyle.split(';')
            #for i, decl in enumerate(declarations):
            #    parts = decl.split(':', 2)
            #    if len(parts) == 2:
            #        (prop, val) = parts
            #        prop = prop.strip().lower()
            #        #if prop == 'fill':
            #        #   declarations[i] = prop + ':none'   
            for lineLayer in lineLayers:
                #lineLayer.set('style', ';'.join(declarations))
                lineLayer.set('style', firstElementStyle)
                lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute
                lineLayer.attrib.pop('fill') # remove unneccesary fill attribute

        else:
            for lineLayer in lineLayers:          
                if lineLayer.attrib.has_key('stroke'):
                    color = lineLayer.get('stroke')
                    lineLayer.set('style', 'stroke:' + color + ';stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.lines_stroke_width))
                    lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute
                    lineLayer.attrib.pop('fill') # remove unneccesary fill attribute

        import_viewBox = import_doc.getroot().get('viewBox').split(" ")
        self_viewBox = self.document.getroot().get('viewBox').split(" ")
        scaleX = self.svg.unittouu(self_viewBox[2]) / self.svg.unittouu(import_viewBox[2])
        scaleY = self.svg.unittouu(self_viewBox[3]) / self.svg.unittouu(import_viewBox[3])

        for element in import_doc.getroot().iter("{http://www.w3.org/2000/svg}g"):
            self.document.getroot().append(element)
            if self.options.input_handling == "layers":
                element.set('transform', 'scale(' + str(scaleX) + ',' + str(scaleY) + ')') #imported groups need to be transformed. Or they have wrong size. Reason: different viewBox sizes/units in namedview definitions
                if self.options.apply_transformations is True and applyTransformAvailable is True: #we apply the transforms directly after adding them
                    applytransform.ApplyTransform().recursiveFuseTransform(element) 

        # Delete the temporary file again because we do not need it anymore
        if os.path.exists(output_file):
            os.remove(output_file)
            
        # Remove selection objects to do a real replace with new objects from vpype document
        if self.options.keep_objects is False:
            for element in elementsToWork:
                element.delete()
Beispiel #11
0
def test_ldelete(big_doc):
    doc = execute("ldelete 1", big_doc)

    assert len(doc.layers) == 0
Beispiel #12
0
def test_lmove_prob_zero(big_doc):
    doc = execute("lmove --prob 0. 1 2", big_doc)

    assert 2 not in doc.layers
    assert len(doc.layers[1]) == 1000
Beispiel #13
0
def test_text_command_empty():
    doc = execute("text ''")
    assert doc.is_empty()
Beispiel #14
0
def run(args):
    saver = ReproSaver('star_results')
    saver.seed()

    if args.rnd:
        description = random_star()
        print(f"description: {description}")
        layers = construct_star(description)
    else:
        N_star = 12
        R = 600
        paths = []
        start_fractions = np.linspace(0.4, 1, 25)
        end_fractions = reversed(start_fractions)
        for start_fraction, end_fraction in zip(start_fractions,
                                                end_fractions):
            start_r = R * start_fraction
            end_r = R * end_fraction
            paths.extend(star_line(N_star, start_r, end_r, 1))

        # vlastovka
        start_fractions = np.geomspace(0.4, 0.65, 20)
        end_fractions = reversed(np.geomspace(0.3, 0.9, 20))
        for start_fraction, end_fraction in zip(start_fractions,
                                                end_fractions):
            start_r = R * start_fraction
            end_r = R * end_fraction
            paths.extend(star_line(N_star, start_r, end_r, 1))

        # stred
        start_fractions = rev_geomspace(0.1, 1, 30)
        end_fractions = start_fractions
        # end_fractions = np.linspace(0.1, 1, 30)
        for start_fraction, end_fraction in zip(start_fractions,
                                                end_fractions):
            start_r = R * start_fraction
            end_r = R * end_fraction
            paths.extend(star_line(N_star, start_r, end_r, 5, mirror=False))

        # stred 2
        start_fractions = rev_geomspace(0.13, 0.7, 20)
        end_fractions = start_fractions
        # end_fractions = np.linspace(0.1, 1, 30)
        for start_fraction, end_fraction in zip(start_fractions,
                                                end_fractions):
            start_r = R * start_fraction
            end_r = R * end_fraction
            paths.extend(star_line(N_star, start_r, end_r, 5, mirror=False))

        paths.extend(star_line(N_star, R, R, 6, mirror=False))
        layers = {'black': paths}

    document = to_vpype_document(layers)
    document = vpype_cli.execute("linemerge linesort", document)
    layers = from_vpype_document(document)
    if not args.nosave:
        saver.add_svg(layers)
    with open('/tmp/stars.svg', 'w') as fout:
        vpype.write_svg(fout, document, color_mode='layer')
    if not args.novis:
        vpype_viewer.show(document)
    return 0
Beispiel #15
0
def test_lmove_prob_one(big_doc):
    doc = execute("lmove --prob 1. 1 2", big_doc)

    assert 1 not in doc.layers
    assert len(doc.layers[2]) == 1000
Beispiel #16
0
    def effect(self):
        #get input file and copy it to some new temporary directory
        inputfile = self.options.inputfile
        if not os.path.exists(inputfile):
            inkex.utils.debug(
                "The input file does not exist. Please select a *.dxf or *.dwg file and try again."
            )
            exit(1)
        temp_input_dir = os.path.join(tempfile.gettempdir(), "dxfdwg_input")
        shutil.rmtree(temp_input_dir, ignore_errors=True
                      )  #remove the input directory before doing new job
        if not os.path.exists(temp_input_dir):
            os.mkdir(temp_input_dir)  #recreate blank dir
        shutil.copy2(
            inputfile, os.path.join(
                temp_input_dir,
                Path(inputfile).name))  # complete target filename given

        #Prepapre output conversion
        outputfilebase = os.path.splitext(os.path.basename(inputfile))[0]
        inputfile_ending = os.path.splitext(os.path.basename(inputfile))[1]
        temp_output_dir = os.path.join(tempfile.gettempdir(), "dxfdwg_output")
        shutil.rmtree(temp_output_dir, ignore_errors=True
                      )  #remove the output directory before doing new job
        if not os.path.exists(temp_output_dir):
            os.mkdir(temp_output_dir)

        #Prepare some more options for proceeding
        autocad_version = self.options.oda_outputformat.split("_")[0]
        autocad_format = self.options.oda_outputformat.split("_")[1]
        self.options.oda_audit_repair = "1" if self.options.oda_audit_repair else "0"  #overwrite string bool with int value
        entityspace = []
        if self.options.allentities or self.options.THREE_DFACE:
            entityspace.append("3DFACE")
        if self.options.allentities or self.options.ARC:
            entityspace.append("ARC")
        if self.options.allentities or self.options.BLOCK:
            entityspace.append("BLOCK")
        if self.options.allentities or self.options.CIRCLE:
            entityspace.append("CIRCLE")
        if self.options.allentities or self.options.ELLIPSE:
            entityspace.append("ELLIPSE")
        if self.options.allentities or self.options.LINE:
            entityspace.append("LINE")
        if self.options.allentities or self.options.LWPOLYLINE:
            entityspace.append("LWPOLYLINE")
        if self.options.allentities or self.options.POINT:
            entityspace.append("POINT")
        if self.options.allentities or self.options.POLYLINE:
            entityspace.append("POLYLINE")
        if self.options.allentities or self.options.POP_TRAFO:
            entityspace.append("POP_TRAFO")
        if self.options.allentities or self.options.SEQEND:
            entityspace.append("SEQEND")
        if self.options.allentities or self.options.SOLID:
            entityspace.append("SOLID")
        if self.options.allentities or self.options.SPLINE:
            entityspace.append("SPLINE")
        if self.options.allentities or self.options.TABLE:
            entityspace.append("TABLE")
        if self.options.allentities or self.options.VERTEX:
            entityspace.append("VERTEX")
        if self.options.allentities or self.options.VIEWPORT:
            entityspace.append("VIEWPORT")

        if self.options.allentities or self.options.THREE_DSOLID:
            entityspace.append("3DSOLID")
        if self.options.allentities or self.options.ATTRIB:
            entityspace.append("ATTRIB")
        if self.options.allentities or self.options.BODY:
            entityspace.append("BODY")
        if self.options.allentities or self.options.ARC_DIMENSION:
            entityspace.append("ARC_DIMENSION")
        if self.options.allentities or self.options.HATCH:
            entityspace.append("HATCH")
        if self.options.allentities or self.options.IMAGE:
            entityspace.append("IMAGE")
        if self.options.allentities or self.options.INSERT:
            entityspace.append("INSERT")
        if self.options.allentities or self.options.MESH:
            entityspace.append("MESH")
        if self.options.allentities or self.options.MTEXT:
            entityspace.append("MTEXT")
        if self.options.allentities or self.options.RAY:
            entityspace.append("RAY")
        if self.options.allentities or self.options.REGION:
            entityspace.append("REGION")
        if self.options.allentities or self.options.SHAPE:
            entityspace.append("SHAPE")
        if self.options.allentities or self.options.SURFACE:
            entityspace.append("SURFACE")
        if self.options.allentities or self.options.TRACE:
            entityspace.append("TRACE")
        if self.options.allentities or self.options.UNDERLAY:
            entityspace.append("UNDERLAY")
        if self.options.allentities or self.options.XLINE:
            entityspace.append("XLINE")

        #ODA to ezdxf mapping
        oda_ezdxf_mapping = []
        oda_ezdxf_mapping.append(
            ["ACAD9", "R12", "AC1004"]
        )  #this mapping is not supported directly. so we use the lowest possible which is R12
        oda_ezdxf_mapping.append(
            ["ACAD10", "R12", "AC1006"]
        )  #this mapping is not supported directly. so we use the lowest possible which is R12
        oda_ezdxf_mapping.append(["ACAD12", "R12", "AC1009"])
        oda_ezdxf_mapping.append(
            ["ACAD13", "R2000", "AC1012"]
        )  #R13 was overwritten by R2000 which points to AC1015 instead of AC1014 (see documentation)
        oda_ezdxf_mapping.append(
            ["ACAD14", "R2000", "AC1014"]
        )  #R14 was overwritten by R2000 which points to AC1015 instead of AC1014 (see documentation)
        oda_ezdxf_mapping.append(["ACAD2000", "R2000", "AC1015"])
        oda_ezdxf_mapping.append(["ACAD2004", "R2004", "AC1018"])
        oda_ezdxf_mapping.append(["ACAD2007", "R2007", "AC1021"])
        oda_ezdxf_mapping.append(["ACAD2010", "R2010", "AC1024"])
        oda_ezdxf_mapping.append(["ACAD2013", "R2013", "AC1027"])
        oda_ezdxf_mapping.append(["ACAD2018", "R2018", "AC1032"])

        ezdxf_autocad_format = None
        for oe in oda_ezdxf_mapping:
            if oe[0] == autocad_version:
                ezdxf_autocad_format = oe[1]
                break
        if ezdxf_autocad_format is None:
            inkex.errormsg("ezdxf conversion format version unknown")

        #Prepare DXF and SVG paths
        dxf_file = os.path.join(temp_output_dir, outputfilebase + ".dxf")
        svg_file = os.path.join(temp_output_dir, outputfilebase + ".svg")

        # Run ODA File Converter
        if self.options.oda_skip_dxf_to_dxf == False or inputfile_ending == ".dwg":
            # Executable test (check for proper configuration by checking mime type. Should return octet stream for a binary executable)
            if os.name == "nt" and "application/octet-stream" not in str(
                    MimeTypes().guess_type(
                        urllib.pathname2url(self.options.oda_fileconverter))):
                inkex.utils.debug(
                    "You selected to use ODA File Converter but it is not configured properly. Check for installation and path location or select 'Skip conversion from DXF to DXF'. You can download ODA Converter from 'https://www.opendesign.com/guestfiles/oda_file_converter'. You need to install it in order to use it."
                )
                exit(1)
            elif os.path.isfile(self.options.oda_fileconverter) == False:
                inkex.utils.debug(
                    "You selected to use ODA File Converter but it is not configured properly. Check for installation and path location or select 'Skip conversion from DXF to DXF'. You can download ODA Converter from 'https://www.opendesign.com/guestfiles/oda_file_converter'. You need to install it in order to use it."
                )
                exit(1)
            else:
                # Build and run ODA File Converter command
                oda_cmd = [
                    self.options.oda_fileconverter, temp_input_dir,
                    temp_output_dir, autocad_version, autocad_format, "0",
                    self.options.oda_audit_repair
                ]
                if os.name == 'nt' and self.options.oda_hidewindow:
                    info = subprocess.STARTUPINFO(
                    )  #hide the ODA File Converter window because it is annoying (does not work for Linux :-()
                    info.dwFlags = 1
                    info.wShowWindow = 0
                    proc = subprocess.Popen(oda_cmd,
                                            startupinfo=info,
                                            shell=False,
                                            stdout=PIPE,
                                            stderr=PIPE)
                else:
                    proc = subprocess.Popen(oda_cmd,
                                            shell=False,
                                            stdout=PIPE,
                                            stderr=PIPE)
                stdout, stderr = proc.communicate()
                if proc.returncode != 0 or (len(stderr) > 0 and
                                            stderr != b"Quit (core dumped)\n"):
                    inkex.utils.debug("ODAFileConverter failed: %d %s %s" %
                                      (proc.returncode, stdout, stderr))
                    if os.name != 'nt':
                        inkex.utils.debug(
                            "If the error message above contains a warning about wrong/missing Qt version please install the required version. You can get the installer from 'https://download.qt.io/archive/qt/'. Sadly you will need to create a free account to install. After installation please configure the shell script '/usr/bin/ODAFileConverter' to add a preceding line with content similar to 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/Qt5.14.2/5.14.2/gcc_64/lib/'."
                        )
                    exit(1)
            # check if ODA converted successfully. This is the case if no error file was created
            oda_errorfile = os.path.join(temp_output_dir,
                                         Path(inputfile).name + ".err")
            if os.path.exists(oda_errorfile):
                inkex.utils.debug(
                    "ODA File Converter failed to process the file. Cannot continue DXF/DWG import. The error message is:"
                )
                errormessage = open(oda_errorfile, 'r')
                errorlines = errormessage.readlines()
                for errorline in errorlines:
                    inkex.utils.debug(errorline.strip())
                errormessage.close()
                exit(1)

        # Do some movings/copies of skipped or processed DXF
        if self.options.oda_skip_dxf_to_dxf:  #if true we need to move the file to simulate "processed"
            shutil.move(os.path.join(temp_input_dir,
                                     Path(inputfile).name),
                        os.path.join(temp_output_dir,
                                     Path(inputfile).name))

        if self.options.oda_keepconverted_dxf:
            shutil.copy2(
                dxf_file,
                os.path.join(os.path.dirname(inputfile), outputfilebase +
                             "_oda.dxf"))  # complete target filename given

        # Preprocessing DXF to DXF (entity filter) by using ezdxf the first time
        if self.options.ezdxf_preprocessing:
            # uniconverter does not handle all entities. we parse the file to exlude stuff which lets uniconverter fail
            dxf = ezdxf.readfile(dxf_file)
            modelspace = dxf.modelspace()
            allowed_entities = []
            # supported entities by UniConverter- impossible: MTEXT TEXT INSERT and a lot of others
            query_string = str(entityspace)[1:-1].replace("'",
                                                          "").replace(",", "")
            if query_string != "":
                for e in modelspace.query(query_string):
                    allowed_entities.append(e)
            #inkex.utils.debug(ezdxf_autocad_format)
            #inkex.utils.debug(self.options.ezdxf_output_version)
            if self.options.ezdxf_output_version == "SAME":
                doc = ezdxf.new(ezdxf_autocad_format)
            else:
                doc = ezdxf.new(
                    self.options.ezdxf_output_version
                )  #use the string values from inx file. Required to match the values from ezdxf library. See Python reference
            msp = doc.modelspace()
            for e in allowed_entities:
                msp.add_foreign_entity(e)
            doc.saveas(dxf_file)
            if self.options.ezdfx_keep_preprocessed:
                shutil.copy2(
                    dxf_file,
                    os.path.join(
                        os.path.dirname(inputfile), outputfilebase +
                        "_ezdxf.dxf"))  # complete target filename given

        # Make SVG from DXF
        if self.options.dxf_to_svg_parser == "sk1":
            if os.name != "nt":
                inkex.utils.debug(
                    "You selected sk1 UniConvertor but you are not running on a Windows platform. On Linux uniconverter 1.1.X can be installed using the now obsolete Python 2.7, but it will not run correctly because you finally will fail at installing liblcms1-dev library on newer systems. That leads to uncompilable sk1libs package. Unfortunately sk1 UniConvertor 2.X does not support dxf format. So please use another DXF to SVG converter."
                )
                exit(1)
            sk1_command_ending = os.path.splitext(
                os.path.splitext(
                    os.path.basename(self.options.sk1_uniconverter))[1])[0]
            if sk1_command_ending != ".cmd":
                inkex.utils.debug(
                    "You selected sk1 UniConverter but it was not configured properly. Check the path to the executable."
                )
                exit(1)
            uniconverter_cmd = [
                self.options.sk1_uniconverter, dxf_file, svg_file
            ]
            #inkex.utils.debug(uniconverter_cmd)
            proc = subprocess.Popen(uniconverter_cmd,
                                    shell=False,
                                    stdout=PIPE,
                                    stderr=PIPE)
            stdout, stderr = proc.communicate()
            if proc.returncode != 0:
                inkex.errormsg("UniConverter failed: %d %s %s" %
                               (proc.returncode, stdout, stderr))
                if self.options.opendironerror:
                    self.openExplorer(temp_output_dir)

        elif self.options.dxf_to_svg_parser == "bjnortier":
            if which("node") is None:
                inkex.utils.debug(
                    "NodeJS executable not found on path. Please check your installation."
                )
                exit(1)
            else:
                bjnortier_cmd = [
                    "node",
                    os.path.join("node_modules", "dxf", "lib", "cli.js"),
                    dxf_file, svg_file
                ]
                #inkex.utils.debug(bjnortier_cmd)
                proc = subprocess.Popen(bjnortier_cmd,
                                        shell=False,
                                        stdout=PIPE,
                                        stderr=PIPE)
                stdout, stderr = proc.communicate()
                if proc.returncode != 0:
                    inkex.errormsg(
                        "node.js DXF to SVG conversion failed: %d %s %s" %
                        (proc.returncode, stdout, stderr))
                    if self.options.opendironerror:
                        self.openExplorer(temp_output_dir)

        elif self.options.dxf_to_svg_parser == "kabeja":
            wd = os.path.join(os.getcwd(), "kabeja")
            #inkex.utils.debug(wd)
            proc = subprocess.Popen(
                "java -jar launcher.jar -nogui -pipeline svg " + dxf_file +
                " " + svg_file,
                cwd=wd,
                shell=True,
                stdout=PIPE,
                stderr=PIPE)
            stdout, stderr = proc.communicate()
            if proc.returncode != 0:
                inkex.errormsg("kabeja failed: %d %s %s" %
                               (proc.returncode, stdout, stderr))
                if self.options.opendironerror:
                    self.openExplorer(temp_output_dir)

        elif self.options.dxf_to_svg_parser == "vpype_dxf":
            try:
                from inkex.command import inkscape
                import vpype
                from vpype_cli import execute
            except Exception as e:
                inkex.errormsg(
                    "Error importing vpype. Did you properly install the vpype and vpype-dxf python modules?"
                )
                exit(1)
            doc = vpype.Document()  #create new vpype document
            command = "dread  --quantization " + str(
                self.options.vpype_quantization)
            if self.options.vpype_simplify is True:
                command += " --simplify"
            if self.options.vpype_parallel is True:
                command += " --parallel"
            #command += " '" + inputfile + "'"
            command += " '" + dxf_file + "'"

            #inkex.errormsg(command)
            doc = execute(command, doc)
            if doc.length() == 0:
                inkex.errormsg(
                    'No lines left after vpype conversion. Conversion result is empty. Cannot continue'
                )
                exit(1)
            # save the vpype document to new svg file and close it afterwards
            output_fileIO = open(svg_file, "w", encoding="utf-8")
            vpype.write_svg(output_fileIO,
                            doc,
                            page_size=None,
                            center=False,
                            source_string='',
                            layer_label_format='%d',
                            show_pen_up=False,
                            color_mode='layer')
            output_fileIO.close()
            # convert vpype polylines/lines/polygons to regular paths again. We need to use "--with-gui" to respond to "WARNING: ignoring verb FileSave - GUI required for this verb."
            cli_output = inkscape(
                svg_file,
                "--with-gui",
                actions=
                "EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit"
            )
            if len(cli_output) > 0:
                self.debug(
                    _("Inkscape returned the following output when trying to run the vpype object to path back-conversion:"
                      ))
                self.debug(cli_output)

        elif self.options.dxf_to_svg_parser == "ezdxf":
            try:
                doc = ezdxf.readfile(dxf_file)
                msp = doc.modelspace()
                #for e in msp: #loop through entities
                #    inkex.errormsg(e)
                #doc.header['$DIMSCALE'] = 0.2 does not apply to the plot :-(
                #inkex.utils.debug(doc.header['$DIMSCALE'])
                #inkex.utils.debug(doc.header['$MEASUREMENT'])
                auditor = doc.audit(
                )  #audit & repair DXF document before rendering
                # The auditor.errors attribute stores severe errors, which *may* raise exceptions when rendering.
                if len(auditor.errors) == 0:
                    fig = plt.figure()
                    ax = plt.axes([0., 0., 1., 1.], xticks=[], yticks=[])
                    #ax = plt.axes([0., 0., 1., 1.], frameon=False, xticks=[], yticks=[])
                    ax.patches = []
                    #plt.axis('off')
                    plt.margins(0, 0)
                    plt.gca().xaxis.set_major_locator(plt.NullLocator())
                    plt.gca().yaxis.set_major_locator(plt.NullLocator())
                    plt.subplots_adjust(top=1,
                                        bottom=0,
                                        right=1,
                                        left=0,
                                        hspace=0,
                                        wspace=0)
                    out = MatplotlibBackend(fig.add_axes(ax))
                    Frontend(RenderContext(doc),
                             out).draw_layout(msp, finalize=True)
                    #plt.show()
                    #fig.savefig(os.path.join(temp_output_dir, outputfilebase + ".png"), dpi=300)
                    fig.savefig(
                        svg_file
                    )  #see https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.savefig.html
            except IOError:
                inkex.errormsg("Not a DXF file or a generic I/O error.")
                exit(1)
            except ezdxf.DXFStructureError:
                inkex.errormsg("Invalid or corrupted DXF file.")
                exit(1)

        elif self.options.dxf_to_svg_parser == "legacy":
            inkex.utils.debug(
                "The selected legacy DXF to SVG parser is not supported by this extension yet. Use File > Import > *.dxf. This calls the \"dxf_input.inx\" extension."
            )
            exit(1)
        else:
            inkex.utils.debug("undefined parser")
            exit(1)

        # Write the generated SVG into InkScape's canvas
        try:
            stream = open(svg_file, 'r')
        except FileNotFoundError as e:
            inkex.utils.debug(
                "There was no SVG output generated. Cannot continue")
            exit(1)
        p = etree.XMLParser(huge_tree=True)
        doc = etree.parse(stream,
                          parser=etree.XMLParser(huge_tree=True)).getroot()
        stream.close()
        doc.set(
            'id',
            self.svg.get_unique_id("dxf_dwg_import-" +
                                   self.options.dxf_to_svg_parser + "-"))
        self.document.getroot().append(doc)

        #get children of the doc and move them one group above - we don't do this for bjnortier tool because this has different structure which we don't want to disturb
        if self.options.dxf_to_svg_parser == "sk1":
            elements = []
            emptyGroup = None
            for firstGroup in doc.getchildren():
                emptyGroup = firstGroup
                for element in firstGroup.getchildren():
                    elements.append(element)
                #break #only one cycle - could be bad idea or not
            for element in elements:
                doc.set('id', self.svg.get_unique_id('dxf_dwg_import'))
                doc.insert(doc.index(firstGroup), element)

            if emptyGroup is not None:
                emptyGroup.getparent().remove(emptyGroup)

        #empty the following vals because they destroy the size aspects of the import / make viewbox looking wrong
        if self.options.dxf_to_svg_parser == "bjnortier" or self.options.dxf_to_svg_parser == "kabeja":
            doc.set('width', '')
            doc.set('height', '')
            doc.set('viewBox', '')
            doc.getchildren()[0].set('transform', '')

        #adjust viewport and width/height to have the import at the center of the canvas
        if self.options.resizetoimport:
            bbox = inkex.elements._selected.ElementList.bounding_box(
                doc.getchildren()[0])
            if bbox is not None:
                root = self.svg.getElement('//svg:svg')
                offset = self.svg.unittouu(
                    str(self.options.extraborder) +
                    self.options.extraborder_units)
                root.set(
                    'viewBox', '%f %f %f %f' %
                    (bbox.left - offset, bbox.top - offset,
                     bbox.width + 2 * offset, bbox.height + 2 * offset))
                root.set('width', bbox.width + 2 * offset)
                root.set('height', bbox.height + 2 * offset)
Beispiel #17
0
def test_ldelete_prob_one(big_doc):
    doc = execute("ldelete --prob 1. 1", big_doc)

    assert len(doc.layers) == 0
Beispiel #18
0
def test_commands_execute(args):
    execute(args)
Beispiel #19
0
def test_commands_execute(args):
    if args.exit_code_no_layer == 0:
        execute(args.command)