292 lines
13 KiB
Python
292 lines
13 KiB
Python
|
#! /usr/bin/python
|
||
|
'''
|
||
|
Copyright (C) 2017 Artem Synytsyn a.synytsyn@gmail.com
|
||
|
|
||
|
#TODO: Code cleaning and refactoring
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 2 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
'''
|
||
|
|
||
|
import inkex
|
||
|
import simplestyle, sys
|
||
|
from math import *
|
||
|
# The simplestyle module provides functions for style parsing.
|
||
|
from simplestyle import *
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class Knob_Scale(inkex.Effect):
|
||
|
def __init__(self):
|
||
|
inkex.Effect.__init__(self)
|
||
|
# General settings
|
||
|
self.OptionParser.add_option("--x",
|
||
|
action="store", type="int",
|
||
|
dest="x", default=0.0,
|
||
|
help="Center X")
|
||
|
self.OptionParser.add_option("--y",
|
||
|
action="store", type="int",
|
||
|
dest="y", default=0.0,
|
||
|
help="Center Y")
|
||
|
self.OptionParser.add_option("--radius",
|
||
|
action="store", type="int",
|
||
|
dest="radius", default=100.0,
|
||
|
help="Knob radius")
|
||
|
self.OptionParser.add_option("--linewidth",
|
||
|
action="store", type="int",
|
||
|
dest="linewidth", default=1,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--angle",
|
||
|
action="store", type="float",
|
||
|
dest="angle", default=260.0,
|
||
|
help="Angle of the knob scale in degrees")
|
||
|
self.OptionParser.add_option("--draw_arc",
|
||
|
action="store", type="inkbool",
|
||
|
dest="draw_arc", default='True',
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--draw_centering_circle",
|
||
|
action="store", type="inkbool",
|
||
|
dest="draw_centering_circle", default='False',
|
||
|
help="")
|
||
|
self.OptionParser.add_option("-u", "--units",
|
||
|
action="store", type="string",
|
||
|
dest="units", default="px",
|
||
|
help="units to measure size of knob")
|
||
|
# Tick settings
|
||
|
self.OptionParser.add_option("--n_ticks",
|
||
|
action="store", type="int",
|
||
|
dest="n_ticks", default=5,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--ticksize",
|
||
|
action="store", type="int",
|
||
|
dest="ticksize", default=10,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--n_subticks",
|
||
|
action="store", type="int",
|
||
|
dest="n_subticks", default=10,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--subticksize",
|
||
|
action="store", type="int",
|
||
|
dest="subticksize", default=5,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--style",
|
||
|
action="store", type="string",
|
||
|
dest="style", default='marks_outwards',
|
||
|
help="Style of marks")
|
||
|
|
||
|
# Label settings
|
||
|
self.OptionParser.add_option("--labels_enabled",
|
||
|
action="store", type="inkbool",
|
||
|
dest="labels_enabled", default='False',
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--rounding_level",
|
||
|
action="store", type="int",
|
||
|
dest="rounding_level", default=0,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--text_size",
|
||
|
action="store", type="int",
|
||
|
dest="text_size", default=1,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--text_offset",
|
||
|
action="store", type="int",
|
||
|
dest="text_offset", default=20,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--start_value",
|
||
|
action="store", type="float",
|
||
|
dest="start_value", default=0,
|
||
|
help="")
|
||
|
self.OptionParser.add_option("--stop_value",
|
||
|
action="store", type="float",
|
||
|
dest="stop_value", default=10,
|
||
|
help="")
|
||
|
# Dummy
|
||
|
self.OptionParser.add_option("","--tab")
|
||
|
|
||
|
def draw_text(self, textvalue, radius, angular_position, text_size, parent):
|
||
|
# Create text element
|
||
|
text = inkex.etree.Element(inkex.addNS('text','svg'))
|
||
|
text.text = textvalue
|
||
|
|
||
|
# Set text position to center of document.
|
||
|
text.set('x', str(self.x_offset + radius*cos(angular_position)))
|
||
|
text.set('y', str(self.y_offset + radius*sin(angular_position) + text_size/2))
|
||
|
|
||
|
# Center text horizontally with CSS style.
|
||
|
style = {
|
||
|
'text-align' : 'center',
|
||
|
'text-anchor': 'middle',
|
||
|
'alignment-baseline' : 'center',
|
||
|
'font-size' : str(text_size),
|
||
|
'vertical-align' : 'middle'
|
||
|
}
|
||
|
|
||
|
text.set('style', formatStyle(style))
|
||
|
parent.append(text)
|
||
|
def draw_knob_arc(self, radius, parent, angle, transform='' ):
|
||
|
|
||
|
start_point_angle = (angle - pi)/2.0
|
||
|
end_point_angle = pi - start_point_angle
|
||
|
|
||
|
style = { 'stroke' : '#000000',
|
||
|
'stroke-width' : str(self.options.linewidth),
|
||
|
'fill' : 'none' }
|
||
|
ell_attribs = {'style':simplestyle.formatStyle(style),
|
||
|
inkex.addNS('cx','sodipodi') :str(self.x_offset),
|
||
|
inkex.addNS('cy','sodipodi') :str(self.y_offset),
|
||
|
inkex.addNS('rx','sodipodi') :str(radius),
|
||
|
inkex.addNS('ry','sodipodi') :str(radius),
|
||
|
inkex.addNS('start','sodipodi') :str(end_point_angle),
|
||
|
inkex.addNS('end','sodipodi') :str(start_point_angle),
|
||
|
inkex.addNS('open','sodipodi') :'true', #all ellipse sectors we will draw are open
|
||
|
inkex.addNS('type','sodipodi') :'arc',
|
||
|
'transform' :transform
|
||
|
|
||
|
}
|
||
|
ell = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs )
|
||
|
|
||
|
def draw_centering_circle(self, radius, parent):
|
||
|
|
||
|
style = { 'stroke' : '#000000',
|
||
|
'stroke-width' : '1',
|
||
|
'fill' : 'none' }
|
||
|
ell_attribs = {'style':simplestyle.formatStyle(style),
|
||
|
inkex.addNS('cx','sodipodi') :str(self.x_offset),
|
||
|
inkex.addNS('cy','sodipodi') :str(self.y_offset),
|
||
|
inkex.addNS('rx','sodipodi') :str(radius),
|
||
|
inkex.addNS('ry','sodipodi') :str(radius),
|
||
|
inkex.addNS('type','sodipodi') :'arc'
|
||
|
}
|
||
|
ell = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs )
|
||
|
|
||
|
def draw_circle_mark(self, x_offset, y_offset, radius, mark_angle, mark_length, parent):
|
||
|
|
||
|
cx = x_offset + radius*cos(mark_angle)
|
||
|
cy = y_offset + radius*sin(mark_angle)
|
||
|
r = mark_length / 2.0
|
||
|
|
||
|
style = {
|
||
|
'stroke': '#000000',
|
||
|
'stroke-width':'0',
|
||
|
'fill': '#000000'
|
||
|
}
|
||
|
|
||
|
circ_attribs = {
|
||
|
'style':simplestyle.formatStyle(style),
|
||
|
'cx':str(cx),
|
||
|
'cy':str(cy),
|
||
|
'r':str(r)
|
||
|
}
|
||
|
circle = inkex.etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs )
|
||
|
|
||
|
def draw_knob_line_mark(self, x_offset, y_offset, radius, mark_angle, mark_length, parent):
|
||
|
x1 = x_offset + radius*cos(mark_angle)
|
||
|
y1 = y_offset + radius*sin(mark_angle)
|
||
|
x2 = x_offset + (radius + mark_length)*cos(mark_angle)
|
||
|
y2 = y_offset + (radius + mark_length)*sin(mark_angle)
|
||
|
|
||
|
line_style = { 'stroke': '#000000',
|
||
|
'stroke-width': str(self.options.linewidth),
|
||
|
'fill': 'none'
|
||
|
}
|
||
|
|
||
|
line_attribs = {'style' : simplestyle.formatStyle(line_style),
|
||
|
inkex.addNS('label','inkscape') : "none",
|
||
|
'd' : 'M '+str(x1) +',' +
|
||
|
str(y1) +' L '+str(x2)
|
||
|
+','+str(y2) }
|
||
|
|
||
|
line = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs )
|
||
|
|
||
|
def draw_tick(self, radius, mark_angle, mark_size, parent):
|
||
|
if (self.options.style == 'marks_inwards') or (self.options.style == 'marks_outwards'):
|
||
|
self.draw_knob_line_mark(self.x_offset, self.y_offset, radius, mark_angle, mark_size, parent)
|
||
|
elif self.options.style == 'marks_circles':
|
||
|
self.draw_circle_mark(self.x_offset, self.y_offset, radius, mark_angle, mark_size, parent)
|
||
|
|
||
|
def effect(self):
|
||
|
|
||
|
parent = self.current_layer
|
||
|
radius = self.unittouu(str(self.options.radius) + self.options.units)
|
||
|
self.x_offset = self.unittouu(str(self.options.x) + self.options.units)
|
||
|
self.y_offset = self.unittouu(str(self.options.y) + self.options.units)
|
||
|
#print >>sys.stderr, "x_offset: %s\n" % x_offset
|
||
|
#print >>sys.stderr, "y_offset: %s\n" % y_offset
|
||
|
#radius = self.options.radius
|
||
|
angle = self.options.angle*pi/180.0
|
||
|
n_ticks = self.options.n_ticks
|
||
|
n_subticks = self.options.n_subticks
|
||
|
is_outer = True
|
||
|
if self.options.style == 'marks_inwards':
|
||
|
is_outer = False
|
||
|
|
||
|
tick_length = self.unittouu(str(self.options.ticksize) + self.options.units)
|
||
|
subtick_length = self.unittouu(str(self.options.subticksize) + self.options.units)
|
||
|
arc_radius = radius
|
||
|
|
||
|
|
||
|
# Labeling settings
|
||
|
start_num = self.options.start_value
|
||
|
end_num = self.options.stop_value
|
||
|
text_spacing = self.unittouu(str(self.options.text_offset) + self.options.units)
|
||
|
text_size = self.unittouu(str(self.options.text_size) + self.options.units)
|
||
|
|
||
|
if not is_outer:
|
||
|
subtick_radius = radius + tick_length - subtick_length
|
||
|
arc_radius = radius + tick_length
|
||
|
else:
|
||
|
subtick_radius = radius
|
||
|
arc_radius = radius
|
||
|
|
||
|
if self.options.draw_arc:
|
||
|
self.draw_knob_arc(arc_radius, parent, angle)
|
||
|
|
||
|
if self.options.draw_centering_circle:
|
||
|
self.draw_centering_circle(arc_radius + tick_length + text_size + text_spacing, parent)
|
||
|
|
||
|
ticks_delta = angle / (n_ticks - 1)
|
||
|
start_ticks_angle = 1.5*pi - 0.5*angle
|
||
|
for tick in range(n_ticks):
|
||
|
self.draw_tick(radius, start_ticks_angle + ticks_delta*tick,
|
||
|
tick_length, parent)
|
||
|
|
||
|
if self.options.labels_enabled:
|
||
|
if self.options.rounding_level > 0:
|
||
|
tick_text = str(round(start_num +
|
||
|
float(tick) * (end_num - start_num) / (n_ticks - 1),
|
||
|
self.options.rounding_level))
|
||
|
else:
|
||
|
tick_text = str(int(start_num + float(tick) * (end_num - start_num) / (n_ticks - 1)))
|
||
|
|
||
|
self.draw_text(tick_text, radius + tick_length + text_spacing,
|
||
|
start_ticks_angle + ticks_delta*tick,
|
||
|
text_size,
|
||
|
parent)
|
||
|
|
||
|
if tick == (n_ticks - 1):
|
||
|
break
|
||
|
|
||
|
subticks_delta = ticks_delta / (n_subticks + 1)
|
||
|
subtick_start_angle = start_ticks_angle + ticks_delta*tick + subticks_delta
|
||
|
for subtick in range(n_subticks):
|
||
|
self.draw_tick(subtick_radius, subtick_start_angle + subticks_delta*subtick,
|
||
|
subtick_length, parent)
|
||
|
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
e = Knob_Scale()
|
||
|
e.affect()
|
||
|
|