Macro Shake Sketch: Difference between revisions

From FreeCAD Documentation
(Marked this version for translation)
(Updated macro version to 1.1)
Line 20: Line 20:
# A Gaussian noise is introduced in all sketch points and the sketch is then
# A Gaussian noise is introduced in all sketch points and the sketch is then
# solved.
# solved.
# Enter edit mode for a sketch and launch the macro.
# Beware that the sketch can look different because some constraints have
# Beware that the sketch can look different because some constraints have
# several solutions. In this case, just undo.
# several solutions. In this case, just undo.
Line 26: Line 25:
# This file is released under the MIT License.
# This file is released under the MIT License.
# Author: Gaël Ecorchard
# Author: Gaël Ecorchard
# Version: 1.0, 2014-08, first release.
# Version:
# - 1.1, 2014-10-31
# * correct import for Part
# - 1.0, 2014-08, first release.


# Amplitude of the point displacements.
# Amplitude of the point displacements.
Line 39: Line 41:
import FreeCADGui as Gui
import FreeCADGui as Gui
from FreeCAD import Base
from FreeCAD import Base
from FreeCAD import Part
import Part


# For each sketch geometry type, map a list of points to move.
# For each sketch geometry type, map a list of points to move.
geom_points = {
g_geom_points = {
'point': [1],
Base.Vector: [1],
'line': [1, 2], # first point, last point
Part.Line: [1, 2], # first point, last point
'circle': [0, 3], # curve, center
Part.Circle: [0, 3], # curve, center
'arc': [1, 2, 3], # first point, last point, center
Part.ArcOfCircle: [1, 2, 3], # first point, last point, center
}
}


Line 125: Line 127:




def move_points(sketch, geom_index, point_indexes, sigma):
def move_points(sketch, geom_index, sigma):
point_indexes = g_geom_points[type(sketch.Geometry[i])]
# Direct access to sketch.Geometry[index] does not work. This would,
# Direct access to sketch.Geometry[index] does not work. This would,
# however prevent repeated recompute.
# however prevent repeated recompute.
Line 149: Line 152:
sigma = max(get_sketch_dims(sketch)) * displacement_amplitude
sigma = max(get_sketch_dims(sketch)) * displacement_amplitude


for i, geom in enumerate(sketch.Geometry):
for i in range(len((sketch.Geometry))):
if isinstance(geom, Base.Vector):
move_points(sketch, i, sigma)
move_points(sketch, i, geom_points['point'], sigma)
elif isinstance(geom, Part.Line):
move_points(sketch, i, geom_points['line'], sigma)
elif isinstance(geom, Part.Circle):
move_points(sketch, i, geom_points['circle'], sigma)
elif isinstance(geom, Part.ArcOfCircle):
move_points(sketch, i, geom_points['arc'], sigma)
</syntaxhighlight>
</syntaxhighlight>



Revision as of 07:50, 7 March 2016

File:Text-x-python Macro Shake Sketch

Description
Shake a sketch in order to discover its unconstrained parts

Author: Gaël Ecorchard
Author
Gaël Ecorchard
Download
None
Links
Macro Version
1.0
Date last modified
None
FreeCAD Version(s)
None
Default shortcut
None
See also
None

Shake a sketch in order to discover its unconstrained parts. Enter edit mode for a sketch and launch the macro. The macro will add a random noise on all sketch points. The sketch is then solved, constrained parts will retain their position, free parts will move.

But be careful working on a copy of your file because the macro "dismantles all" to display and you may start over.

Script

Macro Shake_Sketch.py

# -*- coding: utf-8 -*-

# FreeCAD macro to shake a sketch in order to discover its unconstrained parts.
#
# A Gaussian noise is introduced in all sketch points and the sketch is then
# solved.
# Beware that the sketch can look different because some constraints have
# several solutions. In this case, just undo.
#
# This file is released under the MIT License.
# Author: Gaël Ecorchard
# Version:
# - 1.1, 2014-10-31
#     * correct import for Part
# - 1.0, 2014-08, first release.

