This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.

#### 320 lines 15 KiB Python Raw Permalink Normal View History

 2021-07-23 02:36:56 +02:00 `﻿#!/usr/bin/env python3` ```''' ``` `Created by Danylo Horbatenko 2018, dnkxyz@gmail.com` `Copyright (C) 2018 George Fomitchev, gf@endurancerobots.com` ``` ``` `THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR` `IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,` `FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE` `AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER` `LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,` `OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE` `SOFTWARE.` ```''' ``` `#Version control: last edited by 01.03.2018 8:20` `import os` `import tempfile` `import shutil` `import subprocess` `import math` `import inkex` `import sys` `import png` `from lxml import etree` `from inkex.paths import Path` ``` ``` `def saw(x):` ` #The function returns a symmetric triangle wave with period 4 and varying between -1 and 1` ` x = math.fmod(x, 4.0)` ` x = math.fabs(x)` ` if x > 2.0:` ` y = 3 - x` ` else:` ` y = x - 1 ` ` return y` ` ` `def square(x):` ` #The function returns a square wave with period 4 and varying between -1 and 1` ` x = math.fmod(x, 4.0)` ` if 1.0 < x < 3.0:` ` y = 1.0` ` else:` ` y = -1.0 ` ` return y` ``` ``` `class LineShading(inkex.EffectExtension):` ` ` ` def add_arguments(self, pars):` ` pars.add_argument('--palette', help='Choose the colors...')` ` pars.add_argument("--waveform", help="Select the shape of the curve") ` ` pars.add_argument("--num_lines", type=int, help="Number of lines") ` ` pars.add_argument("--min_period", type=float, help="Minimum period (corresponds to black pixels)") ` ` pars.add_argument("--max_period", type=float, help="Maximum period (corresponds to white pixels)") ` ` pars.add_argument("--min_amplitude", type=float, help="Minimum amplitude (corresponds to white pixels)") ` ` pars.add_argument("--max_amplitude", type=float, help="Maximum amplitude (corresponds to black pixels)") ` ` pars.add_argument("--gamma", type=float, help="Maximum amplitude (corresponds to black pixels)") ` ` pars.add_argument("--line_width", type=float, help="Line width")` ` pars.add_argument("--units", help="Units for line thickness") ` ` pars.add_argument("--remove", type=inkex.Boolean, help="If True, source image is removed") ` ` pars.add_argument("--active-tab", help="The selected UI-tab when OK was pressed")` ``` ``` ` def drawfunction(self, image_w, image_h, file): ` ` reader = png.Reader(file)` ` w, h, pixels, metadata = reader.read_flat() ` ` matrice = [[1.0 for i in range(w)]for j in range(h)] ` ` if metadata['alpha']: ` ` n = 4 ` ` else: ` ` n = 3` ` #RGB convert to grayscale 0.21R + 0.72G + 0.07B ` ` for y in range(h): ` ` for x in range(w):` ` pixel_pos = (x + y * w)*n` ` p = 1.0 - (pixels[pixel_pos]*0.21 + pixels[(pixel_pos+1)]*0.72 + pixels[(pixel_pos+2)]*0.07)/255.0` ` matrice[y][x] = math.pow(p, 1.0/self.options.gamma)` ` ` ` points = []` ` step_y = image_h/h` ` step_x = image_w/(w-1)` ` min_amplitude = self.options.min_amplitude*step_y/2` ` max_amplitude = self.options.max_amplitude*step_y/2` ` min_period = self.options.min_period*step_y` ` max_period = self.options.max_period*step_y` ` min_frequency = 1.0/max_period ` ` max_frequency = 1.0/min_period ` ` ` ` #Sinusoidal wave (optimized) ` ` if self.options.waveform == 'sin':` ` for y in range(h):` ` pi = math.pi` ` phase = 0.0` ` coord_x = 0.0` ` amplitude = 0.0` ` n_step = 0` ` x0 = 0.0` ` y0 = math.sin(phase)*(min_amplitude + (max_amplitude - min_amplitude)*matrice[y][x]) + (y+0.5)*step_y` ` points.append(['M',[x0, y0]])` ` for x in range(w):` ` period = min_period + (max_period - min_period)*(1-matrice[y][x])` ` #period = 1.0/(min_frequency + (max_frequency - min_frequency)*(matrice[y][x]))` ` d_phase = 2.0*pi/period*step_x ` ` #calculate y` ` if phase > 2.0*pi:` ` if n_step > 0:` ` x3 = coord_x` ` y3 = -amplitude/n_step + (y+0.5)*step_y` ` x2 = x3 - (x3-x0)*0.32` ` y2 = y3` ` x1 = x0 + (x3-x0)*0.34` ` y1 = y0` ` x0 = x3` ` y0 = y3` ` points.append(['C',[x1, y1, x2, y2, x3, y3]])` ` n_step = 0` ` amplitude = 0` ` elif phase < pi < (phase + d_phase):` ` if n_step > 0:` ` x3 = coord_x` ` y3 = amplitude/n_step + (y+0.5)*step_y` ` x2 = x3 - (x3-x0)*0.34` ` y2 = y3` ` x1 = x0 + (x3-x0)*0.32` ` y1 = y0` ` x0 = x3` ` y0 = y3` ` points.append(['C',[x1, y1, x2, y2, x3, y3]])` ` n_step = 0` ` amplitude = 0` ` phase = math.fmod(phase, 2.0*pi) ` ` #calculate x` ` if phase < 0.5*pi < (phase + d_phase):` ` coord_x = (x - (phase - 0.5*pi)/d_phase)*step_x` ` elif phase < 1.5*pi < (phase + d_phase):` ` coord_x = (x - (phase - 1.5*pi)/d_phase)*step_x ` ` phase += d_phase` ` amplitude += (min_amplitude + (max_amplitude - min_amplitude)*matrice[y][x])` ` n_step += 1 ` ` #add last point ` ` if n_step > 0:` ` phase = math.fmod(phase, 2.0*pi)` ` if (0 < phase < 0.5*pi) or (pi < phase < 1.5*pi):` ` x3 = (w-1)*step_x` ` y3 = amplitude*math.sin(phase)/n_step + (y+0.5)*step_y` ` x2 = x3` ` y2 = y3` ` x1 = x0 + (x3-x0)*0.33` ` y1 = y0` ` points.append(['C',[x1, y1, x2, y2, x3, y3]])` ` else:` ` if coord_x > (w-1)*step_x:` ` coord_x = (w-1)*step_x` ` x3 = coord_x` ` y3 = math.copysign( amplitude , math.sin(phase))/n_step + (y+0.5)*step_y` ` x2 = x3 - (x3-x0)*0.32` ` y2 = y3` ` x1 = x0 + (x3-x0)*0.34` ` y1 = y0` ` points.append(['C',[x1, y1, x2, y2, x3, y3]])` ` if coord_x < (w-1)*step_x:` ` x0 = x3` ` y0 = y3` ` x3 = (w-1)*step_x` ` y3 = amplitude*math.sin(phase)/n_step + (y+0.5)*step_y` ` x2 = x3` ` y2 = y3` ` x1 = x0 + (x3-x0)*0.33` ` y1 = y0` ` points.append(['C',[x1, y1, x2, y2, x3, y3]])` ` ` ` #Sinusoidal wave (Brute-force) ` ` elif self.options.waveform == 'sin_b': ` ` pi2 = math.pi*2.0` ` for y in range(h): ` ` phase = - pi2/4.0` ` for x in range(w):` ` period = min_period + (max_period - min_period)*(1-matrice[y][x])` ` amplitude = min_amplitude + (max_amplitude - min_amplitude)*matrice[y][x]` ` phase += pi2*step_x/period` ` phase = math.fmod(phase, pi2)` ` if x == 0:` ` points.append(['M',[x*step_x, amplitude*math.sin(phase) + (y+0.5)*step_y]])` ` else:` ` points.append(['L',[x*step_x, amplitude*math.sin(phase) + (y+0.5)*step_y]]) ` ` ` ` #Saw wave ` ` elif self.options.waveform == 'saw':` ` for y in range(h): ` ` phase = 0.0` ` coord_x = 0.0` ` amplitude = 0.0` ` n_step = 0.0` ` for x in range(w):` ` period = min_period + (max_period - min_period)*(1-matrice[y][x])` ` #period = 1.0/(min_frequency + (max_frequency - min_frequency)*(matrice[y][x]))` ` d_phase = 4.0/period*step_x` ` if phase > 4.0:` ` coord_x = (x - (phase - 4.0)/d_phase)*step_x` ` elif phase < 2.0 < (phase + d_phase):` ` coord_x = (x - (phase - 2.0)/d_phase)*step_x ` ` phase = math.fmod(phase, 4.0)` ` if (phase < 1.0 < (phase + d_phase)) or (phase < 3.0 < (phase + d_phase)): ` ` if n_step > 0:` ` if coord_x == 0.0:` ` points.append(['M',[coord_x, amplitude*square(phase - 1.0)/n_step + (y+0.5)*step_y]])` ` else:` ` points.append(['L',[coord_x, amplitude*square(phase - 1.0)/n_step + (y+0.5)*step_y]])` ` n_step = 0` ` amplitude = 0 ` ` phase += d_phase` ` n_step += 1.0` ` amplitude += (min_amplitude + (max_amplitude - min_amplitude)*matrice[y][x])` ` if n_step > 0:` ` points.append(['L',[(w-1)*step_x, amplitude*saw(phase - 1.0)/n_step + (y+0.5)*step_y]])` ``` ``` ` #Square wave ` ` else:` ` for y in range(h): ` ` phase = 0.0` ` coord_x = 0.0` ` amplitude = 0.0` ` n_step = 0` ` for x in range(w):` ` period = min_period + (max_period - min_period)*(1-matrice[y][x])` ` #period = 1.0/(min_frequency + (max_frequency - min_frequency)*(matrice[y][x]))` ` d_phase = 4.0/period*step_x ` ` if phase > 4.0:` ` coord_x = (x - (phase - 4.0)/d_phase)*step_x` ` elif phase < 2.0 < (phase + d_phase):` ` coord_x = (x - (phase - 2.0)/d_phase)*step_x ` ` phase = math.fmod(phase, 4.0) ` ` if phase < 1.0 < (phase + d_phase): ` ` if n_step > 0:` ` if coord_x == 0.0:` ` points.append(['M',[coord_x, amplitude/n_step + (y+0.5)*step_y]])` ` else:` ` points.append(['L',[coord_x, -amplitude/n_step + (y+0.5)*step_y]])` ` points.append(['L',[coord_x, amplitude/n_step + (y+0.5)*step_y]])` ` n_step = 0` ` amplitude = 0 ` ` elif phase < 3.0 < (phase + d_phase): ` ` if n_step > 0:` ` if coord_x == 0.0:` ` points.append(['M',[coord_x, -amplitude/n_step + (y+0.5)*step_y]])` ` else:` ` points.append(['L',[coord_x, amplitude/n_step + (y+0.5)*step_y]])` ` points.append(['L',[coord_x, -amplitude/n_step + (y+0.5)*step_y]])` ` n_step = 0` ` amplitude = 0 ` ` phase += d_phase` ` n_step += 1` ` amplitude += (min_amplitude + (max_amplitude - min_amplitude)*matrice[y][x])` ` if n_step > 0:` ` if 3.0 > phase > 1.0:` ` points.append(['L',[(w-1)*step_x, amplitude/n_step + (y+0.5)*step_y]])` ` else:` ` points.append(['L',[(w-1)*step_x, -amplitude/n_step + (y+0.5)*step_y]])` ` return points` ` ` ` def draw_path(self, node, file): ` ` newpath = etree.Element(inkex.addNS('path','svg'))` ` line_width = self.options.line_width` ` units = self.options.units` ` s = {'stroke': '#000000', 'fill': 'none', 'stroke-linejoin': 'round', 'stroke-linecap': 'round', 'stroke-width': str(self.svg.unittouu(str(line_width) + units))}` ` newpath.set('style', str(inkex.Style(s))) ` ` x = node.get('x')` ` y = node.get('y')` ` t = 'translate('+ x +','+ y +')'` ` newpath.set('transform', t)` ` image_w = float(node.get('width'))` ` image_h = float(node.get('height')) ` ` newpath.set('d', str(Path(self.drawfunction(image_w, image_h, file))))` ` newpath.set('title', 'Line_Shading')` ` node.getparent().append(newpath)` ` newpath.set('x', x)` ` ` ` def export_png(self, node, file):` ` image_w = float(node.get('width'))` ` image_h = float(node.get('height'))` ` min_period = self.options.min_period ` ` max_period = self.options.min_period ` ` poinnt_per_min_period = 8.0 ` ` current_file = self.options.input_file` ` h_png = str(self.options.num_lines)` ` if min_period < max_period:` ` w_png = str(round(poinnt_per_min_period*image_w*float(h_png)/min_period/image_h))` ` else:` ` w_png = str(round(poinnt_per_min_period*image_w*float(h_png)/max_period/image_h))` ` id = node.get('id') ` ` cmd = "inkscape " + current_file + " --export-type=\"png\" --export-filename=" + file + " --actions=\"export-width:"+w_png+";export-height:"+h_png+";export-background:rgb(255,255,255);export-background-opacity:255;export-id:"+id+"\""` ` #inkex.errormsg(cmd) ` ` proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)` ` #inkex.utils.debug(cmd)` ` #inkex.utils.debug(proc.communicate())` ` #sys.exit(0)` ` #return_code = proc.wait()` ` #sys.exit(0)` ` f = proc.stdout` ` err = proc.stderr` ` f.close()` ` err.close()` ` proc.wait()` ` #inkex.errormsg(proc.stdout.read()) ` ` ` ` def effect(self):` ` image_selected_flag = False` ` for id, node in self.svg.selected.items():` ` if node.tag == inkex.addNS('image','svg'):` ` image_selected_flag = True` ` tmp_dir = tempfile.mkdtemp()` ` png_temp_file = os.path.join(tmp_dir, "LineShading.png")` ` self.export_png(node, png_temp_file)` ` self.draw_path(node, png_temp_file)` ` shutil.rmtree(tmp_dir)` ` if self.options.remove:` ` node.delete()` ` return` ` if not image_selected_flag:` ` inkex.errormsg("Please select an image")` ``` ``` `if __name__ == '__main__':` ` LineShading().run()`