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:
.brp
extensionOf 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 FCStd
document.
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
and Sketch
. Similarly, Origin
is a datum that will depend on X_Axis
,...
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.
The 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 Attacher::AttachEnginePlane
), 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 Constraint
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:
Shape
of type Part::PropertyPartShape
that 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.Label
of type App::PropertyString
which is what is displayed to the user in the GUI interfaceGeometry
of type Part::PropertyGeometryList
that contains a description of the actual geometry in the sketch (like lines, arcs, and so on).Support
to specify on which plane the sketch is livingVisibility
of type App::PropertyBool
which contains the visible flag for this sketch.The Geometry
property contains a GeometryList
parent node of Geometry
children.
Each Geometry
has a type (any of Part::GeomArcOfCircle
, Part::GeomLineSegment
, ...).
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"/>
while a 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 FirstPos/SecondPos/ThirdPos
.
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 (First
, Second
or Third
), the default value is -2000
. -1
refers to the axis (with the position being 1 or 2 for X or Y axis). When not applicable, it can be 0.
The position -1
or -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:
Constraint name | Type | Value | First | FirstPos | Second | SecondPos | Third | ThirdPos |
---|---|---|---|---|---|---|---|---|
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 |
LabelDistance
and 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).