FreeCAD-macros

Форк
0
/
HilbertCurve.FCMacro 
256 строк · 9.0 Кб
1
# -*- coding: utf-8 -*-
2

3
# Copyright (c) 2020 Simone Bignetti, Gottolengo Italy (simone.b)
4
#
5
# This file is part of the FreeCAD CAx development system.
6
#
7
# This library is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU Lesser General Public
9
# License as published by the Free Software Foundation; either
10
# version 2.1 of the License, or (at your option) any later version.
11
#
12
# This library is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public
18
# License along with this library; if not, write to the Free Software
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
20
# USA
21
#
22
# You can contact me by mail at simone.bignetti@linux.it
23

24
__Name__ = 'HilbertCurve'
25
__Comment__ = 'This macro creates an Hilbert curve wire in 2 or 3 dimensions with many iterations.'
26
__Author__  = 'Simone Bignetti'
27
__Version__ = '1.2.0'
28
__Date__    = '2020-12-29'
29
__License__ = 'LGPL-2.1-or-later'
30
__Web__ = 'https://wiki.freecadweb.org/Macro_HilbertCurve'
31
__Wiki__ = 'https://wiki.freecadweb.org/Macro_HilbertCurve'
32
__Icon__ = 'HilbertCurve.svg'
33
__Help__ = 'Choose the dimensions of the wire, the number of the iterations and the length of the wire segment.'
34
__Status__ = 'Stable'
35
__Requires__ = ''
36
__Communication__ = 'https://forum.freecadweb.org/viewtopic.php?f=22&t=53781'
37
__Files__ = 'HilbertCurve.svg'
38

39

40
# For the wire
41
import FreeCAD as app
42
import Draft
43

44
# For the gui
45
from PySide import QtGui, QtCore
46

47
class HilbertCurve:
48
    """The class of the Hilbert curve.
49
    
50
    By this class it's possible to create a wire
51
    of a fractal Hilbert curve with
52
    a fixed number of dimensions and iterations.
53
    """
54
    
55
    def __init__(self, dimensions, iterations):
56
        """Initialize the Hilbert curve.
57

58
        Args:
59
            iterations (int): iterations to use in constructing the curve
60
            dimensions (int): number of dimensions
61
        """
62

63
        self.dimensions = dimensions
64
        self.iterations = iterations
65

66
        # minimum and maximum distance along curve
67
        self.min_distance = 0
68
        self.max_distance = 2 ** (self.iterations * self.dimensions) - 1
69

70
        # minimum and maximum coordinate value in any dimension
71
        self.min_coordinate = 0
72
        self.max_coordinate = 2 ** self.iterations - 1
73

74
        # number of points
75
        self.number_of_points = 2 ** (self.iterations * self.dimensions)
76

77
    def point_from_distance(self, distance):
78
        """Return a point in n-dimensional space given a distance along a the curve.
79

80
        Args:
81
            distance (int): integer distance along the curve
82

83
        Returns:
84
            point (iterable of ints): an n-dimensional vector of length dimensions where
85
            each component value is between 0 and 2**iterations-1.
86
        """
87

88
        bit_string = format(distance, 'b').zfill(self.iterations * self.dimensions)  # zero filled binary distance
89
        point = [int(bit_string[i::self.dimensions], 2) for i in range(self.dimensions)]  # transpose of distance
90

91
        # Gray decode: point = point xor (point / 2)
92
        gray = point[self.dimensions-1] >> 1
93
        for i in range(self.dimensions-1, 0, -1):
94
            point[i] ^= point[i-1]
95
        point[0] ^= gray
96

97
        # Undo excess work
98
        q = 2
99
        while q != (2 << (self.iterations-1)):
100
            p = q - 1
101
            for i in range(self.dimensions-1, -1, -1):
102
                if point[i] & q:
103
                    # invert
104
                    point[0] ^= p
105
                else:
106
                    # exchange
107
                    gray = (point[0] ^ point[i]) & p
108
                    point[0] ^= gray
109
                    point[i] ^= gray
110
            q <<= 1
111

112
        return point
113

114
    def get_min_distance(self):
115
        """Return the minimum distance along the curve."""
116
        return self.min_distance
117

118
    def get_max_distance(self):
119
        """Return the maximum distance along the curve."""
120
        return self.max_distance
121

122
    def get_min_coordinate(self):
123
        """Return the minimum coordinate value in any dimension."""
124
        return self.min_coordinate
125

126
    def get_max_coordinate(self):
127
        """Return the maximum coordinate value in any dimension."""
128
        return self.max_coordinate
129

130
    def get_number_of_points(self):
131
        """Return the number of points in the curve."""
132
        return self.number_of_points
133

134
    def get_points(self):
135
        """Return the list of points in the curve."""
136
        points = []
137
        for point_number in range(self.number_of_points):
138
            points.append(self.point_from_distance(point_number))
139
        return points
140

141
    def __str__(self):
142
        return f"HilbertCurve(dimensions={self.dimensions}, iterations={self.iterations})"
143

144
    def __repr__(self):
145
        return self.__str__()
146

147

