SylvainLS

More up-to-date ldraw.xml LDD/LDraw conversion file

Recommended Posts

Update 2016-08-11

Added:

  • 15070 / 15070.dat Plate 1x1 with Tooth Perpendicular

Corrected:

  • 2586 / 2586.dat Minifig Shield Ovoid
  • 3846 / 3846.dat Minifig Shield Triangular
  • 3876 / 3876.dat Minifig Shield Round
  • 3957 / 3957b.dat Antenna 4H with Flat Top
  • 10049 / 10049.dat Minifig Shield Broad with Spiked Bottom and Cutout Corner
  • 64647 / 64647.dat Minifig Plume/Flame Triple
  • 75902 / 75902.dat Minifig Shield Round Bowed
  • 87692 / 87692.dat Minifig Plume Triple
  • 87693 / 87693.dat Minifig Plume Small
  • 87694 / 87694.dat Minifig Plume Large
  • 92747 / 92747.dat Minifig Shield Oval
  • 93251 / 93251.dat Minifig Shield Scarab
  • 98367 / 98367.dat Minifig Shield Rectangular Curved with Stud

Shields are aligned on the handle, except for 98367 which is aligned on its stud (0.008cm lower than the handle).

Share this post


Link to post
Share on other sites

Update 2016-08-13

Added:

  • 99562 / 96910.dat Gold Ingot

Corrected:

  • 2530 / 2530.dat Minifig Sword Cutlass
  • 4499 / 4499.dat Minifig Bow with Arrow
  • 6126 / 6126a.dat Minifig Flame with Pegs
  • 10053 / 10053.dat Minifig Small Sword with Curved Blade
  • 92231 / 93231.dat Minifig Long Bow with Arrow
  • 95673 / 95673.dat Minifig Sword Roman Gladius

Share this post


Link to post
Share on other sites

New Howto!

Hi all!

I’ve added a howto (in the first post) for the brave ones who would want to plunge into it.

Feel free to comment on how it all looks like Greek to you :grin:

Edit: Okay… The forum ate a few bits and keeps mangling some of the text at each edit. :look: I hope I corrected and foolproofed it now… :hmpf_bad:

Edited by SylvainLS

Share this post


Link to post
Share on other sites

Hey Sylvain!

I love your how-to! It took me a couple of days to get my head around the quaternions but now I think I understand them a little bit. I even accomplished a full correct translation using your tutorial.

Though there sill ar a couple things I don't get... maybe I'm just a little bit slow with maths.

I was wondering how do you get √2/2 (which is 1) from this: cos(a/2) = sin(a/2) = cos(π/4) = sin(π/4) = √2/2?

And than how you get this: cos(π/2) = 0, sin(π/2) = 1? Because by typing it into my calculator I get something completely different...

And lastly do you build your quaternion off of this form: q = cos(a/2) + sin(a/2).(ax.i + ay.j + az.k)? And if yes what happens with the rest of the factors? (I feel that all this questions have a really simple answer and I'm just being dumb, but as I said I've been trying to get my head around this for a couple days now and I need to finally know!)

But other than that, as I said, I was able to use your examples and calculation to translate a part without any problems and I think that it would be very easy for everybody alse that has this problem with the translation. It's just me that would like to understand how it works. :classic:

With regards,

Theodor.

Share this post


Link to post
Share on other sites

Hey Sylvain!

I love your how-to! It took me a couple of days to get my head around the quaternions but now I think I understand them a little bit. I even accomplished a full correct translation using your tutorial.

Thanks! It’s great if it helped you.

Though there sill ar a couple things I don't get... maybe I'm just a little bit slow with maths.

I was wondering how do you get √2/2 (which is 1) from this: cos(a/2) = sin(a/2) = cos(π/4) = sin(π/4) = √2/2?

Sorry, habits.

√2/2 is 1/√2 but mathematicians don’t like square roots in the denominator, well, at least my teachers didn’t.

I used the Unicode fraction characters (so, basically, I wrote √(1/2) ) but the forum transformed some of them to {amp}frac12; which does not work everywhere. (And it keeps tranforming my “α” (alpha) into “a”!)

