1#!/usr/local/bin/python3.8
2# coding=utf-8
3#
4# Copyright (C) 2007 Peter Lewerin, peter.lewerin@tele2.se
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19#
20"""
21An Inkscape effect for adding CAD style dimensions to selected objects
22in a drawing.
23
24It uses the selection's bounding box, so if the bounding box has empty
25space in the x- or y-direction (such as with some stars) the results
26will look strange.  Strokes might also overlap the edge of the
27bounding box.
28
29The dimension arrows aren't measured: use the "Visualize Path/Measure
30Path" effect to add measurements.
31
32This code contains snippets from existing effects in the Inkscape
33extensions library, and marker data from markers.svg.
34"""
35
36import inkex
37from inkex import Group, Marker, PathElement
38
39import pathmodifier
40
41class Dimension(pathmodifier.PathModifier):
42    """Add dimensions as a path modifier"""
43    def add_arguments(self, pars):
44        pars.add_argument("--xoffset", type=float, default=100.0,\
45            help="x offset of the vertical dimension arrow")
46        pars.add_argument("--yoffset", type=float, default=100.0,\
47            help="y offset of the horizontal dimension arrow")
48        pars.add_argument("--type", default="geometric", help="Bounding box type")
49
50    def add_marker(self, name, rotate):
51        """Create a marker in the defs of the svg"""
52        marker = Marker()
53        marker.set('id', name)
54        marker.set('orient', 'auto')
55        marker.set('refX', '0.0')
56        marker.set('refY', '0.0')
57        marker.set('style', 'overflow:visible')
58        marker.set('inkscape:stockid', name)
59        self.svg.defs.append(marker)
60
61        arrow = PathElement(d='M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z ')
62        if rotate:
63            arrow.set('transform', 'scale(0.8) rotate(180) translate(12.5,0)')
64        else:
65            arrow.set('transform', 'scale(0.8) translate(12.5,0)')
66        arrow.set('style', 'fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none')
67        marker.append(arrow)
68
69    def horz_line(self, y, xlat, bbox):
70        """Create a horzontal line"""
71        line = PathElement()
72        x1 = bbox.left - xlat[0] * self.options.xoffset
73        x2 = bbox.right
74        y1 = y - xlat[1] * self.options.yoffset
75        line.set('d', 'M %f %f H %f' % (x1, y1, x2))
76        return line
77
78    def vert_line(self, x, xlat, bbox):
79        """Create a vertical line"""
80        line = PathElement()
81        x = x - xlat[0] * self.options.xoffset
82        y1 = bbox.top - xlat[1] * self.options.yoffset
83        y2 = bbox.bottom
84        line.set('d', 'M %f %f V %f' % (x, y1, y2))
85        return line
86
87    def effect(self):
88        scale = self.svg.unittouu('1px')  # convert to document units
89        self.options.xoffset *= scale
90        self.options.yoffset *= scale
91
92        if not self.svg.selected:
93            raise inkex.AbortExtension("Please select an object")
94        if self.options.type == "geometric":
95            bbox = self.svg.selection.bounding_box()
96        else:
97            bbox = self.svg.selection.first().bounding_box()
98
99        layer = self.svg.get_current_layer()
100
101        self.add_marker('Arrow1Lstart', False)
102        self.add_marker('Arrow1Lend', True)
103
104        group = Group()
105        layer.append(group)
106        group.set('fill', 'none')
107        group.set('stroke', 'black')
108
109        line = self.horz_line(bbox.top, [0, 1], bbox)
110        line.set('marker-start', 'url(#Arrow1Lstart)')
111        line.set('marker-end', 'url(#Arrow1Lend)')
112        line.set('stroke-width', str(scale))
113        group.append(line)
114
115        line = self.vert_line(bbox.left, [0, 2], bbox)
116        line.set('stroke-width', str(0.5 * scale))
117        group.append(line)
118
119        line = self.vert_line(bbox.right, [0, 2], bbox)
120        line.set('stroke-width', str(0.5 * scale))
121        group.append(line)
122
123        line = self.vert_line(bbox.left, [1, 0], bbox)
124        line.set('marker-start', 'url(#Arrow1Lstart)')
125        line.set('marker-end', 'url(#Arrow1Lend)')
126        line.set('stroke-width', str(scale))
127        group.append(line)
128
129        line = self.horz_line(bbox.top, [2, 0], bbox)
130        line.set('stroke-width', str(0.5 * scale))
131        group.append(line)
132
133        line = self.horz_line(bbox.bottom, [2, 0], bbox)
134        line.set('stroke-width', str(0.5 * scale))
135        group.append(line)
136
137        for node in self.svg.selected.values():
138            group.append(node)
139
140        layer.append(group)
141        return None
142
143
144if __name__ == '__main__':
145    Dimension().run()
146