summaryrefslogtreecommitdiff
path: root/graphics/pgf/contrib/liftarm/liftarm.sty
blob: 63d6b51f44b7ed6e4388b93063d3f7a13240098c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
%% liftarm.sty
%% Copyright 2022-2024 Matthias Floré
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
%   http://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is Matthias Floré.
%
% This work consists of the files liftarm.pdf, liftarm.sty,
% liftarm.tex and README.md.
\NeedsTeXFormat{LaTeX2e}
\RequirePackage[dvipsnames]{xcolor}
\RequirePackage{tikz}
\usetikzlibrary{calc}
\ProvidesExplPackage{liftarm}{2024/05/20}{3.0}{Geometric constructions with liftarms using TikZ and LaTeX3}

%%> \subsection{Variables}

\newcounter { g__liftarm_animate_frame_number_counter }
\newcounter { g__liftarm_animate_number_of_animation_counter }
\newcounter { g__liftarm_animate_number_of_steps_counter }
\newcounter { g__liftarm_animate_step_number_counter }

\bool_new:N \l__liftarm_animate_bool
\bool_new:N \l__liftarm_brick_bool
\bool_new:N \l__liftarm_contour_bool
\bool_new:N \l__liftarm_LU_bool

\clist_new:N \l__liftarm_trace_clist

\fp_new:N \l__liftarm_angle_fp
\fp_const:Nn \c__liftarm_axle_hole_angle_fp { 21.76702028497987 }%asind ( 1.78 / ( 16 * 0.3 ) )
\fp_new:N \l__liftarm_connect_det_fp
\fp_new:N \l__liftarm_connect_norm_fp
\fp_new:N \l__liftarm_connect_start_constant_x_fp
\fp_new:N \l__liftarm_connect_start_constant_y_fp
\fp_new:N \l__liftarm_connect_stop_value_fp
\fp_new:c { l__liftarm_connect_two_1_option_0_angle_fp }
\fp_new:c { l__liftarm_connect_two_1_option_1_angle_fp }
\fp_new:c { l__liftarm_connect_two_2_option_0_angle_fp }
\fp_new:c { l__liftarm_connect_two_2_option_1_angle_fp }
\fp_new:N \l__liftarm_connect_two_angle_fp
\fp_new:N \l__liftarm_connect_two_A_angle_fp
\fp_new:N \l__liftarm_connect_two_A_length_fp
\fp_new:N \l__liftarm_connect_two_A_x_fp
\fp_new:N \l__liftarm_connect_two_A_y_fp
\fp_new:N \l__liftarm_connect_two_B_angle_fp
\fp_new:N \l__liftarm_connect_two_B_length_fp
\fp_new:N \l__liftarm_connect_two_B_x_fp
\fp_new:N \l__liftarm_connect_two_B_y_fp
\fp_new:N \l__liftarm_connect_two_length_fp
\fp_new:N \g__liftarm_coord_x_fp
\fp_new:N \g__liftarm_coord_y_fp
\fp_new:N \l__liftarm_half_thickness_fp
\fp_new:N \l__liftarm_hole_radius_fp
\fp_new:N \l__liftarm_length_fp
\fp_new:N \l__liftarm_LU_maxA_fp
\fp_new:N \l__liftarm_LU_tmp_fp
\fp_new:N \l__liftarm_mark_radius_fp
\fp_new:N \l__liftarm_origin_fp
\fp_new:N \l__liftarm_origin_connect_initial_fp
\fp_new:N \l__liftarm_scalefactor_fp
\fp_new:N \l__liftarm_screw_angle_fp
\fp_new:N \l__liftarm_screw_radius_fp

\int_new:N \l__liftarm_connect_count_int
\int_new:N \l__liftarm_connect_equation_int
\int_new:N \l__liftarm_LU_count_int
\int_new:N \l__liftarm_LU_imax_int
\int_new:N \l__liftarm_LU_j_int
\int_new:N \l__liftarm_LU_N_int

\iow_new:N \g__liftarm_animate_write_timeline_iow

\seq_new:N \l__liftarm_connect_coordinate_seq
\seq_new:N \l__liftarm_connect_start_arg_seq
\seq_new:N \l__liftarm_connect_start_coeff_seq
\seq_new:N \l__liftarm_coordinate_seq
\seq_new:N \l__liftarm_trace_item_seq

\str_new:N \l__liftarm_connect_stop_type_str
\str_new:N \l__liftarm_type_str

\tl_new:N \g__liftarm_animate_frames_tl
\tl_new:N \g__liftarm_animate_frames_trace_tl
\tl_new:N \l__liftarm_animate_value_tl
\tl_new:N \l__liftarm_color_tl
\tl_new:N \g__liftarm_construct_tl
\tl_const:Nn \c__liftarm_cos_sin_diff_x_tl { - sin }
\tl_const:Nn \c__liftarm_cos_sin_diff_y_tl { cos }
\tl_const:Nn \c__liftarm_cos_sin_x_tl { cos }
\tl_const:Nn \c__liftarm_cos_sin_y_tl { sin }
\tl_new:N \l__liftarm_holes_tl
\tl_new:N \l__liftarm_shape_tl
\tl_new:N \l__liftarm_tmp_tl

%%> \subsection{Pgfkeys}