And I don’t “get” 1/√2, it’s just the value of sine and cosine for π/4 (45°). A square with sides of 1/√2 has a diagonal of 1 (the radius of the trigonometric circle).

And than how you get this: cos(π/2) = 0, sin(π/2) = 1? Because by typing it into my calculator I get something completely different...

Again, simple trigonometry. cosine and sine of π/2 = 90° are a given.

Maybe your calculator is waiting for angles in degrees, not radians?

And lastly do you build your quaternion off of this form: q = cos(a/2) + sin(a/2).(ax.i + ay.j + az.k)? And if yes what happens with the rest of the factors? (I feel that all this questions have a really simple answer and I'm just being dumb, but as I said I've been trying to get my head around this for a couple days now and I need to finally know!)

The quaternions are only used for the rotations.

A quaternion that represents a rotation is of the form q = cos(angle/2) + sin(angle/2).(ax.i + ay.j + az.k).

It’s doubly unitary: its length is cos²(angle/2) + sin²(angle/2).(ax² + ay² + az²) = 1 and also ax² + ay² + az² = 1 (the length of the unitary vector defining the axis).

When you multiply rotation-quaternions, you get a rotation-quaternion.

If you know the angle angle and the axis (ax, ay, az) of the rotation, you can write q.

If you have a quaternion that you know represents a rotation (as, for example, the result we get from combining several rotation-quarternions), its general form will be q = a + b.i + c.j + d.k.

As it is a rotation quaternion, you know it can be written as q = cos(angle/2) + sin(angle/2).(ax.i + ay.j + az.k). Therefore ax = b / sin(angle/2), ay = c / sin(angle/2), az = d / sin(angle/2) and a = cos(angle/2), so angle = 2.Acos(a).

Hence we get the angle, ax, ay, az to put in ldraw.xml.

But other than that, as I said, I was able to use your examples and calculation to translate a part without any problems and I think that it would be very easy for everybody alse that has this problem with the translation. It's just me that would like to understand how it works. :classic:

So, rotations are a b… :grin:

I hope my explanations are not more confusing :skeptic:

Share this post


Link to post
Share on other sites

Thank you very much for explaining to me every thing I asked about.

As I thought all my problems were pretty easy to solve. (I can't believe it didn't came to my mind that π/2 is 90° and that my calculator needed the degrees!)

