-
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
-
913
-
914
-
915
-
916
-
917
-
918
-
919
-
920
-
921
-
922
-
923
-
924
-
925
-
926
-
927
-
928
-
929
-
930
-
931
-
932
-
933
-
934
-
935
-
936
-
937
-
938
-
939
-
940
-
941
-
942
-
943
-
944
-
945
-
946
-
947
-
948
-
949
-
950
-
951
-
952
-
953
-
954
-
955
-
956
-
957
-
958
-
959
-
960
-
961
-
962
-
963
-
964
-
965
-
966
-
967
-
968
-
969
-
970
-
971
-
972
-
973
-
974
-
975
-
976
-
977
-
978
-
979
-
980
-
981
-
982
-
983
-
984
-
985
-
986
-
987
-
988
-
989
-
990
-
991
-- Copyright 2026 Shota FUJI
--
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
--
-- SPDX-License-Identifier: MPL-2.0
module Template exposing (template)
import Length exposing (Length, toMM)
import Parameters exposing (LoopStyle(..), Parameters, canvasSizeDimension)
import String
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Svg.Path exposing (LargeArcFlag(..), PathCommand(..), PointMode(..), SweepFlag(..))
type Anchor
= TopLeft ( Float, Float )
| TopRight ( Float, Float )
| BottomLeft ( Float, Float )
| BottomRight ( Float, Float )
type alias Size =
{ width : Float, height : Float }
topLeftFor : Anchor -> Size -> ( Float, Float )
topLeftFor anchor { width, height } =
case anchor of
TopLeft ( x, y ) ->
( x, y )
TopRight ( x, y ) ->
( x - width, y )
BottomLeft ( x, y ) ->
( x, y - height )
BottomRight ( x, y ) ->
( x - width, y - height )
template : Parameters -> List (Svg.Attribute msg) -> Svg.Svg msg
template params attrs =
let
( canvasWidth, canvasHeight ) =
canvasSizeDimension params.rendering.size
|> Tuple.mapBoth toMM toMM
margin =
toMM params.rendering.margin
in
svg
(viewBox (String.join " " [ "0", "0", String.fromInt (ceiling canvasWidth), String.fromInt (ceiling canvasHeight) ])
:: class "print"
:: attrs
)
[ scaleChecker (BottomLeft ( margin, canvasHeight - margin ))
, legends params (BottomLeft ( margin + scaleCheckerSize.width + 10, canvasHeight - margin ))
, defs
[]
[ pattern
[ id "SkivingPattern"
, x "0"
, y "0"
, width "3"
, height "3"
, patternUnits "userSpaceOnUse"
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( -1, -1 )
, LineTo Absolute ( 4, 4 )
]
, fill "none"
, stroke "currentColor"
, strokeWidth "0.1"
, strokeDasharray "1"
]
[]
]
]
, surfacePieces params (TopLeft ( margin, margin ))
, linings params (TopRight ( canvasWidth - margin, margin ))
-- TODO: Move below the linings
, loops params (BottomRight ( canvasWidth - margin, canvasHeight - margin ))
]
scaleCheckerSize : Size
scaleCheckerSize =
Size 10 10
scaleChecker : Anchor -> Svg msg
scaleChecker anchor =
let
( ox, oy ) =
topLeftFor anchor scaleCheckerSize
px : Float -> String
px n =
String.fromFloat (ox + n)
py : Float -> String
py n =
String.fromFloat (oy + n)
in
g
[]
[ g
[ stroke "currentColor", strokeWidth "0.2" ]
[ line
[ x1 (px 0), y1 (py 8), x2 (px 0), y2 (py 10) ]
[]
, line
[ x1 (px 0), y1 (py 9), x2 (px 10), y2 (py 9) ]
[]
, line
[ x1 (px 10), y1 (py 8), x2 (px 10), y2 (py 10) ]
[]
]
, text_
[ x (px 5)
, y (py 7)
, fontSize "3"
, fontWeight "100"
, textAnchor "middle"
, fill "currentColor"
]
[ text "10mm" ]
]
legendsSize : Size
legendsSize =
Size 34 23
legends : Parameters -> Anchor -> Svg msg
legends params anchor =
let
( ox, oy ) =
topLeftFor anchor legendsSize
px : Float -> String
px n =
String.fromFloat (ox + n)
py : Float -> String
py n =
String.fromFloat (oy + n)
in
g
[]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + 5 )
, HorizontalLineTo Relative 8
]
, stroke "currentColor"
, strokeWidth (params.rendering.lineWidth |> toMM |> String.fromFloat)
]
[]
, text_
[ x (px 10)
, y (py 6)
, fontSize "4"
, fontWeight "100"
, textAnchor "left"
, fill "currentColor"
]
[ text "Cut line" ]
, rect
[ x (px 0)
, y (py 10)
, stroke "currentColor"
, width "8"
, height "5"
, strokeWidth "0.2"
, fill "url(#SkivingPattern)"
]
[]
, text_
[ x (px 10)
, y (py 14)
, fontSize "4"
, fontWeight "100"
, textAnchor "left"
, fill "currentColor"
]
[ text "Skiving area" ]
, g
[ fill "none"
, stroke "currentColor"
, strokeWidth "0.1"
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + 1.5, oy + 20.5 )
, HorizontalLineTo Relative 5
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + 4, oy + 18 )
, VerticalLineTo Relative 5
]
]
[]
]
, text_
[ x (px 10)
, y (py 22)
, fontSize "4"
, fontWeight "100"
, textAnchor "left"
, fill "currentColor"
]
[ text "Hole center" ]
]
skivingSeamStroke : Parameters -> List (Svg.Attribute msg)
skivingSeamStroke params =
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, strokeDasharray "1 0.5"
]
surfacePieces : Parameters -> Anchor -> Svg msg
surfacePieces params anchor =
let
lugWidth =
toMM params.lugWidth
gap =
toMM params.rendering.gap
headerHeight =
5.0
sectionWidth =
lugWidth * 2 + gap
( ox, oy ) =
topLeftFor anchor
(Size sectionWidth
(headerHeight
+ gap
/ 2
+ Basics.max
(.height (longPieceSize params))
(.height (shortPieceSize params))
)
)
in
g
[]
[ longPiece params (TopLeft ( ox, oy + headerHeight + gap / 2 ))
, shortPiece params (TopLeft ( ox + lugWidth + gap, oy + headerHeight + gap / 2 ))
, text_
[ x (ox + sectionWidth / 2 |> String.fromFloat)
, y (oy + 4 |> String.fromFloat)
, fontSize "5"
, fontWeight "100"
, textAnchor "middle"
, fill "currentColor"
]
[ text ("Surfaces / t = " ++ (toMM params.thickness |> String.fromFloat) ++ "mm") ]
]
longPieceSize : Parameters -> Size
longPieceSize params =
Size (toMM params.lugWidth) (toMM params.longPiece.length + toMM params.longPiece.flap)
longPiece : Parameters -> Anchor -> Svg msg
longPiece params anchor =
let
lugWidth =
toMM params.lugWidth
length =
toMM params.longPiece.length
flap =
toMM params.longPiece.flap
( ox, oy ) =
topLeftFor anchor (longPieceSize params)
in
g
[]
(Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative (length + flap - lugWidth / 2)
, EllipticalArcCurve Relative
{ rx = lugWidth / 2
, ry = lugWidth / 2
, angle = 0.0
, largeArcFlag = LargeArc
, sweepFlag = Counterclockwise
, x = lugWidth
, y = 0
}
[]
, VerticalLineTo Relative -(length + flap - lugWidth / 2)
, ClosePath
]
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[]
:: Svg.path
(Svg.Path.d
[ MoveTo Absolute ( ox, oy + flap )
, HorizontalLineTo Relative lugWidth
]
:: skivingSeamStroke params
)
[]
:: Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, HorizontalLineTo Relative lugWidth
, VerticalLineTo Relative flap
, HorizontalLineTo Relative -lugWidth
, ClosePath
]
, fill "url(#SkivingPattern)"
]
[]
:: (List.range 0 (params.longPiece.buckleHole.count - 1)
|> List.map
(\index ->
let
{ buckleHole } =
params.longPiece
offset =
toMM buckleHole.offset
interval =
toMM buckleHole.interval
in
hole
params
buckleHole.diameter
( ox + lugWidth / 2
, oy + offset + interval * toFloat index
)
)
)
)
hole : Parameters -> Length -> ( Float, Float ) -> Svg msg
hole params diameter ( cx, cy ) =
let
radius =
toMM diameter / 2
in
g
[]
[ circle
[ Svg.Attributes.cx (String.fromFloat cx)
, Svg.Attributes.cy (String.fromFloat cy)
, r (String.fromFloat radius)
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[]
, g
[ fill "none"
, stroke "currentColor"
, strokeWidth "0.1"
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( cx - radius / 2, cy )
, HorizontalLineTo Relative radius
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( cx, cy - radius / 2 )
, VerticalLineTo Relative radius
]
]
[]
]
]
shortPieceSize : Parameters -> Size
shortPieceSize params =
Size (toMM params.lugWidth) (toMM params.shortPiece.length + toMM params.shortPiece.caseSideFlap)
shortPiece : Parameters -> Anchor -> Svg msg
shortPiece params anchor =
let
lugWidth =
toMM params.lugWidth
length =
toMM params.shortPiece.length
caseSideFlap =
toMM params.shortPiece.caseSideFlap
claspSideFlap =
toMM params.shortPiece.claspSideFlap
( ox, oy ) =
topLeftFor anchor (shortPieceSize params)
in
g
[]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative (length + caseSideFlap + claspSideFlap)
, HorizontalLineTo Relative lugWidth
, VerticalLineTo Relative -(length + caseSideFlap + claspSideFlap)
, ClosePath
]
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[]
, g
(skivingSeamStroke params)
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + caseSideFlap )
, HorizontalLineTo Relative lugWidth
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + caseSideFlap + length )
, HorizontalLineTo Relative lugWidth
]
]
[]
]
, g
[ fill "url(#SkivingPattern)" ]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, HorizontalLineTo Relative lugWidth
, VerticalLineTo Relative caseSideFlap
, HorizontalLineTo Relative -lugWidth
, ClosePath
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + caseSideFlap + length )
, HorizontalLineTo Relative lugWidth
, VerticalLineTo Relative claspSideFlap
, HorizontalLineTo Relative -lugWidth
, ClosePath
]
]
[]
]
]
linings : Parameters -> Anchor -> Svg msg
linings params anchor =
if toMM params.lining == 0 then
g [] []
else
let
gap =
toMM params.rendering.gap
lugWidth =
toMM params.lugWidth
sectionWidth =
lugWidth * 2 + gap
headerHeight =
5.0
size =
Size
sectionWidth
(headerHeight + gap / 2 + Basics.max (toMM params.longPiece.length) (toMM params.shortPiece.length))
( ox, oy ) =
topLeftFor anchor size
in
g
[]
[ longLining params (TopLeft ( ox, oy + headerHeight + gap / 2 ))
, shortLining params (TopLeft ( ox + lugWidth + gap, oy + headerHeight + gap / 2 ))
, text_
[ x (ox + sectionWidth / 2 |> String.fromFloat)
, y (oy + 4 |> String.fromFloat)
, fontSize "5"
, fontWeight "100"
, textAnchor "middle"
, fill "currentColor"
]
[ text ("Linings / t = " ++ (toMM params.lining |> String.fromFloat) ++ "mm") ]
]
longLiningSize : Parameters -> Size
longLiningSize params =
Size (toMM params.lugWidth) (toMM params.longPiece.length)
longLining : Parameters -> Anchor -> Svg msg
longLining params anchor =
let
lugWidth =
toMM params.lugWidth
length =
toMM params.longPiece.length
( ox, oy ) =
topLeftFor anchor (longLiningSize params)
in
g
[]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative (length - lugWidth / 2)
, EllipticalArcCurve Relative
{ rx = lugWidth / 2
, ry = lugWidth / 2
, angle = 0.0
, largeArcFlag = LargeArc
, sweepFlag = Counterclockwise
, x = lugWidth
, y = 0
}
[]
, VerticalLineTo Relative -(length - lugWidth / 2)
, ClosePath
]
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[]
]
shortLiningSize : Parameters -> Size
shortLiningSize params =
Size (toMM params.lugWidth) (toMM params.shortPiece.length)
shortLining : Parameters -> Anchor -> Svg msg
shortLining params anchor =
let
lugWidth =
toMM params.lugWidth
length =
toMM params.shortPiece.length
( ox, oy ) =
topLeftFor anchor (shortLiningSize params)
in
g
[]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative length
, HorizontalLineTo Relative lugWidth
, VerticalLineTo Relative -length
, ClosePath
]
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[]
]
loops : Parameters -> Anchor -> Svg msg
loops params anchor =
let
root =
params.shortPiece.loops
in
if root.fixed == Nothing && root.free == Nothing then
g [] []
else
let
gap =
toMM params.rendering.gap
lugWidth =
toMM params.lugWidth
strapThickness =
toMM params.thickness + toMM params.lining
widthModifier =
case root.style of
Simple ->
1.0
Folded ->
2.0
freeLoopLength =
lugWidth
* 2
+ strapThickness
* 2
+ (root.free |> Maybe.map .play |> Maybe.map toMM |> Maybe.withDefault 0)
+ (root.free |> Maybe.map .overlap |> Maybe.map toMM |> Maybe.withDefault 0)
freeLoopWidth =
root.free |> Maybe.map .width |> Maybe.map toMM |> Maybe.withDefault 0 |> (*) widthModifier
fixedLoopLength =
lugWidth * 2 + strapThickness * 2 + (root.fixed |> Maybe.map .play |> Maybe.map toMM |> Maybe.withDefault 0)
fixedLoopWidth =
root.fixed |> Maybe.map .width |> Maybe.map toMM |> Maybe.withDefault 0 |> (*) widthModifier
sectionWidth =
Basics.max freeLoopLength fixedLoopLength
headerHeight =
5.0
( ox, oy ) =
topLeftFor anchor (Size sectionWidth (headerHeight + gap / 2 + freeLoopWidth + gap + fixedLoopWidth))
in
g
[]
[ text_
[ x (ox + sectionWidth / 2 |> String.fromFloat)
, y (oy + 4 |> String.fromFloat)
, fontSize "5"
, fontWeight "100"
, textAnchor "middle"
, fill "currentColor"
]
[ text ("Loops / t = " ++ (toMM root.thickness |> String.fromFloat) ++ "mm") ]
, fixedLoop params (TopLeft ( ox, oy + headerHeight + gap / 2 ))
, freeLoop params (TopLeft ( ox, oy + headerHeight + gap * 1.5 + fixedLoopWidth ))
]
fixedLoop : Parameters -> Anchor -> Svg msg
fixedLoop params anchor =
case params.shortPiece.loops.fixed of
Nothing ->
g [] []
Just { width, play } ->
let
strapThickness =
toMM params.thickness + toMM params.lining
lugWidth =
toMM params.lugWidth
length =
lugWidth * 2 + strapThickness * 2 + toMM play
materialWidth =
case params.shortPiece.loops.style of
Simple ->
toMM width
Folded ->
toMM width * 2
( ox, oy ) =
topLeftFor anchor (Size length materialWidth)
hCutLine : Float -> Svg msg
hCutLine yOffset =
g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + yOffset )
, HorizontalLineTo Relative lugWidth
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth, oy + yOffset )
, HorizontalLineTo Relative strapThickness
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth + strapThickness, oy + yOffset )
, HorizontalLineTo Relative lugWidth
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth * 2 + strapThickness, oy + yOffset )
, HorizontalLineTo Relative strapThickness
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth * 2 + strapThickness * 2, oy + yOffset )
, HorizontalLineTo Relative (toMM play)
]
]
[]
]
in
g
[]
[ hCutLine 0
, hCutLine materialWidth
, case params.shortPiece.loops.style of
Simple ->
g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative (toMM width)
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + length, oy )
, VerticalLineTo Relative (toMM width)
]
]
[]
]
Folded ->
g
[]
[ g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, strokeDasharray "1 0.5"
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + toMM width / 2 )
, HorizontalLineTo Relative length
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + toMM width * 1.5 )
, HorizontalLineTo Relative length
]
]
[]
]
, g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative (toMM width * 2)
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + length, oy )
, VerticalLineTo Relative (toMM width * 2)
]
]
[]
]
]
]
freeLoop : Parameters -> Anchor -> Svg msg
freeLoop params anchor =
case params.shortPiece.loops.free of
Nothing ->
g [] []
Just { width, play, overlap } ->
let
strapThickness =
toMM params.thickness + toMM params.lining
lugWidth =
toMM params.lugWidth
length =
lugWidth * 2 + strapThickness * 2 + toMM play + toMM overlap
materialWidth =
case params.shortPiece.loops.style of
Simple ->
toMM width
Folded ->
toMM width * 2
( ox, oy ) =
topLeftFor anchor (Size length materialWidth)
hCutLine : Float -> Svg msg
hCutLine yOffset =
g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + yOffset )
, HorizontalLineTo Relative lugWidth
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth, oy + yOffset )
, HorizontalLineTo Relative strapThickness
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth + strapThickness, oy + yOffset )
, HorizontalLineTo Relative lugWidth
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth * 2 + strapThickness, oy + yOffset )
, HorizontalLineTo Relative strapThickness
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth * 2 + strapThickness * 2, oy + yOffset )
, HorizontalLineTo Relative (toMM play)
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + lugWidth * 2 + strapThickness * 2 + toMM play, oy + yOffset )
, HorizontalLineTo Relative (toMM overlap)
]
]
[]
]
in
g
[]
[ hCutLine 0
, hCutLine materialWidth
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + toMM overlap, oy )
, VerticalLineTo Relative materialWidth
]
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, strokeDasharray "1 0.5"
]
[]
, case params.shortPiece.loops.style of
Simple ->
g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative (toMM width)
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + length, oy )
, VerticalLineTo Relative (toMM width)
]
]
[]
]
Folded ->
g
[]
[ g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, strokeDasharray "1 0.5"
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + toMM width / 2 )
, HorizontalLineTo Relative length
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy + toMM width * 1.5 )
, HorizontalLineTo Relative length
]
]
[]
]
, g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
]
[ Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox, oy )
, VerticalLineTo Relative (toMM width * 2)
]
]
[]
, Svg.path
[ Svg.Path.d
[ MoveTo Absolute ( ox + length, oy )
, VerticalLineTo Relative (toMM width * 2)
]
]
[]
]
]
]