Macro Shake Sketch
Description |
---|
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). Macro version: 1.1 Last modified: 2014-10-31 Author: Gaël Ecorchard |
Author |
Gaël Ecorchard |
Download |
None |
Links |
Macros recipes How to install macros How to customize toolbars |
Macro Version |
1.1 |
Date last modified |
2014-10-31 |
FreeCAD Version(s) |
None |
Default shortcut |
None |
See also |
None |
Description
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)