Now, I took another look at your examples and (I really don't mean to make you flip out or so, but...) this is as far as I got: p.q = (0 + i) . (√2/2 + √2/2 j) = √2/2 i + √2/2 i.j = 0 + √2/2 ( i + k ). I really can't figure out how √2/2 i + √2/2 i.j becomes 0 + √2/2 ( i + k )! ( the (i + k )...)

I'm sure it's just some 5th grade math that I'm missing but I'm sick of getting quaternions nightmares because I try to get my head around this before I go to sleep. So I'll just ask you. :laugh:

I hope these won't be just my problems with this calculation and other people that would like to get into part translation in the future will find your explanation for their (and my) problems here too.

Share this post


Link to post
Share on other sites

Now, I took another look at your examples and (I really don't mean to make you flip out or so, but...) this is as far as I got: p.q = (0 + i) . (√2/2 + √2/2 j) = √2/2 i + √2/2 i.j = 0 + √2/2 ( i + k ). I really can't figure out how √2/2 i + √2/2 i.j becomes 0 + √2/2 ( i + k )! ( the (i + k )...)

By definition, i.j = k, so √2/2.i + √2/2.i.j = √2/2.( i + i.j ) = √2/2.( i + k )

I'm sure it's just some 5th grade math that I'm missing but I'm sick of getting quaternions nightmares because I try to get my head around this before I go to sleep. So I'll just ask you. :laugh:

The thing is, you’re overthinking it :wink:

You don’t have to understand why or how, you just apply the rules:

The definition: i.i = j.j = k.k = i.j.k = -1

Its corollaries:

i.j = k = -j.i

j.k = i = -k.j

k.i = j = -i.k

You apply them until you get only a scalar and simple i, j, k components.

And that’s not 5th grade maths. Trigonometry is taught to 14-15 year-olds here (what we call “troisième” (third), should be 9th grade), and easily forgotten. And quaternions are college/university level stuff. (Well, my high school teacher introduced us to quaternions during the last year but he was a madman :grin:)

I hope these won't be just my problems with this calculation and other people that would like to get into part translation in the future will find your explanation for their (and my) problems here too.

Yes, I didn’t want to go too far into the maths but I may have shortened a bit too much :grin:

Share this post


Link to post
Share on other sites

Of corse i.j=k !!! I just had to look back a couple of lines!

Thank you very much! Now I finally understand it all.

Your how-to is perfect the way it is right now, I'm just too curious... :classic:

Share this post


Link to post
Share on other sites

Update 2016-08-22

Added:

  • 95646 / 95646.dat Electric Mindstorms EV3
  • 95650 / 95650.dat Electric Mindstorms EV3 Color Sensor
  • 95652 / 95652.dat Electric Mindstorms EV3 Ultrasonic Sensor
  • 95654 / 95654.dat Electric Mindstorms EV3 Infrared Sensor
  • 99380 / 99380.dat Electric Mindstorms EV3 Gyroscopic Sensor
  • 95648 / 95648.dat Electric Mindstorms EV3 Touch Sensor
  • 74665 / 99385c01.dat Electric Mindstorms EV3 Touch Sensor Body Assembly
  • 99386 / 99386.dat Electric Mindstorms EV3 Touch Sensor Button
  • 95658 / 95658.dat Electric Mindstorms EV3 Large Motor
  • 54725 / 99550c01.dat Electric Mindstorms EV3 Large Motor Case
  • 74042 / 99617c01.dat Electric Mindstorms EV3 Large Motor Drive Hub Double
  • 99455 / 99455.dat Electric Mindstorms EV3 Medium Motor
  • 59158 / 99535c01.dat Electric Mindstorms EV3 Medium Motor Body Assembly
  • 74043 / 99543.dat Electric Mindstorms EV3 Medium Motor

Thanks Jarema!

95648, 95658, and 99455 are assemblies in LDD. They are disassembled when exporting.

Importing them (as assemblies) doesn’t work. The components are correctly imported though.

I thought I would have to fumble with the Assembly XML element, but, actually, the ones that exist (torso, steering wheel, control stick/lever) don’t work: the separated components are “assembled” (replaced by the assembly) in LDD when they are correctly placed in the LDR file (as when exported :wink:), but the LDraw ready-made assemblies aren’t imported.

Edited by SylvainLS

Share this post


Link to post
Share on other sites

Update 2016-08-23

Corrected:

  • 60410 / 933c01.dat Electric Mindstorms NXT RJ12 Style Blug w/ Cable End (Complete)

Added*:

  • 55804 / 55804.dat Electric Mindstorm NXT Cable 20cm
  • 55805 / 55805.dat Electric Mindstorm NXT Cable 35cm
  • 55806 / 55806.dat Electric Mindstorm NXT Cable 50cm
  • 11145 / 11145.dat Electric Mindstorm EV3 Cable 25cm
  • 11146 / 11146.dat Electric Mindstorm EV3 Cable 35cm
  • 11147 / 11147.dat Electric Mindstorm EV3 Cable 50cm

* The cables are loose assemblies in LDD (the plugs can be moved at will), they are exported as two plugs. As they are loose, we can’t state the relative positions of their components in an Assembly XML element. So they can’t be imported. (Besides they’re 6 assemblies with the exact same two components.)

Share this post


Link to post
Share on other sites

What a delight to see this topic! I have been updating the ldraw.xml file myself whenever I needed to. I'll see which part mappings I have that I can contribute...

To help me do the part mappings, I wrote a simple program to do the equivalent rotation thing for me, so that I don't have to mess around with matrices or quaternions. I rewrote it in Python with only the most basic of functions. This way, you can plug it into an online compiler like https://repl.it/languages/python and simply hit the run button without any installing or programming knowledge required. I wrote instructions on how to use it, so once you hit run everything should go by itself. I hope this can help people who are less savvy with these kinds of things to contribute!

So here's what you do:

1. Go the the website I just mentionned

2. Paste the code from the spoiler below into the left window

from math import cos, acos, sin, pi

def multiply_matrices(matrix_1, matrix_2):
   """Calculates the matrix-matrix product between matrix_1 and matrix_2. If
   their dimensions aren't correct, None is returned"""
   if len(matrix_1[0]) != len(matrix_2):
       matrix_out = None

   else:
       matrix_out = [[0 for i in range(len(matrix_2[0]))]
                        for j in range(len(matrix_1))]

       for row in range(len(matrix_out)):
           for col in range(len(matrix_out[0])):
               for i in range(len(matrix_1[0])):
                   matrix_out[row][col] += matrix_1[row][i]*matrix_2[i][col]

   return matrix_out

def trace(matrix):
   """Calculates the trace (sum of the diagonal elements) of a square matrix"""
   trace = 0

   for i in range(len(matrix)):
       trace += matrix[i][i]

   return trace

def rotation_matrix(axis, angle):
   """Returns a 3x3 rotation matrix that corresponds to the given rotation
   about the axis ("x", "y" or "z") with the given angle in radians"""
   axis_indeces = {"x":0, "y":1, "z":2}
   ax_ind = axis_indeces[axis]

   rot_mat = [[0,0,0],[0,0,0],[0,0,0]]
   rot_mat[ax_ind][ax_ind] = 1

   for i in range(1,3):
       col = ax_ind + i
       if col > 2:
           col -= 3

       rot_mat[col][col] = cos(angle)

       sin_row = range(3)
       sin_row.remove(col)
       sin_row.remove(ax_ind)
       sin_row = sin_row.pop()

       if i == 1:
           rot_mat[sin_row][col] = sin(angle)
       else:
           rot_mat[sin_row][col] = -sin(angle)

   return rot_mat

def equivalent_rotation(axes, angles):
   """Returns the equivalent axis (in x,y,z coordinates) and the equivalent
   angle (in radians) corresponding to the sequence of rotations given by
   the arguments. "axes" is a list of all the axes around which is being
   rotated (e.g. ["x", "y", "z"]) in order of occurence, and "angles" is a list
   of equal length with the angles (in degrees) of the corresponding
   rotations"""
   rot_mat = [[1,0,0],[0,1,0],[0,0,1]]

   for i in range(len(axes)):
       rot_mat = multiply_matrices(rot_mat, rotation_matrix(axes[i], angles[i]*pi/180))

   eq_angle = acos((trace(rot_mat)-1)/2.0)
   eq_axis = [rot_mat[2][1] - rot_mat[1][2],
   rot_mat[0][2] - rot_mat[2][0],
   rot_mat[1][0] - rot_mat[0][1]]
   eq_axis = [eq_axis_coord/(2.0*sin(eq_angle)) for eq_axis_coord in eq_axis]

   return eq_axis, eq_angle

def generate_rotation_code(eq_axis, eq_angle):
   """Returns a string containing the code for the xml file for the given
   rotation that is entered by means of eq_axis, which is a list containing
   the three cartesian coordinates of the equivalent axis of rotation, and
   eq_angle, a number that gives the angle of the equivalent rotation. If the
   eq_axis can be scaled to a vector with only integers as coordinates, the
   function returns the integer version"""
   max_el = float(max(eq_axis))
   if max_el != 0:
       new_eq_axis = [coord/max_el for coord in eq_axis]

       all_ints = True
       for coord in new_eq_axis:
           if int(coord) != coord:
               all_ints = False

       if all_ints == True:
           eq_axis = new_eq_axis

   if all_ints == True:
       eq_axis = [int(coord) for coord in eq_axis]
   else:
       eq_axis = [round(coord, 6) for coord in eq_axis]
   eq_angle = round(eq_angle, 6)

   eq_rot_data = eq_axis
   eq_rot_data.append(eq_angle)

   return 'ax="{0}" ay="{1}" az="{2}" angle="{3}"'.format(*eq_rot_data)




# Code for the text based interface
print """Below you can enter the consecutive rotations you want to reduce to an \
equivalent rotation about a single axis. You will be prompted to enter the \
axis each time: this requires you to input either x, y or z (without \
quotation marks and the like). You will also be prompted to enter the \
angle of the rotation about each axis. Give this angle in degrees. You \
can enter up to 3 consecutive rotations. You can also input less. Hitting \
enter without filling anything in a certain field will make the program \
go on with the information you provided"""
print

nb_rots = 0
axes = []
angles = []
cont = True

while nb_rots < 3 and cont == True:
   axes.append(raw_input("Please enter the axis of rotation: "))
   if axes[-1] == "":
       cont = False
       del axes[-1]
   else:
       angles.append(float(raw_input("Please enter the angle of the rotation: ")))
       if angles[-1] == "":
           cont = False
           del axes[-1]
           del angles[-1]
   nb_rots += 1

[eq_axis, eq_angle] = equivalent_rotation(axes, angles)

print
print generate_rotation_code(eq_axis, eq_angle)

3. Hit the "run" button

4. Follow the instructions the program gives you

5. Paste the code you obtain from the program right into the ldraw.xml file on the place where it belongs.

Don't be afraid to ask any questions though!

O, and thanks again for putting together this document, everyone!

Share this post


Link to post
Share on other sites

[…]I'll see which part mappings I have that I can contribute...

If the task seems daunting, feel free to post me your ldraw.xml file, I can easily find the diffs. :grin:

To help me do the part mappings, I wrote a simple program to do the equivalent rotation thing for me, so that I don't have to mess around with matrices or quaternions.[…]

One little problem with your code: the forum ate all the leading spaces. That’s not good with Python :wink::devil:

Another little problem is multiplying matrices can lead to errors or at least approximations (floating point computations).

And another big problem: where’s the fun in automating that? :grin:

O, and thanks again for putting together this document, everyone!

You’re welcome!

Edited by SylvainLS

Share this post


Link to post
Share on other sites

Update 2016-08-31

Corrected:

  • 6255 / 6255.dat Plant 1 x 1 x 0.667 Round with 3 Large Leaves
  • 63965 / 63965.dat Bar 6L with Thick Stop

Added:

  • 15530 / 15530.dat Minifig Hat Police
  • 20430 / 20430.dat Cylinder 2 x 4 x 4
  • 24445 / 24445.dat Tile 1 x 2 with Minifig Head Post
  • 24593 / 24593.dat Cylinder Half 2 x 4 x 2 with 1 x 2 cutout
  • 24607 / 24607.dat Windscreen 2 x 10 x 3

Note: the new ones are new unofficial LDraw parts.

Share this post


Link to post
Share on other sites

Another transformation for inclusion in the next release.

<!-- Electric Mindstorms EV3 IR-Beacon / Remote Handset -->

<Transformation ldraw="72156.dat" tx="2.0" ty="0.08" tz="1.6" ax="0" ay="1" az="0" angle="1.570796"/>

Regards,

David

Share this post


Link to post
Share on other sites

Please note

I’m in the process of cleaning, verifying, and correcting the whole mess file.

Importing assemblies is the last ditch (see http://www.eurobrick...howtopic=140239 ).

Don’t fear, I won’t wait for that to publish the result, I’m just waiting for a few new files in the LDraw Unofficial Library to write the big announcement.

Share this post


Link to post
Share on other sites

Giant Overhaul!

  • The file has been cleaned.
  • The existing transformations have all been tested and corrected.
  • A lot of new transformatins have been added.

As far as I can tell, all the possible conversions are included. That’s about 2600 parts (depending on how you count them), 64 assemblies.

1075 LDD parts remain unconverted: they are missing in LDraw (at least, I couldn’t find them).

You’ll need the latest LDraw Unofficial Library, especially for the assemblies (new parts have just been submitted for them).

Here are a few files if you want to help or comment:

  • The working assemblies.
  • The assemblies for which parts are missing (dark grey are missing, white is the rest of the parts of the assembly we already have the LDraw parts for).
  • The parts for which a variant is used instead (red and orange: the missing variant, dark grey: the substitute, brown: substitutes that are also wrong).
  • And, finally, the list of LDD parts that I know no LDraw equivalent for: CSV (columns: LDD ID, LDD name, guessed BrickLink ID, BrickLink name).

As I added in the first post, LDraw assumes technic holes/pins/axles in bricks are at the same height as studs. They should be 0.12 mm higher. LDD places them 0.2 mm higher.

The old version of ldraw.xml used to add those 0.2 mm to a great number of parts (pins and axles mainly, but also beams and technic panels), not all, and arbitrarily.

The picture below shows a 2L Axle (red) and Pin and Axle (blue) in a Technic Brick with Axle Hole. The axles parts have been rotated a quarter turn each level up (that is the higher ones are 270°, the lower ones are 0°). The left part uses the old conversion that added 0.2 mm, the right part uses the new conversion.

before_now.png

Disclaimer: I’m prone to tpyos and other herrors, also, sometimes, after looking at a lot of parts and lines of XML, I might not have seen obvious errors. So, if you find an error, be indulgent and post it here. Thank you.

Edited by SylvainLS

Share this post


Link to post
Share on other sites

Stunning! That must have been a labour of love. Well done.

If my eyes don't deceive me, I think there is a spurious "w" in the file, immediately below the definition for 76276.dat.

Regards,

David

Share this post


Link to post
Share on other sites

Stunning! That must have been a labour of love. Well done.

I was fed up with little errors, the untidyness (that I uselessly kept to make comparisons easy), and had time on my hands.

If my eyes don't deceive me, I think there is a spurious "w" in the file, immediately below the definition for 76276.dat.

Thanks. Corrected.

Share this post


Link to post
Share on other sites

Update 2016-09-23

Added:

  • 21271 / 21271.dat  Minifig Hoverboard
  • 24085 / 24085.dat  Minifig Mop
Edited by SylvainLS
Mop just added

Share this post


Link to post
Share on other sites

Update 2016-09-30

Added:

  • 21229 / 21229.dat  Fence Spindled 4 x 4 x 2 Quarter Round with 3 Studs
  • 96487 / 96487.dat  Plant Flower with 5 Serrated Petals with Pin

Added some LDraw colors for import (rubbers and the like; imported as plain).

Edited by SylvainLS

Share this post


Link to post
Share on other sites

Update 2016-10-02

Added new unofficial parts:

  • 47404 / 47404.dat  Boat Base 10 x 12
  • 64645 / 64645.dat  Boat Base 10 x 16 Stern / Bow
  • 64651 / 64651.dat  Boat Base 13 x 16 Stern / Bow

(47404.dat has little problems though.)

 

Share this post


Link to post
Share on other sites

So...

I really needed this piece: Part#: 15362 Name: CLAW 7 MODULES WITH CROSS AXEL.

It wasn't inside LDraw's parts folder (not even inside the Unofficial parts Folder), but I found it here http://www.digital-bricks.de/en/index.php?site=1392 .

I inserted a new line for it in the XML file (witch wasn't as straight forward as I expected, you should add a "template" for adding new parts in your original post) and it works/looks perfect if you would ask me.

The question is: how could we make parts like this one accessible for everyone that needs them trough the XML file or through this forum?

 

Teodor

PS: On the website listed above are a lot of unavailable parts free for download.

Share this post


Link to post
Share on other sites

(Durn! Can’t cut the quotes anymore.)

About a template: Could you develop on what more is needed than a simple copy-paste of a transformation line (either one in the file or one of the examples in the howto)?

About digital-bricks.de: I didn’t add/use them for several reasons, mainly because I simply forgot about them :grin:, and they are all-in-one files not using LDraw primitives, so they don’t fit well.

As for making them available, what I can do is add them in ldraw.xml. Leave me a few days…. :classic:

Share this post


Link to post
Share on other sites

It wasn't hard to find the right composition of the transformation line, but I first had to copy one of the existing ones or I could have copied one from the how to, doesn't matter, and than paste ti in. Than I had to adjust all the fields and figure out what goes where.

If you could just add this here into your how to, it would be for everyone, I suppose, much easier to write a new transformation:
 

  <!-- Part name and partnumber.dat -->
  <!-- OFICIAL LEGO PART NAME and number -->
  <Transformation ldraw="LDraw part name.dat" tx="0" ty="0" tz="0" ax="1" ay="0" az="0" angle="0" />

 

Please correct it if that isn't right!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.