Added RealScale
This commit is contained in:
parent
4b86dc4aee
commit
bd41a81cb4
92
extensions/fablabchemnitz_realscale.inx
Normal file
92
extensions/fablabchemnitz_realscale.inx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||||
|
<_name>Scale To Real</_name>
|
||||||
|
<id>fablabchemnitz.de.realscale</id>
|
||||||
|
<param name="tab" type="notebook">
|
||||||
|
<page name="realscale" _gui-text="RealScale">
|
||||||
|
<_param name="measurement" type="description" appearance="header">Measurement</_param>
|
||||||
|
<param name="length" type="float" min="0.1" max="100000.0" precision="3" _gui-text="Length of scaling path: " _gui-description="Real-world length of the scaling path">100.0</param>
|
||||||
|
<param name="unit" type="optiongroup" appearance="minimal" _gui-text="Unit: " _gui-description="Real-world unit for drawing">
|
||||||
|
<option value="px">px</option>
|
||||||
|
<option value="pt">pt</option>
|
||||||
|
<option value="in">in</option>
|
||||||
|
<option value="ft">ft</option>
|
||||||
|
<option value="yd">yd</option>
|
||||||
|
<option value="mm">mm</option>
|
||||||
|
<option value="cm">cm</option>
|
||||||
|
<option value="m">m</option>
|
||||||
|
<option value="km">km</option>
|
||||||
|
</param>
|
||||||
|
<_param name="scale_drawing" type="description" appearance="header">Scale Drawing</_param>
|
||||||
|
<param name="choosescale" _gui-text="Scale Category" appearance="minimal" _gui-description="Select which kind of scaling you would like to use, then select the actual scale in the corresponding dropdown." type="optiongroup">
|
||||||
|
<option value="metric">Metric</option>
|
||||||
|
<option value="imperial">Imperial</option>
|
||||||
|
<option value="custom">Custom</option>
|
||||||
|
</param>
|
||||||
|
<param name="metric" type="enum" _gui-text="Metric Scale: " appearance="minimal" _gui-description="Metric scales for drawings">
|
||||||
|
<item value="1">1:1</item>
|
||||||
|
<item value="2">1:2</item>
|
||||||
|
<item value="5">1:5</item>
|
||||||
|
<item value="10">1:10</item>
|
||||||
|
<item value="20">1:20</item>
|
||||||
|
<item value="25">1:25</item>
|
||||||
|
<item value="50">1:50</item>
|
||||||
|
<item value="100">1:100</item>
|
||||||
|
<item value="200">1:200</item>
|
||||||
|
<item value="250">1:250</item>
|
||||||
|
<item value="500">1:500</item>
|
||||||
|
<item value="1000">1:1000</item>
|
||||||
|
<item value="1250">1:1250</item>
|
||||||
|
<item value="2500">1:2500</item>
|
||||||
|
</param>
|
||||||
|
<param name="imperial" type="enum" _gui-text="Imperial Scale: " _gui-description="Imperial scales for drawings">
|
||||||
|
<item value="1">1:1</item>
|
||||||
|
<item value="2">1:2</item>
|
||||||
|
<item value="4">1:4</item>
|
||||||
|
<item value="8">1:8</item>
|
||||||
|
<item value="16">1:16</item>
|
||||||
|
<item value="24">1:24</item>
|
||||||
|
<item value="32">1:32</item>
|
||||||
|
<item value="48">1:48</item>
|
||||||
|
<item value="64">1:64</item>
|
||||||
|
<item value="96">1:96</item>
|
||||||
|
<item value="128">1:128</item>
|
||||||
|
</param>
|
||||||
|
<param name="custom_scale" type="float" min="0.00001" max="2000000" _gui-text="Custom Scale Value (1: value)" precision="5" _gui-description="Enter a custom drawing scale factor. Enlarge your drawing by entering a value smaller than 1.">45</param>
|
||||||
|
<_param name="scale_rule" type="description" appearance="header">Scale Rule</_param>
|
||||||
|
<param name="showscale" type="boolean" _gui-text="Generate Scale Rule" _gui-description="Draw the scale rule on the page">true</param>
|
||||||
|
<param name="unitlength" type="optiongroup" appearance="minimal" _gui-text="Half scale length " _gui-description="Number of units in one half of scale rule">
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="100">100</option>
|
||||||
|
</param>
|
||||||
|
</page>
|
||||||
|
<page name="help" gui-text="Help">
|
||||||
|
<_param name="usage" type="description" appearance="header">Usage</_param>
|
||||||
|
<_param name="Instructions" type="description" xml:space="preserve">Scale an object (or a group) by indicating the length of a scaling path in real-life units (useful for scaling architectural drawings, for example):
|
||||||
|
|
||||||
|
• Create a straight two-point line of known length by drawing on the object you want to scale (e.g. if you know how long a wall is in your drawing, draw the line from one end of the wall to the other).
|
||||||
|
|
||||||
|
• Select the line first, then the drawing (which must be a group or a single object).
|
||||||
|
|
||||||
|
• Length: indicate how long the line you drew is in the real world (e.g. how long the wall is).
|
||||||
|
|
||||||
|
• Unit: indicate the unit the measurement is in, but remember that your drawing can become huge if you choose km or even m. Fix this with "Scale Drawing".
|
||||||
|
|
||||||
|
• Scale Drawing: Select a scale category, then a value other than 1:1 from the dropdown for the selected scale category to scale the drawing. For upscaling, select "Custom" and enter a custom value smaller than 1.
|
||||||
|
|
||||||
|
• Scale Rule: You can display the scale with a ruler. The number of units used along the ruler can be selected (e.g. to make the ruler show marks for one, ten or one hundred centimeters).</_param>
|
||||||
|
</page>
|
||||||
|
</param>
|
||||||
|
<effect needs-live-preview="true">
|
||||||
|
<object-type>all</object-type>
|
||||||
|
<effects-menu>
|
||||||
|
<submenu _name="FabLab Chemnitz">
|
||||||
|
<submenu _name="Modify existing Path(s)" />
|
||||||
|
</submenu>
|
||||||
|
</effects-menu>
|
||||||
|
</effect>
|
||||||
|
<script>
|
||||||
|
<command reldir="extensions" interpreter="python">fablabchemnitz_realscale.py</command>
|
||||||
|
</script>
|
||||||
|
</inkscape-extension>
|
244
extensions/fablabchemnitz_realscale.py
Normal file
244
extensions/fablabchemnitz_realscale.py
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
"""
|
||||||
|
Copyright (C) 2015 Maren Hachmann, marenhachmann@yahoo.com
|
||||||
|
Copyright (C) 2010 Blair Bonnett, blair.bonnett@gmail.com (parts from multiscale extension)
|
||||||
|
Copyright (C) 2005 Aaron Spike, aaron@ekips.org (parts from perspective extension)
|
||||||
|
Copyright (C) 2015 Giacomo Mirabassi, giacomo@mirabassi.it (parts from jpeg export extension)
|
||||||
|
Copyright (C) 2016 Neon22 @github (scale ruler)
|
||||||
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import inkex
|
||||||
|
from inkex import Transform
|
||||||
|
from inkex.paths import CubicSuperPath
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
inkex.localization.localize
|
||||||
|
|
||||||
|
### Scale Ruler
|
||||||
|
# inches = [1, 2, 4, 8, 16, 24, 32, 48, 64, 96, 128]
|
||||||
|
# metric = [1,2,5,10,20,50,100,200,250,500,1000,1250,2500]
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# - maybe turn dropdown for choosing scale type (metric/imperial/custom) into radio buttons?
|
||||||
|
# - scale font size
|
||||||
|
# - scale box-height better for small boxes
|
||||||
|
# - add ruler into current layer
|
||||||
|
# - add magnification e.g. 2:1 for small drawings
|
||||||
|
|
||||||
|
class Realscale(inkex.Effect):
|
||||||
|
def __init__(self):
|
||||||
|
inkex.Effect.__init__(self)
|
||||||
|
self.arg_parser.add_argument('--tab')
|
||||||
|
self.arg_parser.add_argument('--length', type=float, default=100.0, help='Length of scaling path in real-world units')
|
||||||
|
self.arg_parser.add_argument('--unit', default='cm', help='Real-world unit')
|
||||||
|
self.arg_parser.add_argument('--showscale', default='false', help='Show Scale Ruler')
|
||||||
|
self.arg_parser.add_argument('--choosescale', default='all', help='Choose Scale')
|
||||||
|
self.arg_parser.add_argument('--metric', default='1', help='Common metric scales')
|
||||||
|
self.arg_parser.add_argument('--imperial',default='1', help='Common imperial scales')
|
||||||
|
self.arg_parser.add_argument('--custom_scale', type=float, default=45, help='Custom scale')
|
||||||
|
self.arg_parser.add_argument('--unitlength', type=int, default='1', help='Length of scale ruler')
|
||||||
|
|
||||||
|
def calc_scale_center(self, p1x, p1y, p2x, p2y):
|
||||||
|
""" Use straight line as scaling center.
|
||||||
|
- determine which point is center on basis of quadrant the line is in.
|
||||||
|
- approx this by using center of line
|
||||||
|
0,0 corresponds to UL corner of page
|
||||||
|
"""
|
||||||
|
scale_center = (0,0) # resulting scaling point
|
||||||
|
# calc page center
|
||||||
|
pagecenter_x = self.svg.unittouu(self.document.getroot().get('width'))/2
|
||||||
|
pagecenter_y = self.svg.unittouu(self.document.getroot().get('height'))/2
|
||||||
|
# calc minmax of straightline ref points
|
||||||
|
minx = min(p1x, p2x)
|
||||||
|
maxx = max(p1x, p2x)
|
||||||
|
miny = min(p1y, p2y)
|
||||||
|
maxy = max(p1y, p2y)
|
||||||
|
# simplifiy calc by using center of line to determine quadrant
|
||||||
|
line_x = p1x + (p2x - p1x)/2
|
||||||
|
line_y = p1y + (p2y - p1y)/2
|
||||||
|
# determine quadrant
|
||||||
|
if line_x < pagecenter_x:
|
||||||
|
# Left hand side
|
||||||
|
if line_y < pagecenter_y:
|
||||||
|
scale_center = (minx,miny) # UL
|
||||||
|
else:
|
||||||
|
scale_center = (minx,maxy) # LL
|
||||||
|
else: # Right hand side
|
||||||
|
if line_y < pagecenter_y:
|
||||||
|
scale_center = (maxx,miny) # UR
|
||||||
|
else:
|
||||||
|
scale_center = (maxx,maxy) # LR
|
||||||
|
#inkex.debug("%s %s,%s" % (scale_center, pagecenter_x*2, pagecenter_y*2))
|
||||||
|
return scale_center
|
||||||
|
|
||||||
|
def create_ruler(self, parent, width, pos, value, drawing_scale):
|
||||||
|
""" Draw Scale ruler
|
||||||
|
- Position above user's straightline.
|
||||||
|
- Ruler shows two units together. First one cut into 5
|
||||||
|
- pos is a tuple e.g. (0,0)
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- Fix font size for large and small rulers
|
||||||
|
- Fix line width for large and small rulers
|
||||||
|
"""
|
||||||
|
" Ruler is always 2 units long with 5 divs in the left half "
|
||||||
|
# Draw two boxes next to each other. Top half of right half of ruler is filled black
|
||||||
|
line_width = self.svg.unittouu('0.25 mm')
|
||||||
|
box_height = max(width/15, self.svg.unittouu('2 mm'))
|
||||||
|
font_height = 8
|
||||||
|
White = '#ffffff'
|
||||||
|
Black = '#000000'
|
||||||
|
t = 'translate' + str(pos)
|
||||||
|
|
||||||
|
# create clip in order to get an exact ruler width (without the outer half of the stroke)
|
||||||
|
path = '//svg:defs'
|
||||||
|
defslist = self.document.getroot().xpath(path, namespaces=inkex.NSS)
|
||||||
|
if len(defslist) > 0:
|
||||||
|
defs = defslist[0]
|
||||||
|
|
||||||
|
clipPathData = {inkex.addNS('label', 'inkscape'):'rulerClipPath', 'clipPathUnits':'userSpaceOnUse', 'id':'rulerClipPath'}
|
||||||
|
clipPath = etree.SubElement(defs, 'clipPath', clipPathData)
|
||||||
|
clipBox = {'x':str(-width), 'y':'0.0',
|
||||||
|
'width':str(width*2), 'height':str(box_height),
|
||||||
|
'style':'fill:%s; stroke:none; fill-opacity:1;' % (Black)}
|
||||||
|
|
||||||
|
etree.SubElement(clipPath, 'rect', clipBox)
|
||||||
|
|
||||||
|
# create groups for scale rule and ruler
|
||||||
|
scale_group = etree.SubElement(parent, 'g', {inkex.addNS('label','inkscape'):'scale_group', 'transform':t})
|
||||||
|
ruler_group = etree.SubElement(scale_group, 'g', {inkex.addNS('label','inkscape'):'ruler', 'clip-path':"url(#rulerClipPath)"})
|
||||||
|
|
||||||
|
# box for right half of ruler
|
||||||
|
boxR = {'x':'0.0', 'y':'0.0',
|
||||||
|
'width':str(width), 'height':str(box_height),
|
||||||
|
'style':'fill:%s; stroke:%s; stroke-width:%s; stroke-opacity:1; fill-opacity:1;' % (White, Black, line_width)}
|
||||||
|
etree.SubElement(ruler_group, 'rect', boxR)
|
||||||
|
# top half black
|
||||||
|
boxRf = {'x':'0.0', 'y':'0.0',
|
||||||
|
'width':str(width), 'height':str(box_height/2),
|
||||||
|
'style':'fill:%s; stroke:none; fill-opacity:1;' % (Black)}
|
||||||
|
etree.SubElement(ruler_group, 'rect', boxRf)
|
||||||
|
# Left half of ruler
|
||||||
|
boxL = {'x':str(-width), 'y':'0.0',
|
||||||
|
'width':str(width), 'height':str(box_height),
|
||||||
|
'style':'fill:%s; stroke:%s; stroke-width:%s; stroke-opacity:1; fill-opacity:1;' % (White, Black, line_width)}
|
||||||
|
etree.SubElement(ruler_group, 'rect', boxL)
|
||||||
|
# staggered black fills on left half
|
||||||
|
start = -width
|
||||||
|
for i in range(5):
|
||||||
|
boxRf = {'x':str(start), 'y':str((i+1)%2 * box_height/2),
|
||||||
|
'width':str(width/5), 'height':str(box_height/2),
|
||||||
|
'style':'fill:%s; stroke:none; fill-opacity:1;' % (Black)}
|
||||||
|
etree.SubElement(ruler_group, 'rect', boxRf)
|
||||||
|
start += width/5
|
||||||
|
# text
|
||||||
|
textstyle = {'font-size': str(font_height)+ " px",
|
||||||
|
'font-family': 'sans-serif',
|
||||||
|
'text-anchor': 'middle',
|
||||||
|
'text-align': 'center',
|
||||||
|
'fill': Black }
|
||||||
|
text_atts = {'style': str(inkex.Style(textstyle)),
|
||||||
|
'x': '0', 'y': str(-font_height/2) }
|
||||||
|
text = etree.SubElement(scale_group, 'text', text_atts)
|
||||||
|
text.text = "0"
|
||||||
|
text_atts = {'style': str(inkex.Style(textstyle)),
|
||||||
|
'x': str(width), 'y': str(-font_height/2) }
|
||||||
|
text = etree.SubElement(scale_group, 'text', text_atts)
|
||||||
|
text.text = str(value)
|
||||||
|
|
||||||
|
text_atts = {'style':str(inkex.Style(textstyle)),
|
||||||
|
'x': str(-width), 'y': str(-font_height/2) }
|
||||||
|
text = etree.SubElement(scale_group, 'text', text_atts)
|
||||||
|
text.text = str(value)
|
||||||
|
# Scale note
|
||||||
|
text_atts = {'style':str(inkex.Style(textstyle)),
|
||||||
|
'x': '0', 'y': str(-font_height*2.5) }
|
||||||
|
text = etree.SubElement(scale_group, 'text', text_atts)
|
||||||
|
text.text = "Scale 1:" + str(drawing_scale) + " (" + self.options.unit + ")"
|
||||||
|
|
||||||
|
|
||||||
|
def effect(self):
|
||||||
|
if len(self.options.ids) != 2:
|
||||||
|
inkex.errormsg(_("This extension requires two selected objects. The first selected object must be the straight line with two nodes."))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# drawing that will be scaled is selected second, must be a single object
|
||||||
|
scalepath = self.svg.selected[self.options.ids[0]]
|
||||||
|
drawing = self.svg.selected[self.options.ids[1]]
|
||||||
|
|
||||||
|
if scalepath.tag != inkex.addNS('path','svg'):
|
||||||
|
inkex.errormsg(_("The first selected object is not a path.\nPlease select a straight line with two nodes instead."))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# apply its transforms to the scaling path, so we get the correct coordinates to calculate path length
|
||||||
|
scalepath.apply_transform()
|
||||||
|
|
||||||
|
path = CubicSuperPath(scalepath.get('d'))
|
||||||
|
if len(path) < 1 or len(path[0]) < 2:
|
||||||
|
inkex.errormsg(_("This extension requires that the first selected path be two nodes long."))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# calculate path length
|
||||||
|
p1_x = path[0][0][1][0]
|
||||||
|
p1_y = path[0][0][1][1]
|
||||||
|
p2_x = path[0][1][1][0]
|
||||||
|
p2_y = path[0][1][1][1]
|
||||||
|
|
||||||
|
p_length = self.svg.unittouu(str(distance((p1_x, p1_y),(p2_x, p2_y))) + self.svg.unit)
|
||||||
|
|
||||||
|
# Find Drawing Scale
|
||||||
|
if self.options.choosescale == 'metric':
|
||||||
|
drawing_scale = int(self.options.metric)
|
||||||
|
elif self.options.choosescale == 'imperial':
|
||||||
|
drawing_scale = int(self.options.imperial)
|
||||||
|
elif self.options.choosescale == 'custom':
|
||||||
|
drawing_scale = self.options.custom_scale
|
||||||
|
|
||||||
|
# calculate scaling center
|
||||||
|
center = self.calc_scale_center(p1_x, p1_y, p2_x, p2_y)
|
||||||
|
|
||||||
|
# calculate scaling factor
|
||||||
|
target_length = self.svg.unittouu(str(self.options.length) + self.options.unit)
|
||||||
|
factor = (target_length / p_length) / drawing_scale
|
||||||
|
# inkex.debug("%s, %s %s" % (target_length, p_length, factor))
|
||||||
|
|
||||||
|
# Add scale ruler
|
||||||
|
if self.options.showscale == "true":
|
||||||
|
dist = int(self.options.unitlength)
|
||||||
|
|
||||||
|
ruler_length = self.svg.unittouu(str(dist) + self.options.unit) / drawing_scale
|
||||||
|
ruler_pos = (p1_x + (p2_x - p1_x)/2, (p1_y + (p2_y - p1_y)/2) - self.svg.unittouu('4 mm'))
|
||||||
|
|
||||||
|
# TODO: add into current layer instead
|
||||||
|
self.create_ruler(self.document.getroot(), ruler_length, ruler_pos, dist, drawing_scale)
|
||||||
|
|
||||||
|
# Get drawing and current transformations
|
||||||
|
for obj in (scalepath, drawing):
|
||||||
|
# Scale both objects about the center, first translate back to origin
|
||||||
|
scale_matrix = [[1, 0.0, -center[0]], [0.0, 1, -center[1]]]
|
||||||
|
obj.transform = Transform(scale_matrix) * obj.transform
|
||||||
|
# Then scale
|
||||||
|
scale_matrix = [[factor, 0.0, 0.0], [0.0, factor, 0.0]]
|
||||||
|
obj.transform = Transform(scale_matrix) * obj.transform
|
||||||
|
# Then translate back to original scale center location
|
||||||
|
scale_matrix = [[1, 0.0, center[0]], [0.0, 1, center[1]]]
|
||||||
|
obj.transform = Transform(scale_matrix) * obj.transform
|
||||||
|
|
||||||
|
# Helper function
|
||||||
|
def distance(xy0, xy1):
|
||||||
|
x0, y0 = xy0
|
||||||
|
x1, y1 = xy1
|
||||||
|
return math.sqrt((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
Realscale().run()
|
Reference in New Issue
Block a user