# Amplitude of the point displacements.
# The standard deviation of the Gaussian noise is the largest sketch dimension
# multiplied by this factor.
displacement_amplitude = 0.1

# End of configuration.

from random import gauss

import FreeCADGui as Gui
from FreeCAD import Base
import Part

# For each sketch geometry type, map a list of points to move.
g_geom_points = {
    Base.Vector: [1],
    Part.Line: [1, 2],  # first point, last point
    Part.Circle: [0, 3],  # curve, center
    Part.ArcOfCircle: [1, 2, 3],  # first point, last point, center
}


class BoundingBox(object):
    xmin = None
    xmax = None
    ymin = None
    ymax = None

    def enlarge_x(self, x):
        if self.xmin is None:
            self.xmin = x
            self.xmax = x
            return
        if self.xmin > x:
            self.xmin = x
            return
        if self.xmax < x:
            self.xmax = x
            return

    def enlarge_y(self, y):
        if self.ymin is None:
            self.ymin = y
            self.ymax = y
            return
        if self.ymin > y:
            self.ymin = y
            return
        if self.ymax < y:
            self.ymax = y
            return

    def enlarge_point(self, point):
        self.enlarge_x(point.x)
        self.enlarge_y(point.y)

    def enlarge_line(self, line):
        self.enlarge_x(line.StartPoint.x)
        self.enlarge_x(line.EndPoint.x)
        self.enlarge_y(line.StartPoint.y)
        self.enlarge_y(line.EndPoint.y)

    def enlarge_circle(self, circle):
        self.enlarge_x(circle.Center.x - circle.Radius)
        self.enlarge_x(circle.Center.x + circle.Radius)
        self.enlarge_y(circle.Center.y - circle.Radius)
        self.enlarge_y(circle.Center.y + circle.Radius)

    def enlarge_arc_of_circle(self, arc):
        # TODO: correctly compute the arc extrema (cf. toShape().BoundBox)
        self.enlarge_x(arc.Center.x)
        self.enlarge_y(arc.Center.y)


def get_sketch_dims(sketch):
    bbox = BoundingBox()
    for geom in sketch.Geometry:
        if isinstance(geom, Base.Vector):
            bbox.enlarge_point(geom)
        elif isinstance(geom, Part.Line):
            bbox.enlarge_line(geom)
        elif isinstance(geom, Part.Circle):
            bbox.enlarge_circle(geom)
        elif isinstance(geom, Part.ArcOfCircle):
            bbox.enlarge_arc_of_circle(geom)
    if (bbox.xmin is not None) and (bbox.ymin is not None):
        return bbox.xmax - bbox.xmin, bbox.ymax - bbox.ymin
    else:
        return 0, 0


def add_noise(point, sigma):
    """Add a Gaussian noise with standard deviation sigma"""
    point.x = gauss(point.x, sigma)
    point.y = gauss(point.y, sigma)


def move_points(sketch, geom_index, sigma):
    point_indexes = g_geom_points[type(sketch.Geometry[i])]
    # Direct access to sketch.Geometry[index] does not work. This would,
    # however prevent repeated recompute.
    for point_index in point_indexes:
        point = sketch.getPoint(geom_index, point_index)
        add_noise(point, sigma)
        sketch.movePoint(geom_index, point_index, point)

view_provider = Gui.activeDocument().getInEdit()

# Don't know how to exit from a macro.
do_move = True
if not view_provider:
    do_move = False

if do_move:
    sketch = view_provider.Object

    if sketch.TypeId != 'Sketcher::SketchObject':
        do_move = False

if do_move:
    sigma = max(get_sketch_dims(sketch)) * displacement_amplitude

    for i in range(len((sketch.Geometry))):
        move_points(sketch, i, sigma)
Other languages: