Skip to content

Latest commit

 

History

History
328 lines (240 loc) · 10.8 KB

Macro_HilbertCurve.md

File metadata and controls

328 lines (240 loc) · 10.8 KB

Macro HilbertCurve

{{Macro |Name=HilbertCurve |Description=This macro creates an Hilbert curve wire in 2 or 3 dimensions with many iterations. |Author=Simone Bignetti |Download=[https://wiki.freecadweb.org/images/6/69/Hilbert_curve_icon.png toolbar icon] |Date=2021-02-13 |Version=1.2.0 |FCVersion= 0.16 to 0.19 }}

Description

This macro creates a Hilbert curve wire in 2 or 3 dimensions with many iterations.

Usage

  1. Start the macro in a FreeCAD document.
  2. In the dialog that opens choose the parameters for the Hilbert curve:
    • Select if the curve should be 2 dimensional: {{CheckBox|TRUE|2D}}, or 3 dimensional: {{CheckBox|FALSE|3D}}.
    • Specify the number of iterations {{SpinBox|1}}. Attention! Increasing the number of iterations will also increase the computation time.
    • Specify the length of the wire segment: {{SpinBox|10.00}}.
  3. Click OK to create the wire or CANCEL to exit the macro.

The Hilbert wire in 3D and 2 iterations.

You can use a Hilbert curve as the path for a Part Sweep, but it's better to apply a radius to the wire first, or the sweep will be badly formed.

The wire of the Hilbert Curve with a radius.

Finding the right radius may requires some testing. It depends on the length of the curve segments and the shape of the profile you want to sweep.

A solid obtained by the sweep of a circle by the Hilbert curve.

Script

ToolBar Icon

Macro_HilbertCurve.FCMacro

{{MacroCode|code=

-- coding: utf-8 --

Copyright (c) 2020 Simone Bignetti, Gottolengo Italy (simone.b)

This file is part of the FreeCAD CAx development system.

This library is free software; you can redistribute it and/or

modify it under the terms of the GNU Lesser General Public

License as published by the Free Software Foundation; either

version 2.1 of the License, or (at your option) any later version.

This library 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

Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public

License along with this library; if not, write to the Free Software

Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301

USA

You can contact me by mail at simone.bignetti@linux.it

Name = 'HilbertCurve' Comment = 'This macro creates an Hilbert curve wire in 2 or 3 dimensions with many iterations.' Author = 'Simone Bignetti' Version = '1.2.0' Date = '2020-12-29' License = 'GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999' Web = 'https://wiki.freecadweb.org/Macro_HilbertCurve' Wiki = 'https://wiki.freecadweb.org/Macro_HilbertCurve' Icon = 'HilbertCurve.svg' Help = 'Choose the dimensions of the wire, the number of the iterations and the length of the wire segment.' Status = 'Stable' Requires = '' Communication = 'https://forum.freecadweb.org/viewtopic.php?f=22&t=53781' Files = 'HilbertCurve.svg'

For the wire

import FreeCAD as app import Draft

For the gui

from PySide import QtGui, QtCore

class HilbertCurve: """The class of the Hilbert curve.By this class it's possible to create a wire of a fractal Hilbert curve with a fixed number of dimensions and iterations. """def init(self, dimensions, iterations): """Initialize the Hilbert curve. Args: iterations (int): iterations to use in constructing the curve dimensions (int): number of dimensions """

self.dimensions = dimensions self.iterations = iterations

minimum and maximum distance along curve

    self.min_distance = 0
    self.max_distance = 2 ** (self.iterations * self.dimensions) - 1

minimum and maximum coordinate value in any dimension

    self.min_coordinate = 0
    self.max_coordinate = 2 ** self.iterations - 1

number of points

    self.number_of_points = 2 ** (self.iterations * self.dimensions)

def point_from_distance(self, distance): """Return a point in n-dimensional space given a distance along a the curve. Args: distance (int): integer distance along the curve Returns: point (iterable of ints): an n-dimensional vector of length dimensions where each component value is between 0 and 2**iterations-1. """

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

Gray decode: point = point xor (point / 2)

    gray = point[self.dimensions-1] >> 1
    for i in range(self.dimensions-1, 0, -1):
        point[i] ^= point[i-1]
    point[0] ^= gray

Undo excess work

    q = 2
    while q != (2 << (self.iterations-1)):
        p = q - 1
        for i in range(self.dimensions-1, -1, -1):
            if point[i] & q:
                # invert
                point[0] ^= p
            else:
                # exchange
                gray = (point[0] ^ point[i]) & p
                point[0] ^= gray
                point[i] ^= gray
        q <<= 1

return point

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

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

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

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

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

def get_points(self): """Return the list of points in the curve.""" points = [] for point_number in range(self.number_of_points): points.append(self.point_from_distance(point_number)) return points

def str(self): return f"HilbertCurve(dimensions={self.dimensions}, iterations={self.iterations})"

def repr(self): return self.str()

class Hilbert_Dialog(QtGui.QDialog): """The dialog for the Hilbert curveThis class opens in FreeCAD a dialog to input the number of dimensions and the number of iterations to create the Hilbert curve. OK creates the curve. CANCEL quit the macro. """

def init(self): super(Hilbert_Dialog, self).init() self.setupUi()

def setupUi(self): self.dimensions = 2 self.iterations = 3

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

titleLabel = QtGui.QLabel("Create an Hilbert curve in two or three dimensions") # Title and subtitle titleFont = QtGui.QFont() titleFont.setBold(True) titleFont.setWeight(75) titleLabel.setFont(titleFont) 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.") titleBox = QtGui.QVBoxLayout() titleBox.addWidget(titleLabel) titleBox.addWidget(subtitleLabel) titleBox.insertStretch(-1)

dimensionsLabel = QtGui.QLabel("Number of dimensions: ") # Number of dimensions self.twoDradioButton = QtGui.QRadioButton() self.twoDradioButton.setText("2D") self.twoDradioButton.setChecked(True) self.threeDradioButton = QtGui.QRadioButton() self.threeDradioButton.setText("3D") dimensionsBox=QtGui.QHBoxLayout() dimensionsBox.addWidget(dimensionsLabel) dimensionsBox.addWidget(self.twoDradioButton) dimensionsBox.addWidget(self.threeDradioButton)

iterationsLabel = QtGui.QLabel("Iterations:") # Iterations and length spins in a grid self.iterationsSpin = QtGui.QSpinBox() self.iterationsSpin.setMinimum(1) self.iterationsSpin.setMaximum(10) lengthLabel = QtGui.QLabel("Length:") self.lengthSpin = QtGui.QDoubleSpinBox() self.lengthSpin.setMinimum(1.0) self.lengthSpin.setMaximum(999999.000000000000000) self.lengthSpin.setValue(10.000000000000000) grid = QtGui.QGridLayout() grid.setSpacing(10) grid.addWidget(iterationsLabel, 1, 0) grid.addWidget(self.iterationsSpin, 1, 1) grid.addWidget(lengthLabel, 2, 0) grid.addWidget(self.lengthSpin, 2, 1)

okButton = QtGui.QPushButton("OK") # Ok and Cancel Buttons at right bottom okButton.clicked.connect(self.onOkButton) cancelButton = QtGui.QPushButton("Cancel") cancelButton.clicked.connect(self.onCancelButton) buttonBox = QtGui.QHBoxLayout() buttonBox.addStretch() buttonBox.addWidget(okButton) buttonBox.addWidget(cancelButton)

vbox = QtGui.QVBoxLayout() vbox.addLayout(titleBox) vbox.addLayout(dimensionsBox) vbox.addLayout(grid) vbox.addLayout(buttonBox) vbox.setSpacing(30) vbox.insertStretch(1) self.setLayout(vbox)

self.show()

def onOkButton(self): if self.twoDradioButton.isChecked(): dimensions = 2 else: dimensions = 3 iterations = self.iterationsSpin.value() length = self.lengthSpin.value() HC=HilbertCurve(dimensions, iterations) points = HC.get_points() pl = app.Placement() pl.Rotation.Q = (0.0, 0.0, 0.0, 1.0) pl.Base = app.Vector(0.0, 0.0, 0.0) vectors = [] if dimensions == 2: for point in points: vectors.append(app.Vector(point[0]*length, point[1]*length, 0.0)) else: for point in points: vectors.append(app.Vector(point[0]*length, point[1]*length, point[2]*length)) wire = Draft.makeWire(vectors, placement=pl, closed=False, face=False, support=None) wire.Label = "Hilbert" Draft.autogroup(wire) self.close()

def onCancelButton(self): self.close()

hilbert_dialog = Hilbert_Dialog() hilbert_dialog.exec() }}


documentation index > Macro HilbertCurve