\pgfkeys
  {
    / liftarm /. is~family ,
    / liftarm ,
    axle~holes /. initial = {} ,
    brick /. code = { \bool_set:Nn \l__liftarm_brick_bool { \cs:w c_#1_bool\cs_end: } } ,
    brick /. default = true ,
    brick = false ,
    color /. code~2~args =
      {
        \tl_clear_new:c { l__liftarm_color_\int_eval:n {#1}_tl }
        \tl_set:cn { l__liftarm_color_\int_eval:n {#1}_tl } {#2}
      } ,
    color = { 0 } { Gray } ,
    color = { 1 } { darkgray } ,
    color = { 2 } { Yellow } ,
    color = { 3 } { Orange } ,
    color = { 4 } { Red } ,
    color = { 5 } { Green } ,
    color = { 6 } { Blue } ,
    color = { 7 } { Brown } ,
    color~modulo /. initial = 8 ,
    connect~stop /. is~choice ,
    connect~stop / 1-norm /. code =
      {
        \str_set:Nn \l__liftarm_connect_stop_type_str { 1-norm }
        \fp_set:Nn \l__liftarm_connect_stop_value_fp {#1}
      } ,
    connect~stop / 1-norm /. default = 0.001 ,
    connect~stop / 2-norm /. code =
      {
        \str_set:Nn \l__liftarm_connect_stop_type_str { 2-norm }
        \fp_set:Nn \l__liftarm_connect_stop_value_fp {#1}
      } ,
    connect~stop / 2-norm /. default = 0.001 ,
    connect~stop / iterations /. code =
      {
        \str_set:Nn \l__liftarm_connect_stop_type_str { iterations }
        \fp_set:Nn \l__liftarm_connect_stop_value_fp {#1}
      } ,
    connect~stop / iterations /. default = 10 ,
    connect~stop = 1-norm ,
    contour /. code = { \bool_set:Nn \l__liftarm_contour_bool { \cs:w c_#1_bool\cs_end: } } ,
    contour /. default = true ,
    contour = false ,
    contour~style /. style = { contour_style /. style = {#1} } ,
    contour_style /. style = {} ,
    coordinate /. initial = {} ,
    hole~radius /. initial = 0.3 ,
    liftarm~style /. style = { liftarm_style /. style = {#1} } ,
    liftarm_style /. style = {} ,
    liftarm~thickness /. initial = 0.92 ,
    mark~holes /. initial = {} ,
    mark~radius /. code =
      {
        \pgfmathparse {#1}
        \fp_set:Nn \l__liftarm_mark_radius_fp { \pgfmathresult }
      } ,
    mark~radius = 1 ,
    mark~style /. style = { mark_style /. style = {#1} } ,
    mark_style /. style = {} ,
    origin /. code =
      {
        \pgfmathparse {#1}
        \fp_set:Nn \l__liftarm_origin_fp { \pgfmathresult }
      } ,
    origin = 0 ,
    scalefactor /. code =
      {
        \pgfmathparse {#1}
        \fp_set:Nn \l__liftarm_scalefactor_fp { \pgfmathresult }
      } ,
    scalefactor = 0.5 ,
    screw~angle /. code =
      {
        \pgfmathparse {#1}
        \fp_set:Nn \l__liftarm_screw_angle_fp { \pgfmathresult }
      } ,
    screw~angle = 10 ,
    screw~holes /. initial = {} ,
    screw~radius /. code =
      {
        \pgfmathparse {#1}
        \fp_set:Nn \l__liftarm_screw_radius_fp { \pgfmathresult }
      } ,
    screw~radius = 0.8 ,
    screw~style /. style = { screw_style /. style = {#1} } ,
    screw_style /. style = {} ,
    trace /. code = { \clist_set:Nn \l__liftarm_trace_clist {#1} } ,
    type /. is~choice ,
    type / liftarm /. code = { \str_set:Nn \l__liftarm_type_str { liftarm } } ,
    type / liftarm /. value~forbidden ,
    type / line~segment /. code = { \str_set:Nn \l__liftarm_type_str { line~segment } } ,
    type / line~segment /. value~forbidden ,
    type = liftarm ,
  }

\pgfkeys
  {
    / liftarm / connect_algorithm /. is~family ,
    / liftarm / connect_algorithm /. unknown /. code = {} ,
    / liftarm / connect_algorithm ,
    coordinate /. initial = {} ,
    origin /. code =
      {
        \pgfmathparse {#1}
        \fp_set:Nn \l__liftarm_origin_fp { \pgfmathresult }
      } ,
  }

%%> \subsection{Functions}

\cs_generate_variant:Nn \clist_if_in:nnTF { enTF }
\cs_generate_variant:Nn \clist_map_inline:nn { en }
\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn }
\cs_generate_variant:Nn \tl_build_begin:N { c }
\cs_generate_variant:Nn \tl_build_gbegin:N { c }
\cs_generate_variant:Nn \tl_build_end:N { c }
\cs_generate_variant:Nn \tl_build_gend:N { c }
\cs_generate_variant:Nn \tl_build_put_right:Nn { ce , cn }
\cs_generate_variant:Nn \tl_build_gput_right:Nn { ce , cn }

\cs_new_protected:Npn \__liftarm_connect:nnnn #1#2#3#4
  {
    \int_incr:N \l__liftarm_connect_count_int
    \fp_zero_new:c { l__liftarm_connect_angle_\int_use:N \l__liftarm_connect_count_int _fp }
    \pgfmathparse {#4}
    \fp_set:cn { l__liftarm_connect_angle_\int_use:N \l__liftarm_connect_count_int _fp } { \pgfmathresult * deg }
    \fp_set_eq:NN \l__liftarm_origin_fp \l__liftarm_origin_connect_initial_fp
    \pgfkeys
      {
        / liftarm / connect_algorithm ,
        coordinate = \pgfkeysvalueof { / liftarm / coordinate } ,
        #1
      }
    \seq_if_in:NnTF \l__liftarm_connect_coordinate_seq {#2}
      {
        \fp_set_eq:Nc \l__liftarm_connect_start_constant_x_fp { l__liftarm_connect_constant_x_coord_#2_fp }
        \fp_set_eq:Nc \l__liftarm_connect_start_constant_y_fp { l__liftarm_connect_constant_y_coord_#2_fp }
        \seq_set_eq:Nc \l__liftarm_connect_start_arg_seq { l__liftarm_connect_arg_coord_#2_seq }
        \seq_set_eq:Nc \l__liftarm_connect_start_coeff_seq { l__liftarm_connect_coeff_coord_#2_seq }
      }
      {
        \__liftarm_def_coord:n {#2}
        \fp_set_eq:NN \l__liftarm_connect_start_constant_x_fp \g__liftarm_coord_x_fp
        \fp_set_eq:NN \l__liftarm_connect_start_constant_y_fp \g__liftarm_coord_y_fp
        \seq_clear:N \l__liftarm_connect_start_arg_seq
        \seq_clear:N \l__liftarm_connect_start_coeff_seq
      }
    \clist_map_inline:en { \pgfkeysvalueof { / liftarm / connect_algorithm / coordinate } }
      {
        \seq_set_split:Nnn \l__liftarm_coordinate_seq { / } {##1}
        \pgfmathparse { \seq_item:Nn \l__liftarm_coordinate_seq { 1 } }
        \fp_set:Nn \l__liftarm_length_fp { \pgfmathresult - \l__liftarm_origin_fp }
        \seq_if_in:NeTF \l__liftarm_connect_coordinate_seq { \seq_item:Nn \l__liftarm_coordinate_seq { 2 } }
          {
            \clist_map_inline:nn { x , y }
              {
                \int_incr:N \l__liftarm_connect_equation_int
                \tl_clear_new:c { l__liftarm_connect_F_\int_use:N \l__liftarm_connect_equation_int _tl }
                \tl_build_begin:c { l__liftarm_connect_F_\int_use:N \l__liftarm_connect_equation_int _tl }
                \int_step_inline:nn { \l__liftarm_LU_N_int }
                  {
                    \tl_clear_new:c { l__liftarm_connect_Jacobian_\int_use:N \l__liftarm_connect_equation_int _########1_tl }
                    \tl_build_begin:c { l__liftarm_connect_Jacobian_\int_use:N \l__liftarm_connect_equation_int _########1_tl }
                    \tl_build_put_right:cn { l__liftarm_connect_Jacobian_\int_use:N \l__liftarm_connect_equation_int _########1_tl }
                      { 0 }
                    \fp_zero_new:c { l__liftarm_LU_A_\int_use:N \l__liftarm_connect_equation_int _########1_fp }
                  }
                \tl_build_put_right:ce { l__liftarm_connect_F_\int_use:N \l__liftarm_connect_equation_int _tl }
                  {
                    \fp_use:c { l__liftarm_connect_constant_####1_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_fp }
                    - \fp_use:c { l__liftarm_connect_start_constant_####1_fp }
                    - \fp_use:N \l__liftarm_length_fp * \cs:w c__liftarm_cos_sin_####1_tl\cs_end:
                    ( \exp_not:N \cs:w l__liftarm_connect_angle_\int_use:N \l__liftarm_connect_count_int _fp \exp_not:N \cs_end: )
                  }
                \tl_build_put_right:ce
                  {
                    l__liftarm_connect_Jacobian_\int_use:N \l__liftarm_connect_equation_int _
                    \int_use:N \l__liftarm_connect_count_int _tl
                  }
                  {
                    - \fp_use:N \l__liftarm_length_fp * \cs:w c__liftarm_cos_sin_diff_####1_tl\cs_end:
                    ( \exp_not:N \cs:w l__liftarm_connect_angle_\int_use:N \l__liftarm_connect_count_int _fp \exp_not:N \cs_end: )
                  }
                \seq_map_indexed_inline:cn { l__liftarm_connect_arg_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_seq }
                  {
                    \tl_build_put_right:ce { l__liftarm_connect_F_\int_use:N \l__liftarm_connect_equation_int _tl }
                      {
                        + \seq_item:cn { l__liftarm_connect_coeff_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_seq } {########1}
                        * \cs:w c__liftarm_cos_sin_####1_tl\cs_end:
                        ( \exp_not:N \cs:w l__liftarm_connect_angle_########2_fp \exp_not:N \cs_end: )
                      }
                    \tl_build_put_right:ce { l__liftarm_connect_Jacobian_\int_use:N \l__liftarm_connect_equation_int _########2_tl }
                      {
                        + \seq_item:cn { l__liftarm_connect_coeff_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_seq } {########1}
                        * \cs:w c__liftarm_cos_sin_diff_####1_tl\cs_end:
                        ( \exp_not:N \cs:w l__liftarm_connect_angle_########2_fp \exp_not:N \cs_end: )
                      }
                  }
                \seq_map_indexed_inline:Nn \l__liftarm_connect_start_arg_seq
                  {
                    \tl_build_put_right:ce { l__liftarm_connect_F_\int_use:N \l__liftarm_connect_equation_int _tl }
                      {
                        - \seq_item:Nn \l__liftarm_connect_start_coeff_seq {########1} * \cs:w c__liftarm_cos_sin_####1_tl\cs_end:
                        ( \exp_not:N \cs:w l__liftarm_connect_angle_########2_fp \exp_not:N \cs_end: )
                      }
                    \tl_build_put_right:ce { l__liftarm_connect_Jacobian_\int_use:N \l__liftarm_connect_equation_int _########2_tl }
                      {
                        - \seq_item:Nn \l__liftarm_connect_start_coeff_seq {########1} * \cs:w c__liftarm_cos_sin_diff_####1_tl\cs_end:
                        ( \exp_not:N \cs:w l__liftarm_connect_angle_########2_fp \exp_not:N \cs_end: )
                      }
                  }
                \tl_build_end:c { l__liftarm_connect_F_\int_use:N \l__liftarm_connect_equation_int _tl }
                \int_step_inline:nn { \l__liftarm_LU_N_int }
                  { \tl_build_end:c { l__liftarm_connect_Jacobian_\int_use:N \l__liftarm_connect_equation_int _########1_tl } }
              }
          }
          {
            \clist_map_inline:nn { x , y }
              {
                \fp_zero_new:c { l__liftarm_connect_constant_####1_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_fp }
                \fp_set_eq:cc { l__liftarm_connect_constant_####1_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_fp }
                  { l__liftarm_connect_start_constant_####1_fp }
              }
            \clist_map_inline:nn { arg , coeff }
              {
                \seq_clear_new:c { l__liftarm_connect_####1_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_seq }
                \seq_set_eq:cc { l__liftarm_connect_####1_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_seq }
                  { l__liftarm_connect_start_####1_seq }
              }
            \seq_put_right:ce { l__liftarm_connect_arg_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_seq }
              { \int_use:N \l__liftarm_connect_count_int }
            \seq_put_right:ce { l__liftarm_connect_coeff_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_seq }
              { \fp_use:N \l__liftarm_length_fp }
            \seq_put_right:Ne \l__liftarm_connect_coordinate_seq { \seq_item:Nn \l__liftarm_coordinate_seq { 2 } }
          }
      }
  }

\cs_new_protected:Npn \__liftarm_connect_stop_criterion:
  {
    \int_step_inline:nn { \l__liftarm_LU_N_int }
      { \fp_set:cn { l__liftarm_LU_b_##1_fp } { \cs:w l__liftarm_connect_F_##1_tl\cs_end: } }
    \str_case:Vn \l__liftarm_connect_stop_type_str
      {
        { 1-norm }
          {
            \fp_zero:N \l__liftarm_connect_norm_fp
            \int_step_inline:nn { \l__liftarm_LU_N_int }
              { \fp_add:Nn \l__liftarm_connect_norm_fp { abs ( \cs:w l__liftarm_LU_b_##1_fp\cs_end: ) } }
            \bool_set:Nn \l__liftarm_LU_bool
              { \fp_compare_p:nNn { \l__liftarm_connect_norm_fp } > { \l__liftarm_connect_stop_value_fp } }
          }
        { 2-norm }
          {
            \fp_zero:N \l__liftarm_connect_norm_fp
            \int_step_inline:nn { \l__liftarm_LU_N_int }
              { \fp_add:Nn \l__liftarm_connect_norm_fp { ( \cs:w l__liftarm_LU_b_##1_fp\cs_end: ) ^ 2 } }
            \bool_set:Nn \l__liftarm_LU_bool
              { \fp_compare_p:nNn { sqrt ( \l__liftarm_connect_norm_fp ) } > { \l__liftarm_connect_stop_value_fp } }
          }
        { iterations }
          {
            \bool_set:Nn \l__liftarm_LU_bool
              { \fp_compare_p:nNn { \l__liftarm_LU_count_int } < { \l__liftarm_connect_stop_value_fp } }
          }
      }
  }

\cs_new_protected:Npn \__liftarm_def_coord:n #1
  {
    \path let \p { l__liftarm_coord } = (#1) in
      [
        / utils / exec =
          {
            \fp_gset:Nn \g__liftarm_coord_x_fp
              { ( \pgf@yy * \x { l__liftarm_coord } - \pgf@yx * \y { l__liftarm_coord } ) / \l__liftarm_connect_det_fp }
            \fp_gset:Nn \g__liftarm_coord_y_fp
              { ( \pgf@xx * \y { l__liftarm_coord } - \pgf@xy * \x { l__liftarm_coord } ) / \l__liftarm_connect_det_fp }
          }
      ] ;
  }

\cs_new_protected:Npn \__liftarm_default:nnnn #1#2#3#4
  {
    \pgfmathparse {#3}
    \fp_set:Nn \l__liftarm_length_fp { \pgfmathresult }
    \fp_compare:nNnTF { \l__liftarm_length_fp } < { 0 }
      { \PackageWarning { liftarm } { The~length~( \fp_use:N \l__liftarm_length_fp )~of~the~liftarm~is~smaller~than~0. } }
      {
        \pgfmathparse {#4}
        \fp_set:Nn \l__liftarm_angle_fp { \pgfmathresult }
        \begin { scope }
          [
            shift = { (#2) } ,
            rotate = \fp_use:N \l__liftarm_angle_fp
          ]
          \pgfkeys { / liftarm , #1 }
          \tl_set:Ne \l__liftarm_color_tl
            {
              \cs:w
                l__liftarm_color_
                \int_mod:nn { \fp_eval:n { trunc ( \l__liftarm_length_fp , 0 ) } } { \pgfkeysvalueof { / liftarm / color~modulo } }_tl
              \cs_end:
            }
          \begin { scope } [ shift = { ( - \fp_use:N \l__liftarm_origin_fp , 0 ) } ]
            \str_case:Vn \l__liftarm_type_str
              {
                { liftarm }
                  {
                    \pgfmathparse { \pgfkeysvalueof { / liftarm / liftarm~thickness } }
                    \fp_set:Nn \l__liftarm_half_thickness_fp { 0.5 * \l__liftarm_scalefactor_fp * \pgfmathresult }
                    \pgfmathparse { \pgfkeysvalueof { / liftarm / hole~radius } }
                    \fp_set:Nn \l__liftarm_hole_radius_fp { \l__liftarm_scalefactor_fp * \pgfmathresult }
                    \bool_if:NTF \l__liftarm_brick_bool
                      {
                        \tl_build_begin:N \l__liftarm_shape_tl
                        \tl_build_put_right:Ne \l__liftarm_shape_tl
                          {
                            ( -1 , \fp_eval:n { - \l__liftarm_scalefactor_fp * 0.7 } )
                            -- ( -1 , \fp_eval:n { \l__liftarm_scalefactor_fp * 0.5 } )
                          }
                        \int_step_inline:nnn { -1 } { \fp_eval:n { trunc ( \l__liftarm_length_fp , 0 ) } }
                          {
                            \tl_build_put_right:Ne \l__liftarm_shape_tl
                              {
                                -- (
                                  \fp_eval:n { ##1 + 0.5 - \l__liftarm_scalefactor_fp * 0.3 } ,
                                  \fp_eval:n { \l__liftarm_scalefactor_fp * 0.5 }
                                )
                                --++ ( 0 , \fp_eval:n { \l__liftarm_scalefactor_fp * 0.2 } )
                                --++ ( \fp_eval:n { \l__liftarm_scalefactor_fp * 0.6 } , 0 )
                                --++ ( 0 , \fp_eval:n { - \l__liftarm_scalefactor_fp * 0.2 } )
                              }
                          }
                        \tl_build_put_right:Ne \l__liftarm_shape_tl
                          {
                            -- ( \fp_eval:n { \l__liftarm_length_fp + 1 } , \fp_eval:n { \l__liftarm_scalefactor_fp * 0.5 } )
                            --++ ( 0 , \fp_eval:n { - \l__liftarm_scalefactor_fp * 1.2 } )
                            -- cycle
                          }
                        \tl_build_end:N \l__liftarm_shape_tl
                      }
                      {
                        \tl_set:Ne \l__liftarm_shape_tl
                          {
                            ( 0 , \fp_use:N \l__liftarm_half_thickness_fp )
                            arc
                              [
                                start~angle = 90 ,
                                end~angle = 270 ,
                                radius = \fp_use:N \l__liftarm_half_thickness_fp
                              ]
                            -- ( \fp_use:N \l__liftarm_length_fp , - \fp_use:N \l__liftarm_half_thickness_fp )
                            arc
                              [
                                start~angle = -90 ,
                                end~angle = 90 ,
                                radius = \fp_use:N \l__liftarm_half_thickness_fp
                              ]
                            -- cycle
                          }
                      }
                    \tl_build_begin:N \l__liftarm_holes_tl
                    \int_step_inline:nnn { 0 } { \fp_eval:n { trunc ( \l__liftarm_length_fp , 0 ) } }
                      {
                        \clist_if_in:enTF { \pgfkeysvalueof { / liftarm / axle~holes } } {##1}
                          {
                            \int_step_inline:nn { 4 }
                              {
                                \tl_build_put_right:Ne \l__liftarm_holes_tl
                                  {
                                    (
                                      \fp_eval:n
                                        {
                                          ##1 + sqrt ( 2 ) * \l__liftarm_hole_radius_fp * sind ( \c__liftarm_axle_hole_angle_fp )
                                          * cosd ( ####1 * 90 - 45 )
                                        } ,
                                      \fp_eval:n { sqrt ( 2 ) * \l__liftarm_hole_radius_fp * sind ( \c__liftarm_axle_hole_angle_fp )
                                      * sind ( ####1 * 90 - 45 ) }
                                    )
                                    -- (
                                      \fp_eval:n
                                        {
                                          ##1
                                          + \l__liftarm_hole_radius_fp * cosd ( ####1 * 90 - \c__liftarm_axle_hole_angle_fp )
                                        } ,
                                      \fp_eval:n { \l__liftarm_hole_radius_fp * sind ( ####1 * 90 - \c__liftarm_axle_hole_angle_fp ) }
                                    )
                                    arc
                                      [
                                        start~angle = \fp_eval:n { ####1 * 90 - \c__liftarm_axle_hole_angle_fp } ,
                                        end~angle = \fp_eval:n { ####1 * 90 + \c__liftarm_axle_hole_angle_fp } ,
                                        radius = \fp_use:N \l__liftarm_hole_radius_fp
                                      ]
                                    --
                                  }
                              }
                            \tl_build_put_right:Nn \l__liftarm_holes_tl { cycle }
                          }
                          {
                            \tl_build_put_right:Ne \l__liftarm_holes_tl
                              { ( ##1 , 0 ) circle [ radius = \fp_use:N \l__liftarm_hole_radius_fp ] }
                          }
                      }
                    \tl_build_end:N \l__liftarm_holes_tl
                    \fill [ \l__liftarm_color_tl , even~odd~rule , / liftarm / liftarm_style ]
                      \l__liftarm_shape_tl \l__liftarm_holes_tl ;
                    \bool_if:NT \l__liftarm_contour_bool
                      { \draw [ \l__liftarm_color_tl ! 75 ! black , ultra~thick , / liftarm / contour_style ] \l__liftarm_shape_tl ; }
                    \clist_map_inline:en { \pgfkeysvalueof { / liftarm / mark~holes } }
                      {
                        \fill [ black , / liftarm / mark_style ]
                          ( {##1} , 0 ) circle [ radius = \fp_eval:n { \l__liftarm_mark_radius_fp * \l__liftarm_hole_radius_fp } ] ;
                      }
                    \clist_map_inline:en { \pgfkeysvalueof { / liftarm / screw~holes } }
                      {
                        \clist_map_inline:nn { -1 , 1 }
                          {
                            \fill [ black , shift = { ( {##1} , 0 ) } , rotate = 45 , / liftarm / screw_style ]
                              (
                                \fp_eval:n { ####1 * \l__liftarm_screw_angle_fp }
                                \c_colon_str
                                \fp_eval:n { \l__liftarm_screw_radius_fp * \l__liftarm_hole_radius_fp }
                              )
                              arc
                                [
                                  start~angle = \fp_eval:n { ####1 * \l__liftarm_screw_angle_fp } ,
                                  end~angle = \fp_eval:n { ####1 * ( 180 - \l__liftarm_screw_angle_fp ) } ,
                                  radius = \fp_eval:n { \l__liftarm_screw_radius_fp * \l__liftarm_hole_radius_fp }
                                ]
                              ;
                          }
                      }
                  }
                { line~segment }
                  {
                    \draw [ \l__liftarm_color_tl , / liftarm / liftarm_style ] ( 0 , 0 ) -- ( \fp_use:N \l__liftarm_length_fp , 0 ) ;
                  }
              }
            \clist_map_inline:en { \pgfkeysvalueof { / liftarm / coordinate } }
              {
                \seq_set_split:Nnn \l__liftarm_coordinate_seq { / } {##1}
                \coordinate ( \seq_item:Nn \l__liftarm_coordinate_seq { 2 } )
                  at ( { \seq_item:Nn \l__liftarm_coordinate_seq { 1 } } , 0 ) ;
              }
            \bool_if:NT \l__liftarm_animate_bool
              {
                \clist_map_inline:Nn \l__liftarm_trace_clist
                  {
                    \seq_set_split:Nnn \l__liftarm_trace_item_seq { / } {##1}
                    \stepcounter { g__liftarm_animate_frame_number_counter }
                    \tl_build_gput_right:Ne \g__liftarm_animate_frames_trace_tl
                      {
                        \exp_not:n { \newframe \begin } { scope }
                        [ shift = { (#2) } , rotate = \fp_use:N \l__liftarm_angle_fp ]
                        \exp_not:N \begin { scope }
                        [ shift = { ( \fp_eval:n { \seq_item:Nn \l__liftarm_trace_item_seq { 1 } - \l__liftarm_origin_fp } , 0 ) } ]
                        \tl_if_empty:eTF { \seq_item:Nn \l__liftarm_trace_item_seq { 3 } }
                          {
                            \exp_not:N \fill
                            [ black ] ( 0 , 0 ) circle [ radius = \fp_eval:n { \l__liftarm_hole_radius_fp * 2 / 3 } ] ;
                          }
                          { \seq_item:Nn \l__liftarm_trace_item_seq { 3 } }
                        \exp_not:n { \end { scope } \end { scope } }
                      }
                    \tl_if_empty:eTF { \seq_item:Nn \l__liftarm_trace_item_seq { 2 } }
                      {
                        \tl_build_gput_right:ce { g__liftarm_animate_timeline_0_tl }
                          { \theg__liftarm_animate_frame_number_counter x 0 , }
                      }
                      {
                        \pgfmathparse { \use:e { \seq_item:Nn \l__liftarm_trace_item_seq { 2 } } }
                        \tl_build_gput_right:ce { g__liftarm_animate_timeline_\theg__liftarm_animate_step_number_counter _tl }
                          { \theg__liftarm_animate_frame_number_counter x \fp_eval:n { \pgfmathresult } , }
                      }
                  }
              }
          \end { scope }
        \end { scope }
      }
  }

\cs_new_protected:Npn \__liftarm_LU_decomposition:
  {
    \int_step_inline:nn { \l__liftarm_LU_N_int }
      {
        \int_zero_new:c { l__liftarm_LU_P_##1_int }
        \int_set:cn { l__liftarm_LU_P_##1_int } {##1}
      }
    \int_step_inline:nn { \l__liftarm_LU_N_int }
      {
        \fp_zero:N \l__liftarm_LU_maxA_fp
        \int_set:Nn \l__liftarm_LU_imax_int {##1}
        \int_step_inline:nnn {##1} { \l__liftarm_LU_N_int }
          {
            \fp_set:Nn \l__liftarm_LU_tmp_fp { abs ( \cs:w l__liftarm_LU_A_####1_##1_fp\cs_end: ) }
            \fp_compare:nNnT { \l__liftarm_LU_tmp_fp } > { \l__liftarm_LU_maxA_fp }
              {
                \fp_set_eq:NN \l__liftarm_LU_maxA_fp \l__liftarm_LU_tmp_fp
                \int_set:Nn \l__liftarm_LU_imax_int {####1}
              }
          }
        \int_compare:nNnF { \l__liftarm_LU_imax_int } = {##1}
          {
            \int_set_eq:Nc \l__liftarm_LU_j_int { l__liftarm_LU_P_##1_int }
            \int_set_eq:cc { l__liftarm_LU_P_##1_int } { l__liftarm_LU_P_\int_use:N \l__liftarm_LU_imax_int _int }
            \int_set_eq:cN { l__liftarm_LU_P_\int_use:N \l__liftarm_LU_imax_int _int } \l__liftarm_LU_j_int
            \int_step_inline:nn { \l__liftarm_LU_N_int }
              {
                \fp_set_eq:Nc \l__liftarm_LU_tmp_fp { l__liftarm_LU_A_##1_####1_fp }
                \fp_set_eq:cc { l__liftarm_LU_A_##1_####1_fp } { l__liftarm_LU_A_\int_use:N \l__liftarm_LU_imax_int _####1_fp }
                \fp_set_eq:cN { l__liftarm_LU_A_\int_use:N \l__liftarm_LU_imax_int _####1_fp } \l__liftarm_LU_tmp_fp
              }
          }
        \int_step_inline:nnn { ##1 + 1 } { \l__liftarm_LU_N_int }
          {
            \fp_set:cn { l__liftarm_LU_A_####1_##1_fp }
              { \cs:w l__liftarm_LU_A_####1_##1_fp\cs_end: / \cs:w l__liftarm_LU_A_##1_##1_fp\cs_end: }
            \int_step_inline:nnn { ##1 + 1 } { \l__liftarm_LU_N_int }
              {
                \fp_sub:cn { l__liftarm_LU_A_####1_########1_fp }
                  { \cs:w l__liftarm_LU_A_####1_##1_fp\cs_end: * \cs:w l__liftarm_LU_A_##1_########1_fp\cs_end: }
              }
          }
      }
  }

\cs_new_protected:Npn \__liftarm_LU_solve:
  {
    \int_step_inline:nn { \l__liftarm_LU_N_int }
      {
        \fp_zero_new:c { l__liftarm_LU_x_##1_fp }
        \fp_set_eq:cc { l__liftarm_LU_x_##1_fp } { l__liftarm_LU_b_\int_use:c { l__liftarm_LU_P_##1_int }_fp }
        \int_step_inline:nn { ##1 - 1 }
          {
            \fp_sub:cn { l__liftarm_LU_x_##1_fp }
              { \cs:w l__liftarm_LU_A_##1_####1_fp\cs_end: * \cs:w l__liftarm_LU_x_####1_fp\cs_end: }
          }
      }
    \int_step_inline:nnnn { \l__liftarm_LU_N_int } { -1 } { 1 }
      {
        \int_step_inline:nnn { ##1 + 1 } { \l__liftarm_LU_N_int }
          {
            \fp_sub:cn { l__liftarm_LU_x_##1_fp }
              { \cs:w l__liftarm_LU_A_##1_####1_fp\cs_end: * \cs:w l__liftarm_LU_x_####1_fp\cs_end: }
          }
        \fp_set:cn { l__liftarm_LU_x_##1_fp } { \cs:w l__liftarm_LU_x_##1_fp\cs_end: / \cs:w l__liftarm_LU_A_##1_##1_fp\cs_end: }
      }
  }

\cs_new:Npn \__liftarm_Mod:nn #1#2
  {
    min
      (
        Mod
          (
            \fp_eval:n { \cs:w l__liftarm_connect_two_#1_option_#2_angle_fp\cs_end: - \cs:w l__liftarm_connect_angle_#1_fp\cs_end: } ,
            360
          ) ,
        Mod
          (
            \fp_eval:n { \cs:w l__liftarm_connect_angle_#1_fp\cs_end: - \cs:w l__liftarm_connect_two_#1_option_#2_angle_fp\cs_end: } ,
            360
          )
      )
  }

%%> \subsection{Document commands and environment}

\NewDocumentCommand \liftarm { O {} m m m }
  { \__liftarm_default:nnnn {#1} {#2} {#3} {#4} }

\NewDocumentCommand \liftarmanimate { O {} m m m }
  {
    \bool_set_true:N \l__liftarm_animate_bool
    \stepcounter { g__liftarm_animate_number_of_animation_counter }
    \setcounter { g__liftarm_animate_number_of_steps_counter } { -1 }
    \tl_build_gbegin:N \g__liftarm_animate_frames_tl
    \tl_build_gbegin:N \g__liftarm_animate_frames_trace_tl
    \setcounter { g__liftarm_animate_step_number_counter } { -1 }
    \foreach \l__liftarm_animate_value_tl in {#3}
      {
        \stepcounter { g__liftarm_animate_number_of_steps_counter }
        \tl_build_gput_right:Ne \g__liftarm_animate_frames_tl
          {
            \exp_not:n { \newframe \stepcounter { g__liftarm_animate_step_number_counter } #4 }
            { \l__liftarm_animate_value_tl }
          }
      }
    \tl_build_gend:N \g__liftarm_animate_frames_tl
    \int_step_inline:nnn { 0 } { \theg__liftarm_animate_number_of_steps_counter }
      {
        \tl_clear_new:c { g__liftarm_animate_timeline_##1_tl }
        \tl_build_gbegin:c { g__liftarm_animate_timeline_##1_tl }
      }
    \tl_build_gput_right:cn { g__liftarm_animate_timeline_0_tl } { c , }
    \setcounter { g__liftarm_animate_frame_number_counter } { \theg__liftarm_animate_number_of_steps_counter }
    \file_if_exist:nF { \c_sys_jobname_str \theg__liftarm_animate_number_of_animation_counter . tln }
      {
        \iow_open:Nn \g__liftarm_animate_write_timeline_iow
          { \c_sys_jobname_str \theg__liftarm_animate_number_of_animation_counter . tln }
        \iow_now:Ne \g__liftarm_animate_write_timeline_iow { \c_colon_str \c_colon_str c , 0 }
        \iow_close:N \g__liftarm_animate_write_timeline_iow
      }
    \begin { animateinline } [ #1 , timeline = \c_sys_jobname_str \theg__liftarm_animate_number_of_animation_counter . tln ] {#2}
      \tl_tail:N \g__liftarm_animate_frames_tl%remove the first \newframe
      \tl_build_gend:N \g__liftarm_animate_frames_trace_tl
      \g__liftarm_animate_frames_trace_tl
    \end { animateinline }
    \iow_open:Nn \g__liftarm_animate_write_timeline_iow { \c_sys_jobname_str \theg__liftarm_animate_number_of_animation_counter . tln }
    \int_step_inline:nnn { 0 } { \theg__liftarm_animate_number_of_steps_counter }
      {
        \tl_build_gend:c { g__liftarm_animate_timeline_##1_tl }
        \iow_now:Ne \g__liftarm_animate_write_timeline_iow
          { \c_colon_str \c_colon_str \cs:w g__liftarm_animate_timeline_##1_tl\cs_end: ##1 }
      }
    \iow_close:N \g__liftarm_animate_write_timeline_iow
    \bool_set_false:N \l__liftarm_animate_bool
  }

\NewDocumentCommand \liftarmconstruct { m }
  {
    \tl_gput_right:Nn \g__liftarm_construct_tl {#1}
    \g__liftarm_construct_tl
  }

\NewDocumentCommand \liftarmconstructclear {}
  { \tl_gclear:N \g__liftarm_construct_tl }

\NewDocumentEnvironment { liftarmconnect } { O {} +b }
  {
    \pgfkeys { / liftarm , #1 }
    %verify that the contents consists only of commands \liftarm because the contents of this environment are processed several times
    \DeclareExpandableDocumentCommand \liftarm { O {} m m m } {}%expandable for usage in \tl_set:Ne
    \tl_set:Ne \l__liftarm_tmp_tl {#2}
    \tl_remove_all:Nn \l__liftarm_tmp_tl { \par }
    \tl_if_blank:VF \l__liftarm_tmp_tl
      { \PackageError { liftarm } { The~environment~liftarmconnect~should~only~consist~of~commands~\protect \liftarm } {} }
    \int_zero:N \l__liftarm_LU_N_int
    \RenewDocumentCommand \liftarm { O {} m m m } { \int_incr:N \l__liftarm_LU_N_int }
    #2
    \fp_set:Nn \l__liftarm_connect_det_fp { \pgf@yy * \pgf@xx - \pgf@yx * \pgf@xy }
    \int_case:nnF { \l__liftarm_LU_N_int }
      {
        { 0 }
          {}
        { 1 }
          {
            \RenewDocumentCommand \liftarm { O {} m m m }
              { \__liftarm_default:nnnn {##1} {##2} {##3} {##4} }
          }
        { 2 }
          {
            \int_zero:N \l__liftarm_connect_count_int
            \int_zero:N \l__liftarm_connect_equation_int
            \seq_clear:N \l__liftarm_connect_coordinate_seq
            \fp_set_eq:NN \l__liftarm_origin_connect_initial_fp \l__liftarm_origin_fp
            \RenewDocumentCommand \liftarm { O {} m m m }
              {
                \int_incr:N \l__liftarm_connect_count_int
                \fp_zero_new:c { l__liftarm_connect_angle_\int_use:N \l__liftarm_connect_count_int _fp }
                \pgfmathparse {##4}
                \fp_set:cn { l__liftarm_connect_angle_\int_use:N \l__liftarm_connect_count_int _fp } { \pgfmathresult }
                \fp_set_eq:NN \l__liftarm_origin_fp \l__liftarm_origin_connect_initial_fp
                \pgfkeys
                  {
                    / liftarm / connect_algorithm ,
                    coordinate = \pgfkeysvalueof { / liftarm / coordinate } ,
                    ##1
                  }
                \__liftarm_def_coord:n {##2}
                \fp_set_eq:cN { l__liftarm_connect_two_\int_to_Alph:n { \l__liftarm_connect_count_int }_x_fp } \g__liftarm_coord_x_fp
                \fp_set_eq:cN { l__liftarm_connect_two_\int_to_Alph:n { \l__liftarm_connect_count_int }_y_fp } \g__liftarm_coord_y_fp
                \int_compare:nNnTF { \l__liftarm_connect_count_int } = { 1 }
                  {
                    \clist_map_inline:en { \pgfkeysvalueof { / liftarm / connect_algorithm / coordinate } }
                      {
                        \seq_set_split:Nnn \l__liftarm_coordinate_seq { / } {####1}
                        \tl_clear_new:c { l__liftarm_connect_two_A_length_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_tl }
                        \tl_set:ce { l__liftarm_connect_two_A_length_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_tl }
                          { \seq_item:Nn \l__liftarm_coordinate_seq { 1 } - \fp_use:N \l__liftarm_origin_fp }
                        \seq_put_right:Ne \l__liftarm_connect_coordinate_seq { \seq_item:Nn \l__liftarm_coordinate_seq { 2 } }
                      }
                  }
                  {
                    \clist_map_inline:en { \pgfkeysvalueof { / liftarm / connect_algorithm / coordinate } }
                      {
                        \seq_set_split:Nnn \l__liftarm_coordinate_seq { / } {####1}
                        \seq_if_in:NeT \l__liftarm_connect_coordinate_seq { \seq_item:Nn \l__liftarm_coordinate_seq { 2 } }
                          {
                            \int_incr:N \l__liftarm_connect_equation_int
                            \int_compare:nNnT { \l__liftarm_connect_equation_int } > { 1 }
                              { \PackageError { liftarm } { There~are~too~many~conditions~for~2~liftarms } {} }
                            \pgfmathparse
                              { \cs:w l__liftarm_connect_two_A_length_coord_\seq_item:Nn \l__liftarm_coordinate_seq { 2 }_tl\cs_end: }
                            \fp_set:Nn \l__liftarm_connect_two_A_length_fp { \pgfmathresult }
                            \pgfmathparse { \seq_item:Nn \l__liftarm_coordinate_seq { 1 } }
                            \fp_set:Nn \l__liftarm_connect_two_B_length_fp { \pgfmathresult - \l__liftarm_origin_fp }
                          }
                      }
                  }
              }
            #2
            \fp_set:Nn \l__liftarm_connect_two_length_fp
              {
                sqrt (
                  ( \l__liftarm_connect_two_A_x_fp - \l__liftarm_connect_two_B_x_fp ) ^ 2
                  + ( \l__liftarm_connect_two_A_y_fp - \l__liftarm_connect_two_B_y_fp ) ^ 2
                )
              }
            \fp_set:Nn \l__liftarm_connect_two_angle_fp
              {
                atand (
                  \l__liftarm_connect_two_B_y_fp - \l__liftarm_connect_two_A_y_fp ,
                  \l__liftarm_connect_two_B_x_fp - \l__liftarm_connect_two_A_x_fp
                )
              }
            \fp_set:Nn \l__liftarm_connect_two_A_angle_fp
              {
                acosd (
                  (
                    ( \l__liftarm_connect_two_A_length_fp ) ^ 2 + ( \l__liftarm_connect_two_length_fp ) ^ 2
                    - ( \l__liftarm_connect_two_B_length_fp ) ^ 2
                  ) / ( 2 * \l__liftarm_connect_two_A_length_fp * \l__liftarm_connect_two_length_fp )
                )
              }
            \fp_set:Nn \l__liftarm_connect_two_B_angle_fp
              {
                acosd (
                  (
                    ( \l__liftarm_connect_two_B_length_fp ) ^ 2 + ( \l__liftarm_connect_two_length_fp ) ^ 2
                    - ( \l__liftarm_connect_two_A_length_fp ) ^ 2
                  ) / ( 2 * \l__liftarm_connect_two_B_length_fp * \l__liftarm_connect_two_length_fp )
                )
              }
            \fp_set:cn { l__liftarm_connect_two_1_option_0_angle_fp }
              { \l__liftarm_connect_two_angle_fp + \l__liftarm_connect_two_A_angle_fp }
            \fp_set:cn { l__liftarm_connect_two_1_option_1_angle_fp }
              { \l__liftarm_connect_two_angle_fp - \l__liftarm_connect_two_A_angle_fp }
            \fp_set:cn { l__liftarm_connect_two_2_option_0_angle_fp }
              { 180 + \l__liftarm_connect_two_angle_fp - \l__liftarm_connect_two_B_angle_fp }
            \fp_set:cn { l__liftarm_connect_two_2_option_1_angle_fp }
              { 180 + \l__liftarm_connect_two_angle_fp + \l__liftarm_connect_two_B_angle_fp }
            \pgfmathparse
              {
                \__liftarm_Mod:nn { 1 } { 0 } + \__liftarm_Mod:nn { 2 } { 0 }
                >
                \__liftarm_Mod:nn { 1 } { 1 } + \__liftarm_Mod:nn { 2 } { 1 }
              }
            \tl_set:Ne \l__liftarm_tmp_tl { \pgfmathresult }
            \int_zero:N \l__liftarm_connect_count_int
            \RenewDocumentCommand \liftarm { O {} m m m }
              {
                \int_incr:N \l__liftarm_connect_count_int
                \__liftarm_default:nnnn {##1} {##2} {##3}
                  { \fp_use:c { l__liftarm_connect_two_\int_use:N \l__liftarm_connect_count_int _option_\l__liftarm_tmp_tl _angle_fp } }
              }
          }
      }
      {
        \int_zero:N \l__liftarm_connect_count_int
        \int_zero:N \l__liftarm_connect_equation_int
        \seq_clear:N \l__liftarm_connect_coordinate_seq
        \fp_set_eq:NN \l__liftarm_origin_connect_initial_fp \l__liftarm_origin_fp
        \RenewDocumentCommand \liftarm { O {} m m m } { \__liftarm_connect:nnnn {##1} {##2} {##3} {##4} }
        #2
        \int_compare:nNnF { \l__liftarm_connect_equation_int } = { \l__liftarm_LU_N_int }
          {
            \PackageError { liftarm }
              {
                The~Jacobian~matrix~is~not~square~
                (the~size~is~\int_use:N \l__liftarm_connect_equation_int \space by~\int_use:N \l__liftarm_LU_N_int )
              } {}
          }
        \int_zero:N \l__liftarm_LU_count_int
        \int_step_inline:nn { \l__liftarm_LU_N_int }
          { \fp_zero_new:c { l__liftarm_LU_b_##1_fp } }
        \__liftarm_connect_stop_criterion:
        \bool_while_do:Nn \l__liftarm_LU_bool
          {
            \int_step_inline:nn { \l__liftarm_LU_N_int }
              {
                \int_step_inline:nn { \l__liftarm_LU_N_int }
                  { \fp_set:cn { l__liftarm_LU_A_##1_####1_fp } { \cs:w l__liftarm_connect_Jacobian_##1_####1_tl\cs_end: } }
              }
            \__liftarm_LU_decomposition:
            \__liftarm_LU_solve:
            \int_step_inline:nn { \l__liftarm_LU_N_int }
              { \fp_sub:cn { l__liftarm_connect_angle_##1_fp } { \cs:w l__liftarm_LU_x_##1_fp\cs_end: } }
            \int_incr:N \l__liftarm_LU_count_int
            \__liftarm_connect_stop_criterion:
          }
        \int_zero:N \l__liftarm_connect_count_int
        \RenewDocumentCommand \liftarm { O {} m m m }
          {
            \int_incr:N \l__liftarm_connect_count_int
            \__liftarm_default:nnnn {##1} {##2} {##3}
              { \fp_eval:n { \cs:w l__liftarm_connect_angle_\int_use:N \l__liftarm_connect_count_int _fp\cs_end: / deg } }
          }
      }
    #2
  }
  {}

\endinput