Change color of mesh displayed by the function CGAL::draw() - cgal

Problem descriptionI wonder if there is a way to change the color of mesh displayed by the function CGAL::draw(). I have a Surface_mesh, I want to draw it with CGAL. So I use the function CGAL::draw(), but the color of mesh is blue, which is not pretty in my view. I tried to change the code of CGAL to change the color. I found a functor called DefaultColorFunctorFaceGraph in a header file called draw_face_graoh.h, there is an annotation above the definition of DefaultColorFunctorFaceGraph, which says "// Default color functor; user can change it to have its own face color". I change the functor, in which I change the return value to CGAL::IO::gray(), but it doesn't work at all, the color of mesh is still blue.So can I change the color of mesh by changing the code of CGAL? Is it necessary to change lower level code such as some codes calling OpenGL?
Code
Here is an example about the way I use the function draw().
#include<iostream>
#include<fstream>
#include<CGAL/Surface_mesh.h>
#include<CGAL/draw_surface_mesh.h>
#include<CGAL/Exact_predicates_inexact_constructions_kernel.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel kernel;
typedef kernel::Point_3 point;
typedef CGAL::Surface_mesh<point> Mesh;
int main() {
std::ifstream fin("test.off");
Mesh mesh;
fin >> mesh;
CGAL::draw(mesh);
}
The file called test.off is as follws.
OFF
4 4 0
0 0 1
0 0 0
1 0 0
0 1 0
3 2 0 1
3 1 0 3
3 1 3 2
3 3 0 2
Here is the changed functor.
namespace CGAL
{
// Default color functor; user can change it to have its own face color
struct DefaultColorFunctorFaceGraph
{
template<typename Graph>
CGAL::IO::Color operator()(const Graph&,
typename boost::graph_traits<Graph>::face_descriptor fh) const
{
if (fh == boost::graph_traits<Graph>::null_face()) // use to get the mono color
//return CGAL::IO::Color(100,125,200); // R G B between 0-255
return CGAL::IO::gray();//Here changed
return get_random_color(CGAL::get_default_random());
}
};
Runtime environment
IDE: VS 2017
Solution configuration: Release x64
CGAL version: 5.3

Finally I found a struct named Basic_viewer_qt in a header file called Basic_viewer_qt.h. By changing the values of variables m_faces_mono_color and m_ambient_color in this struct, color of mesh can be changed.

Related

Why does my usb HID output rubbish? STM32Cube

I'm trying to make a force feedback wheel, but software isn't my cup of tea.
This is supposed to toggle button 0 and it doesn't.
typedef struct{
uint8_t buttons;
int8_t relativeMvt;
}steer_t;
steer_t steer = {0, 0};
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
if(steer.buttons) steer.buttons = 0b00000000;
else steer.buttons = 0b00000001;
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, steer, sizeof(steer));
HAL_Delay(500);
}
My Report descriptor (this is the first time I'm using one):
Running that code, the buttons are static "ON" like so:
They DO change (randomly) only when the "relativeMvt" variable is changed, very weird.
What I've tried:
Swap relativeMvt and buttons in the typeDef
Check the report descriptor size etc
Cry
#define USBD_CUSTOMHID_OUTREPORT_BUF_SIZE 2
#define USBD_CUSTOM_HID_REPORT_DESC_SIZE 45
#define CUSTOM_HID_EPIN_SIZE 2
What do I have to change to make it work? Thanks!
I've solved it. I was missing:
#include "usbd_customhid.h"
and I forgot the "&" when passing my variables:
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, &steer, sizeof(steer));

How to use write_ply_with_properties() with Point_set_3