148
class Hilbert_Dialog(QtGui.QDialog):
149
    """The dialog for the Hilbert curve
150
    
151
    This class opens in FreeCAD a dialog to input
152
    the number of dimensions and the number of iterations
153
    to create the Hilbert curve.
154
    OK creates the curve.
155
    CANCEL quit the macro.
156
    """
157

158
    def __init__(self):
159
        super(Hilbert_Dialog, self).__init__()
160
        self.setupUi()
161

162
    def setupUi(self):
163
        self.dimensions = 2
164
        self.iterations = 3
165

166
        self.setGeometry(250, 250, 400, 300)  # Window definition
167
        self.setWindowTitle("Hilbert curve Macro")
168

169
        titleLabel = QtGui.QLabel("Create an Hilbert curve in two or three dimensions")  # Title and subtitle
170
        titleFont = QtGui.QFont()
171
        titleFont.setBold(True)
172
        titleFont.setWeight(75)
173
        titleLabel.setFont(titleFont)
174
        subtitleLabel = QtGui.QLabel("This macro creates a wire in the draft workbench\nwith the shape of an Hilbert curve in two or three dimensions.\nFor example, you can use this wire as a sweep path.\nIt's recommended to apply a radius at the wire,\nin order to obtain a correct sweep generation.")
175
        titleBox = QtGui.QVBoxLayout()
176
        titleBox.addWidget(titleLabel)
177
        titleBox.addWidget(subtitleLabel)
178
        titleBox.insertStretch(-1)
179

180
        dimensionsLabel = QtGui.QLabel("Number of dimensions: ")  # Number of dimensions
181
        self.twoDradioButton = QtGui.QRadioButton()
182
        self.twoDradioButton.setText("2D")
183
        self.twoDradioButton.setChecked(True)
184
        self.threeDradioButton = QtGui.QRadioButton()
185
        self.threeDradioButton.setText("3D")
186
        dimensionsBox=QtGui.QHBoxLayout()
187
        dimensionsBox.addWidget(dimensionsLabel)
188
        dimensionsBox.addWidget(self.twoDradioButton)
189
        dimensionsBox.addWidget(self.threeDradioButton)
190

191
        iterationsLabel = QtGui.QLabel("Iterations:")  # Iterations  and length spins in a grid
192
        self.iterationsSpin = QtGui.QSpinBox()
193
        self.iterationsSpin.setMinimum(1)
194
        self.iterationsSpin.setMaximum(10)
195
        lengthLabel = QtGui.QLabel("Length:")
196
        self.lengthSpin = QtGui.QDoubleSpinBox()
197
        self.lengthSpin.setMinimum(1.0)
198
        self.lengthSpin.setMaximum(999999.000000000000000)
199
        self.lengthSpin.setValue(10.000000000000000)
200
        grid = QtGui.QGridLayout()
201
        grid.setSpacing(10)
202
        grid.addWidget(iterationsLabel, 1, 0)
203
        grid.addWidget(self.iterationsSpin, 1, 1)
204
        grid.addWidget(lengthLabel, 2, 0)
205
        grid.addWidget(self.lengthSpin, 2, 1)
206

207
        okButton = QtGui.QPushButton("OK")  # Ok and Cancel Buttons at right bottom
208
        okButton.clicked.connect(self.onOkButton)
209
        cancelButton = QtGui.QPushButton("Cancel")
210
        cancelButton.clicked.connect(self.onCancelButton)
211
        buttonBox = QtGui.QHBoxLayout()
212
        buttonBox.addStretch()
213
        buttonBox.addWidget(okButton)
214
        buttonBox.addWidget(cancelButton)
215

216
        vbox = QtGui.QVBoxLayout()
217
        vbox.addLayout(titleBox)
218
        vbox.addLayout(dimensionsBox)
219
        vbox.addLayout(grid)
220
        vbox.addLayout(buttonBox)
221
        vbox.setSpacing(30)
222
        vbox.insertStretch(1)
223
        self.setLayout(vbox)
224

225
        self.show()
226

227
    def onOkButton(self):
228
        if self.twoDradioButton.isChecked():
229
            dimensions = 2
230
        else:
231
            dimensions = 3
232
        iterations = self.iterationsSpin.value()
233
        length = self.lengthSpin.value()
234
        HC=HilbertCurve(dimensions, iterations)
235
        points = HC.get_points()
236
        pl = app.Placement()
237
        pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0)
238
        pl.Base = app.Vector(0.0, 0.0, 0.0)
239
        vectors = []
240
        if dimensions == 2:
241
            for point in points:
242
                vectors.append(app.Vector(point[0]*length, point[1]*length, 0.0))
243
        else:
244
            for point in points:
245
                vectors.append(app.Vector(point[0]*length, point[1]*length, point[2]*length))
246
        wire = Draft.makeWire(vectors, placement=pl, closed=False, face=False, support=None)
247
        wire.Label = "Hilbert"
248
        Draft.autogroup(wire)
249
        self.close()
250

251
    def onCancelButton(self):
252
        self.close()
253

254

255
hilbert_dialog = Hilbert_Dialog()
256
hilbert_dialog.exec()
257

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.