This post contains my notes of the FCStd file format used by FreeCAD. It seems there isn't any official documentation of the format (and no, the source code is not a documentation) used by FreeCAD. It's a shame since FreeCAD is probably the only open source software that features parametric CAD modeling with a usable interface. Yet, the developers are busy improving and fixing bugs in the software and no one took time to document the file format used.
Beware however that the file format is not a standard, so it'll likely evolve with FreeCAD versions. This post is based on FreeCAD 0.20
For inter exchange with other CAD software, the industry created the STEP file format. I won't describe what STEP is capable of, but I'll point what it's not:
It's not a parametric file format. It describes the final surfaces of an object, but not how it's made. So, once you receive a STEP file, you can't modify the object (you can use functions on top, but none of the functions used to build the object is available or described).
So, let's say you want to give a model of a drawer to a customer. You'll build an object with your customer's desired dimensions, and export a STEP file. If the customer wants to update one dimension, she'll have to ask you again to regenerate a STEP file with the updated length. She's not able to modify it by herself. It'll take double time to achieve this simple operation, the customer asking for a change, you doing the update in your software and sending the modified file back... It's not efficient.
A parametric CAD file format on the other side would let the customer modify some parameters on her own. It's easier to deal with. It would also help democratize the file format.
A parametric file format is often linked with a way of thinking your object. You will not design your object the same if the only tool you have is a mesh editor or a CSG (understand: boolean) engine. All CAD software have their own way to build objects (although it seems to converge to the same way of thinking). Proprietary software don't want to disclose the internal working of their geometry engine, or simply, don't want to maintain the functions implemented in a version in later versions. Also, they enjoy vendor lock in as it improves their sales (if all you have is a solidworks file, you'll need Solidworks to open it).
Actually, a true, industry wide standard would be better. But since it's not happening, FreeCAD at least does not lock you in. You'll be able to open, modify and also generate a document since it's open. I'm documenting the file format from what I understood from it since I'm using it in my Parametizer tool
A FreeCAD file (with extension
.FCStd) is mainly a ZIP archive. It contains multiple files listed below:
Of all thoses files, only one is important for FreeCAD to bootstrap the document, it's
Document.xml (see below).
The files with the name containing
ColorArray are probably used for presentation of the document in the GUI. They are formatted this way:
00000000 01 00 00 00 00 RR GG BB |........|
The first 32 bits contains the element count of the array (in the example above, it's 1, stored in little endian).
Then, you'll find
N elements (in this case, RGB colors, stored as 32 bits, in format
0x00RRGGBB, with 8 bits per color component)
FreeCAD does not need any of these files to open the document. So it's safe to skip them when generating your own file, FreeCAD will just use default value in that case.
BRP files contains the BREP format of OpenCascade (OCC) serialization of sketches. This format is described in OpenCascade which is the geometry engine library used by FreeCAD to model objects.
As far as I've tried, FreeCAD is able to regenerate these files as well by itself. However, they contains names for geometry features (edges, face, etc...) and when applying functions on your object, these names are what's used for referring the function's operands. Said differently, if you skip these files, you'll have to re-select all the functions operands to re-attach/design faces to recompute the object which is painful.
As a side node, this is the main reason for breakage of your model when you update some value in the bottom of the function stack in FreeCAD, since the names are not "constant". There is currently work in FreeCAD to overcome these topological naming issues and hopefully another better solution will arise
Again, this file is not required by FreeCAD to open your document. So let's get back to it in a future version of this post.
This is the main file that FreeCAD is parsing when opening the
It's an XML document, that's following this format:
<Document> <Properties> <Property> <String/> </Property> </Properties> <Objects> <ObjectDeps> <Dep/> </ObjectDeps> <Object/> </Objects> <ObjectData> <Object> <Extensions> <Extension/> </Extensions> <Properties> <Property> See below for the possible properties </Property> </Properties> </Object> </ObjectData> </Document>
The document is made of
properties, a header declaring the
objects and the related
object data referencing them later on.
Each object can have properties, extensions, or dependencies.
Each list above contains a
Count attribute containing the number of elements in the list (why?) and a
TransientCount for some nodes which are
_-prefixed. I don't know why, and it seems they are not required.
The document's properties contains the author name, the creation date and so on (open a Document.xml to see a list of properties). If you generate your own format, it seems none of them is mandatory.
The current list in a default document is:
Comment, Company, CreatedBy, CreationDate, Id, Label, LastModifiedBy, LastModifiedDate, License, LicenseURL, Material, Meta, ShowHidden, TipName, Uid
Then the objects are listed first by their dependencies
ObjectDeps. So, for example, the object
Body will depends on
Origin is a datum that will depend on
Finally, the objects are listed and they are indexed by a
name and a
type attribute. I'm not listing all the possible types here, since I don't know them. For now, I've found
PartDesign::Body, App::Origin, App::Line, App::Plane, Sketcher::SketchObject. They also have a
id attribute, which I guess, is only for logs.
ObjectData node then describe the actual content of each object listed above. I'll not describe the default object (like origin, axis and so on) since they are unlikely to change, a simple copy and paste here will work for you.
You'll finally find here the actual
object of interest whose
name attribute match the one listed above.
For example, a sketch is an object that's attached to a plane. So it'll have an
Part::AttachExtension extension node and then, many linked properties like
AttacherType (with value
AttachmentOffset which contains the distance to the referred plane.
If the sketch has constraints, it'll have a
Sketcher::PropertyConstraintList property containing a
ConstraintList node with many
If the sketch is linking to external geometry, you'll find a
App::PropertyLinkSubList property and so on.
Of the other properties of interest, there are:
Part::PropertyPartShapethat links to the .BRP file (BREP) file, describing the shape of the sketch. But, if the file is not found, it can be recreated when opening the sketch in the sketch editor.
App::PropertyStringwhich is what is displayed to the user in the GUI interface
Part::PropertyGeometryListthat contains a description of the actual geometry in the sketch (like lines, arcs, and so on).
Supportto specify on which plane the sketch is living
App::PropertyBoolwhich contains the visible flag for this sketch.
Geometry property contains a
GeometryList parent node of
Geometry has a type (any of
Depending on the type, the child node will be different.
For example, a
Part::GeomLineSegment will contain
<LineSegment StartX="12.5000000000000036" StartY="10.0000000000000000" StartZ="0.0000000000000000" EndX="-12.5000000000000000" EndY="10.0000000000000000" EndZ="0.0000000000000000"/>
Part::GeomArcOfCircle will contain
<ArcOfCircle CenterX="12.5000000000000000" CenterY="0.0000000000000000" CenterZ="0.0000000000000000" NormalX="0.0000000000000000" NormalY="0.0000000000000000" NormalZ="1.0000000000000000" AngleXU="-0.0000000000000000" Radius="10.0000000000000000" StartAngle="4.7123889803846897" EndAngle="7.8539816339744828"/>
The parameters are self describing, the angle are in radian. Without any constraint, FreeCAD will use the geometry as written. If there are constraint in the file, the geometry will be recomputed (so the positions itself does not matter in that case).
One of the most interesting aspect of FreeCAD is the ability to specify constraint on geometry, and the "engine" will ensure all constraint are respected to build the geometry. This way, the parameters of your model are all justified and not guessed or approximated. Constraints are stored in a constraint list written this way:
<ConstraintList count="5"> <Constrain Name="" Type="7" Value="4.9999999999999991" First="-1" FirstPos="1" Second="0" SecondPos="1" Third="-2000" ThirdPos="0" LabelDistance="-1.7815394401550293" LabelPosition="-1.1905069351196289" IsDriving="1" IsInVirtualSpace="0" IsActive="1" /> <Constrain Name="" Type="8" Value="-4.9999999999999991" First="-1" FirstPos="1" Second="0" SecondPos="1" Third="-2000" ThirdPos="0" LabelDistance="6.6666665077209473" LabelPosition="0.0000000000000000" IsDriving="1" IsInVirtualSpace="0" IsActive="1" /> <Constrain Name="" Type="6" Value="8.9999999999999982" First="0" FirstPos="0" Second="-2000" SecondPos="0" Third="-2000" ThirdPos="0" LabelDistance="-3.7297201156616211" LabelPosition="0.0991386175155640" IsDriving="1" IsInVirtualSpace="0" IsActive="1" /> <Constrain Name="" Type="9" Value="-1.5707963267948966" First="-1" FirstPos="2" Second="0" SecondPos="1" Third="-2000" ThirdPos="0" LabelDistance="-1.2334032058715820" LabelPosition="0.0000000000000000" IsDriving="1" IsInVirtualSpace="0" IsActive="1" /> <Constrain Name="" Type="1" Value="0.0000000000000000" First="0" FirstPos="2" Second="1" SecondPos="1" Third="-2000" ThirdPos="0" LabelDistance="6.6666665077209473" LabelPosition="0.0000000000000000" IsDriving="1" IsInVirtualSpace="0" IsActive="1" /> </ConstraintList>
The format for any constraint is the same. Constraint are typed by a
Type, and they have a unique
Value (or none for the "same position constraint"). The geometry they refer to is specified in the
First/Second/Third (the value is the index of the geometry in the XML file) and the sub-part is specified in the
This is a bit hard to grasp without an example, so let's take an example:
Let's say you want to specify an "same position constraint" for two points. In that case, you'll need to refer to the 2 objects containing the points (for example segment 1 and segment 2) and also to the end of the each object (for the second end of the segment 1 and the first end of the segment 2). In that case, the constraint will be type = 1, value = 0 (does not make any sense here to have any value), first = 1 (first segment), first pos = 2 (second end of first segment), second = 2 (second segment) and second pos = 1 (first end of the second segment).
Few implementation details here: when it does not make sense a parameter still exists and will hold a default value. For the geometry index (
Third), the default value is
-1 refers to the axis (with the position being 1 or 2 for X or Y axis). When not applicable, it can be 0.
-2 have a special meaning for static geometry that's not listed in the object (like point of origin and axis lines).
Here's a table describing the constraint type and the expected value of the parameters:
|Same position||1||always 0||Index of geometry + 1||Index of the point in the geometry + 1||Index of other geometry +1||Index of the point in the other geometry + 1||-2000||0|
|Horizontal||2||always 0||Index of geometry + 1||0||-2000||0||-2000||0|
|Vertical||3||always 0||Index of geometry + 1||0||-2000||0||-2000||0|
|Length||6||length in mm||Index of geometry + 1||0||-2000||0||-2000||0|
|Horizontal length||7||length in mm||Index of geometry + 1||Index of the point in the geometry + 1||Index of other geometry +1||Index of the point in the other geometry + 1||-2000||0|
|Vertical length||8||length in mm||Index of geometry + 1||Index of the point in the geometry + 1||Index of other geometry +1||Index of the point in the other geometry + 1||-2000||0|
|Angle||9||angle in radian||Index of geometry + 1||Index of the point in the geometry + 1||Index of other geometry +1||Index of the point in the other geometry + 1||-2000||0|
|Radius||11||length in mm||Index of geometry + 1||0||-2000||0||-2000||0|
|Point on||13||always 0||Index of geometry + 1||Index of the point in the geometry + 1||Index of other geometry +1||Index of the point in the other geometry + 1||-2000||0|
LabelPosition are only GUI stuff and IMHO, shouldn't be stored here. It's the distance from the referred object where the constraint rendering is done (for example, the radius symbol and the radius length value are displayed at 10mm from the arc and at 45° from the X axis).