I have a CGAL::Point_set_3 point set with point normal and color. I would like to save all properties to a PLY file, using write_ply_with_properties() function.
My goal is to make the full version work (see code below), but even the simple version doesn't compile, with the same error as the full version.
I work on Linux with CGAL release 4.14 and gcc 7.4.0.
Here is the code:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
#include <tuple> // for std::tie
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
typedef CGAL::Point_set_3<Point> Point_set;
int main(int argc, char*argv[])
{
Point_set points;
points.insert(Point(1., 2., 3.));
points.insert(Point(4., 5., 6.));
// add normal map
points.add_normal_map();
auto normal_map = points.normal_map();
// add color map
typedef Point_set::Property_map< Vector > ColorMap;
bool success = false;
ColorMap color_map;
std::tie(color_map, success) =
points.add_property_map< Vector >("color");
assert(success);
// populate normal and color map
for(auto it = points.begin(); it != points.end(); ++it)
{
normal_map[*it] = Vector(10., 11., 12.);
color_map[*it] = Vector(20., 21., 22.);
}
std::ofstream out("out.ply");
#if 1
// simple version
if(!out || !CGAL::write_ply_points_with_properties(
out,
points.points(), // const PointRange
CGAL::make_ply_point_writer(points.point_map())))
#else
// full version
if(!out || !CGAL::write_ply_points_with_properties(
out,
points.points(), // const PointRange
CGAL::make_ply_point_writer(points.point_map()),
CGAL::make_ply_normal_writer(points.normal_map()),
std::make_tuple(color_map,
CGAL::PLY_property< double >("red"),
CGAL::PLY_property< double >("green"),
CGAL::PLY_property< double >("blue"))))
#endif
{
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
The compilation error is:
...
/usr/include/boost/property_map/property_map.hpp:303:54: error: no match for ‘operator[]’ (operand types are ‘const CGAL::Point_set_3<CGAL::Point_3<CGAL::Epick> >::Property_map<CGAL::Point_3<CGAL::Epick> >’ and ‘const CGAL::Point_3<CGAL::Epick>’)
Reference v = static_cast<const PropertyMap&>(pa)[k];
CGAL-4.14/include/CGAL/Surface_mesh/Properties.h:567:15: note: candidate: CGAL::Properties::Property_map_base<I, T, CRTP_derived_class>::reference CGAL::Properties::Property_map_base<I, T, CRTP_derived_class>::operator[](const I&) [with I = CGAL::Point_set_3<CGAL::Point_3<CGAL::Epick> >::Index; T = CGAL::Point_3<CGAL::Epick>; CRTP_derived_class = CGAL::Point_set_3<CGAL::Point_3<CGAL::Epick> >::Property_map<CGAL::Point_3<CGAL::Epick> >; CGAL::Properties::Property_map_base<I, T, CRTP_derived_class>::reference = CGAL::Point_3<CGAL::Epick>&]
reference operator[](const I& i)
^~~~~~~~
CGAL-4.14/include/CGAL/Surface_mesh/Properties.h:567:15: note: no known conversion for argument 1 from ‘const CGAL::Point_3<CGAL::Epick>’ to ‘const CGAL::Point_set_3<CGAL::Point_3<CGAL::Epick> >::Index&’
How can I fix it?
The problem in your code is that you are using the method points() of CGAL::Point_set_3 which returns a range of points of type CGAL::Point_set_3::Point_range, whereas the property maps that you use (points.point_map(), etc.) are directly applied to a type CGAL::Point_set_3.
So you should simply call the write_ply_points_with_properties() on points, not on points.points().
Note also that if you store your colors on simple types (for example, using three Point_set_3 properties typed unsigned char), you can take advantage of the function CGAL::write_ply_point_set() that will automatically write all the simply-typed properties it finds, which makes it quite straightforward to use (just do CGAL::write_ply_point_set(out, points) and you're done).
One last thing that is really a detail not related to your problem, but you should avoid using the CGAL::Vector_3 for storing anything else than an actual geometric 3D vector (like colors in your case). That makes your code harder to read and is also quite an ineffective way to store colors if they are encoded as integer values between 0 and 255 (which is what unsigned char is for).

How to Optimally Shift Large Arrays n Number of Incidences

I am creating my own version of a music visualizer that responds to the frequency of music; a common project. I am using 2 strips of Neopixels, each with 300 LEDs making a total of 600 LEDs.
I have written functions, shown below, that create the desired affect of having a pulse of light travel down the strips independently. However, when running in real time with music, the updates per second is too slow to create a nice pulse; it looks choppy.
I believe the problem is the number of operations that must be preformed when the function is called. For each call to the function, a 300 value array per strip must be shifted 5 indices and 5 new values added.
Here is an illustration of how the function currently works:
-Arbitrary numbers are used to fill the array
-A shift of 2 indices shown
-X represents an index with no value assigned
-N represents the new value added by the function
Initial array: [1][3][7][2][9]
Shifted array: [X][X][1][3][7]
New array: [N][N][1][3][7]
Here if my code. Function declarations below loop(). I am using random() to trigger a pulse for testing purposes; no other functions were included for brevity.
#include <FastLED.h>
// ========================= Define setup parameters =========================
#define NUM_LEDS1 300 // Number of LEDS in strip 1
#define NUM_LEDS2 300 // Number of LEDS in strip 1
#define STRIP1_PIN 6 // Pin number for strip 1
#define STRIP2_PIN 10 // Pin number for strip 2
#define s1Band 1 // String 1 band index
#define s2Band 5 // String 2 band index
#define numUpdate 5 // Number of LEDs that will be used for a single pulse
// Colors for strip 1: Band 2 (Index 1)
#define s1R 255
#define s1G 0
#define s1B 0
// Colors for strip 2: Band 6 (Index 5)
#define s2R 0
#define s2G 0
#define s2B 255
// Create the arrays of LEDs
CRGB strip1[NUM_LEDS1];
CRGB strip2[NUM_LEDS2];
void setup() {
FastLED.addLeds<NEOPIXEL, STRIP1_PIN>(strip1, NUM_LEDS1);
FastLED.addLeds<NEOPIXEL, STRIP2_PIN>(strip2, NUM_LEDS2);
FastLED.setBrightness(10);
FastLED.clear();
FastLED.show();
}
void loop() {
int num = random(0, 31);
// Pulse strip based on random number for testing
if (num == 5) {
pulseDownStrip1();
}
pulseBlack1();
}
// ======================= FUNCTION DECLARATIONS =======================
// Pulse a set of colored LEDs down the strip
void pulseDownStrip1() {
// Move all current LED states by n number of leds to be updated
for (int i = NUM_LEDS1 - 1; i >= 0; i--) {
strip1[i] = strip1[i - numUpdate];
}
// Add new LED values to the pulse
for (int j = 0; j < numUpdate; j++) {
strip1[j].setRGB(s1R, s1G, s1B);
}
FastLED.show();
}
// Pulse a set of black LEDs down the strip
void pulseBlack1(){
// Move all current LED states by n number of leds to be updated
for (int i = NUM_LEDS1 - 1; i >= 0; i--) {
strip1[i] = strip1[i - numUpdate];
}
// Add new LED values to the pulse
for (int j = 0; j < numUpdate; j++) {
strip1[j].setRGB(0, 0, 0);
}
FastLED.show();
}
I am looking for any suggestions regarding optimizing this operation. Through my research, copying the desired values to a new array rather than shifting the existing array seems to be a faster operation.
If you have any advice on optimizing this process, or alternate methods to produce the same animation, I would appreciate the help.
The secret is to not shift it. Shift where you start reading it instead. Keep track of a separate variable that keeps the start position and alter your reading through the array to start there, roll back over to zero when it gets to the array length, and stop one short of where it starts.
Google the term "circular buffer" Look at the Arduino HardwareSerial class for a decent implementation example.

Do constrained refinement with CGAL isotropic_remeshing

I'd like to do refinement of eg a simple cube (from a .off); there are a few ways but the ones suitable for what I want to do next end up with 'wrinkles', ie the object shape gets distorted.
This way below promises to allow the boundaries (shape?) of the object to be preserved, permitting what you'd expect of refinement, to just add more edges and vertices:
http://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2isotropic_remeshing_example_8cpp-example.html
I want an edge constraint map (and if that isn't sufficient then I'll want a vertex constraint map as well) but can't figure out the template abstractions well enough. I tried an OpenMesh Constrained_edge_map from a different CGAL example, but that's too different and won't compile. What I'm asking for is an edge map and maybe a vertex map that I can feed to the call:
PMP::isotropic_remeshing(
faces(mesh),
target_edge_length,
mesh,
PMP::parameters::number_of_iterations(nb_iter)
.protect_constraints(true)//i.e. protect border, here
);
I'm using CGAL 4.8.1, the latest at time of writing. Thanks.
Here is a minimal example to remesh a triangulated cube:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Mesh_3/dihedral_angle_3.h>
#include <boost/foreach.hpp>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef boost::graph_traits<Mesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Mesh>::edge_descriptor edge_descriptor;
namespace PMP=CGAL::Polygon_mesh_processing;
int main(int, char* argv[])
{
std::ifstream input(argv[1]);
Mesh tmesh;
input >> tmesh;
double target_edge_length = 0.20;
unsigned int nb_iter = 10;
// give each vertex a name, the default is empty
Mesh::Property_map<edge_descriptor,bool> is_constrained =
tmesh.add_property_map<edge_descriptor,bool>("e:is_constrained",false).first;
//detect sharp features
BOOST_FOREACH(edge_descriptor e, edges(tmesh))
{
halfedge_descriptor hd = halfedge(e,tmesh);
if ( !is_border(e,tmesh) ){
double angle = CGAL::Mesh_3::dihedral_angle(tmesh.point(source(hd,tmesh)),
tmesh.point(target(hd,tmesh)),
tmesh.point(target(next(hd,tmesh),tmesh)),
tmesh.point(target(next(opposite(hd,tmesh),tmesh),tmesh)));
if ( CGAL::abs(angle)<100 )
is_constrained[e]=true;
}
}
//remesh
PMP::isotropic_remeshing(
faces(tmesh),
target_edge_length,
tmesh,
PMP::parameters::number_of_iterations(nb_iter)
.edge_is_constrained_map(is_constrained) );
std::ofstream out("out.off");
out << tmesh;
return 0;
}

What's the format of boost.serialization's output

I tried to serialize a vector and a map container and output their value by cout. However, it is hard for me to get the meaning of boost's output. My code looks like this:
#include <iostream>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>
#include <boost/assign.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <sstream>
#include <fstream>
using namespace std;
int main()
{
vector<int> v = boost::assign::list_of(1)(3)(5);
map<int, string> m = boost::assign::map_list_of(1,"one")(2,"two");
std::stringstream ss;
boost::archive::text_oarchive oa(ss);
oa<<v<<m;
vector<int> v_;
map<int,string> m_;
boost::archive::text_iarchive ia(ss);
ia>>v_>>m_;
boost::archive::text_oarchive ib(cout);
ib<<v_<<m_;
return 0;
}
The output looks like this:
22 serialization::archive 9 3 0 1 3 5 0 0 2 0 0 0 1 3 one 2 3 two
What's the meaning of the numbers 9 3 0 before the value 1 3 5 I compose? How about the 0 0 2 0 0 0 ? Does the '3' between '1' and 'one' mean there are 3 characters ?
I'm not sure about some zeros in the map (maybe some version number or tracking levels) but for the rest :
22 (length of the signature)
serialization::archive (signature)
9 (archive version, 10 on boost 1.53)
3 (vector size)
0 (item version)
1 3 5 (vector items)
0 (map class tracking level ?)
0 (map class version ?)
2 (map size)
0 (item class tracking _level ?)
0 (item class version ?)
0 (item version)
1 (key) 3 (value length) one (value)
2 (key) 3 (value length) two (value)
Note that the content and format of the text output is Boost's internal business and may change with future Boost revisions, so your application shouldn't rely on the internal archive contents.
If you put these lines at the end of your code, you will obtain a human readable XML version of the archive.
boost::archive::xml_oarchive ib(cout);
ib<< boost::serialization::make_nvp("v", v_) << boost::serialization::make_nvp("m", m_); // ib<<v_<<m_;
return 0;
you will get this output, which describes it self:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="10">
<v>
<count>3</count>
<item_version>0</item_version>
<item>1</item>
<item>3</item>
<item>5</item>
</v>
<m class_id="1" tracking_level="0" version="0">
<count>2</count>
<item_version>0</item_version>
<item class_id="2" tracking_level="0" version="0">
<first>1</first>
<second>one</second>
</item>
<item>
<first>2</first>
<second>two</second>
</item>
</m>
</boost_serialization>
So #zacinter is correct and the three 0 after the 2 are: 1) item_version (of std::pair, the value type of the map) 2) tracking_level of std::pair and 3) version of std::pair.
22 is the length of the text "serialization::archive".
Every text that is archived has such a text-length-number in front of it I believe.
I'm aware that one should normally not try to parse the text_oarchive format of boost::serialization because the format is subject to change and should only ever be consumed by boost itself. In my case though, the software producing this output is a proprietary binary blob that uses messages on TCP and UDP encoded with boost::serialization. My (FOSS) client needs to be able to talk to it without having boost available. So I needed to figure out how all of this works and this SO question was the most useful I've found so far on this platform. So for any other poor souls in a similar situation, I wanted to share some more discoveries I made about the text_oarchive format.
I'll talk about an example as it would typically be produced by the proprietary application I need to inferface with. So here is some code with dummy classes A1, A2, A3, A4 and A5. A1 is the base class and all of the following classes are sub-classes inheriting from the one before.
#include "boost/serialization/export.hpp"
#include "boost/serialization/extended_type_info.hpp"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <sstream>
class A1 {
public:
A1(void) {}
virtual ~A1(void) {}
private:
unsigned int mem1 = 101;
friend class boost::serialization::access;
template <class Archive> void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(mem1);
}
};
BOOST_CLASS_EXPORT_KEY(A1);
class A2 : public A1 {
public:
A2(void) {}
private:
unsigned int mem2 = 102;
friend class boost::serialization::access;
template <class Archive> void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(A1);
ar & BOOST_SERIALIZATION_NVP(mem2);
}
};
BOOST_CLASS_EXPORT_KEY(A2);
class A3 : public A2 {
public:
A3(void) {}
private:
virtual void foo() = 0;
unsigned int mem3 = 103;
friend class boost::serialization::access;
template <class Archive> void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(A2);
ar & BOOST_SERIALIZATION_NVP(mem3);
}
};
BOOST_CLASS_EXPORT_KEY(A3);
class A4 : public A3 {
public:
A4(void) {}
private:
void foo() {}
unsigned int mem4 = 104;
friend class boost::serialization::access;
template <class Archive> void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(A3);
ar & BOOST_SERIALIZATION_NVP(mem4);
}
};
BOOST_CLASS_EXPORT_KEY(A4);
class A5 : public A4 {
public:
A5(void) {}
private:
unsigned int mem5 = 105;
friend class boost::serialization::access;
template <class Archive> void serialize(Archive & ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(A4);
ar & BOOST_SERIALIZATION_NVP(mem5);
}
};
BOOST_CLASS_EXPORT_KEY(A5);
BOOST_CLASS_EXPORT_IMPLEMENT(A1);
BOOST_CLASS_EXPORT_IMPLEMENT(A2);
BOOST_CLASS_EXPORT_IMPLEMENT(A3);
BOOST_CLASS_EXPORT_IMPLEMENT(A4);
BOOST_CLASS_EXPORT_IMPLEMENT(A5);
BOOST_CLASS_VERSION(A1, 11)
BOOST_CLASS_VERSION(A2, 12)
BOOST_CLASS_VERSION(A3, 13)
BOOST_CLASS_VERSION(A4, 14)
BOOST_CLASS_VERSION(A5, 15)
void func(const A1 * const packet) {
std::string outpacket;
std::ostringstream stream;
boost::archive::xml_oarchive oa_xml(stream);
oa_xml << BOOST_SERIALIZATION_NVP(packet);
boost::archive::text_oarchive oa_text(stream);
oa_text << BOOST_SERIALIZATION_NVP(packet);
outpacket = stream.str();
std::cout << outpacket << std::endl;
}
int main() {
auto data = new A5();
func(data);
}
At the end, the main() function creates an A5 object and passes it to the function called func which is able to handle arguments of type A1 of which A5 is a sub-class. This is important as the boost::serialization output would be different if func took A5 as input directly.
Another important bit is the virtual destructor of A1. This one is required as well or otherwise one will not get the expected output for reasons that I do not yet understand.
To be able to better understand the text format, func encodes the data as XML as well as in the text format. To be able to use the XML output, BOOST_SERIALIZATION_BASE_OBJECT_NVP and BOOST_SERIALIZATION_NVP are applied wherever necessary. This does not seem to inferfere with the text output at all, which does not need these.
Each of the classes A1 to A5 has a member called mem1 to mem5, respectively. These are filled with the values 101 to 105, respectively, to better debug which integer in the text output stands for what.
To understand the output even better, BOOST_CLASS_VERSION is used to give all classes a unique class version. This helps because by default the class version is zero and then it's not clear what all the zeroes in the output stand for.
Without further ado, this is the output of compiling and running above code:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="18">
<packet class_id="1" class_name="A5" tracking_level="1" version="15" object_id="_0">
<A4 class_id="2" tracking_level="1" version="14" object_id="_1">
<A3 class_id="3" tracking_level="0" version="13">
<A2 class_id="4" tracking_level="1" version="12" object_id="_2">
<A1 class_id="0" tracking_level="1" version="11" object_id="_3">
<mem1>101</mem1>
</A1>
<mem2>102</mem2>
</A2>
<mem3>103</mem3>
</A3>
<mem4>104</mem4>
</A4>
<mem5>105</mem5>
</packet>
22 serialization::archive 18 1 2 A5 1 15
0 1 14
1 0 13 1 12
2 1 11
3 101 102 103 104 105
Some general observations:
in general, the values in the XML output seem to be in the same order as in the text output when reading the xml sequentially from top to bottom and left to right
consequently, the member variables of each of the sub-classes are bundled together all at the end of the output
class A3, which is the only class with a virtual function, is the only one with tracking level 0 (all the others are tracking level 1) and without an object id
I do not understand where the newlines in front of the object-ids come from in the text output but boost seems to be able to parse a message with newlines replaced by spaces just the same, so they do not seem to have any semantic meaning
Lets look at the values one-by-one by having each of them in their own line:
22 -- length of the string "serialization::archive"
serialization::archive -- BOOST_ARCHIVE_SIGNATURE defined in src/basic_archive.cpp
18 -- BOOST_ARCHIVE_VERSION as defined in src/basic_archive.cpp
1 -- class-id of A5
2 -- length of class name "A5"
A5 -- class name of A5
1 -- tracking level of A5
15 -- class version of A5
0 -- object-id of A5
1 -- tracking level of A4
14 -- class version of A4
1 -- object-id of A4
0 -- tracking level of A3 (the only "0" tracking level -- A3 is abstract class)
13 -- class version of A3 (notice the absence of object-id for abstract class A3 as well)
1 -- tracking level of A2
12 -- class version of A2
2 -- object-id of A2
1 -- tracking level of A1
11 -- class version of A1
3 -- object-id of A1
101 -- member of A1
102 -- member of A2
103 -- member of A3
104 -- member of A4
105 -- member of A5