I wrote a little macro that could help people visualize where the degrees of freedom are in a sketch. The principle is very simple, a random noise is added in all points and the sketch is then solved. What is free moves what is constrained does not.

I'll post it on the wiki if you find it useful and bug-free (at least with only few bugs).

I you think it's a good idea, I'll do a command out of it.

If someone is motivating to create an icon, please do it!

Gaël

FCMacro extension are not allowed as attachment so I include the macro directly:

Code: Select all

```
# -*- 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.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
from FreeCAD import Part
# For each sketch geometry type, map a list of points to move.
geom_points = {
'point': [1],
'line': [1, 2], # first point, last point
'circle': [0, 3], # curve, center
'arc': [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, point_indexes, sigma):
# 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, geom in enumerate(sketch.Geometry):
if isinstance(geom, Base.Vector):
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)
```