-
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
-- Copyright 2026 Shota FUJI
--
-- This source code and the design it expresses are licensed under
-- the Creative Commons Attribution 4.0 International License.
--
-- SPDX-License-Identifier: CC-BY-4.0
module Models.PWS01.Template.Cuts exposing (cuts)
import Html.Attributes exposing (attribute)
import Length exposing (Length, toMM)
import Models.PWS01.Parameters exposing (LoopStyle(..), Parameters, Profile(..), TipStyle(..))
import Models.PWS01.Parameters.Key exposing (Key(..))
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Svg.Path as Path exposing (..)
import Template.Layout.Container as Container exposing (columns, rows)
import Template.Layout.Coordinate exposing (Request(..), SizeRequest, minSizeOf)
import Template.Layout.Item exposing (Item)
import Template.Layout.Packer exposing (pack)
highlightStroke : Bool -> Svg.Attribute msg
highlightStroke enabled =
if enabled then
class "highlight-stroke"
else
class "highlight-none"
cuts : SizeRequest -> Parameters -> Maybe Key -> List (Item msg)
cuts container params highlighting =
let
empty =
{ size = { width = Exactly 0, height = Exactly 0 }, element = \_ _ -> g [] [] }
in
pack
container
(toMM params.rendering.gap)
[ if toMM params.caseSideFlap > 0 then
tailPiece
{ offset = 0
, highlighting = highlighting
, parameters = params
, renderFlap = True
, renderHoles = True
}
|> labelled "Tail Top (A1)"
else
empty
, if toMM params.caseSideFlap > 0 || toMM params.shortPiece.buckleSideFlap > 0 then
headPiece
{ offset = 0
, parameters = params
, highlighting = highlighting
, renderFlaps = True
}
|> labelled "Head Top (A2)"
else
empty
-- Linings
, tailPiece
{ offset = 0
, highlighting = highlighting
, parameters = params
, renderFlap = False
, renderHoles = True
}
|> labelled "Tail Lining (B1)"
, headPiece
{ offset = 0
, parameters = params
, highlighting = highlighting
, renderFlaps = False
}
|> labelled "Head Lining (B2)"
-- Paddings
, if toMM params.paddingOffset > 0 then
tailPiece
{ offset = toMM params.paddingOffset
, parameters = params
, highlighting = highlighting
, renderFlap = False
, renderHoles = False
}
|> labelled "Tail Padding (C1)"
else
empty
, if toMM params.paddingOffset > 0 then
headPiece
{ offset = toMM params.paddingOffset
, parameters = params
, highlighting = highlighting
, renderFlaps = False
}
|> labelled "Head Padding (C2)"
else
empty
, fixedLoop params highlighting |> Maybe.map (labelled "Fixed Loop (D3)") |> Maybe.withDefault empty
, freeLoop params highlighting |> Maybe.map (labelled "Free Loop (D4)") |> Maybe.withDefault empty
]
labelled : String -> Item msg -> Item msg
labelled text item =
let
itemSize =
minSizeOf item.size
rotated =
itemSize.height > itemSize.width
label =
let
width =
Exactly (Basics.max itemSize.width itemSize.height)
height =
Exactly 3
in
Item
(if rotated then
{ width = height, height = width }
else
{ width = width, height = height }
)
(\p size ->
Svg.text_
(x (String.fromFloat p.x)
:: y (String.fromFloat p.y)
:: fontWeight "100"
:: textAnchor "left"
:: dominantBaseline "hanging"
:: fill "currentColor"
:: (if rotated then
[ fontSize (String.fromFloat size.width)
, transform
([ "rotate(90 " ++ String.fromFloat p.x ++ " " ++ String.fromFloat (p.y + size.width) ++ ")"
, "translate(" ++ String.fromFloat -size.width ++ " 0)"
]
|> String.join " "
)
]
else
[ fontSize (String.fromFloat size.height) ]
)
)
[ Svg.text text ]
)
in
if rotated then
columns |> Container.gapped 2 |> Container.build [ item, label ]
else
rows |> Container.gapped 2 |> Container.build [ label, item ]
guideStroke : List (Svg.Attribute msg)
guideStroke =
[ fill "none"
, stroke "currentColor"
, strokeWidth "0.1"
, strokeDasharray "0.5 1"
]
type alias TailPieceProps =
{ offset : Float
, highlighting : Maybe Key
, parameters : Parameters
, renderFlap : Bool
, renderHoles : Bool
}
tailPieceGraphicId : TailPieceProps -> String
tailPieceGraphicId { offset, renderFlap, renderHoles } =
[ "tail"
, offset * 100 |> ceiling |> String.fromInt
, if renderFlap then
"flap"
else
"noflap"
, if renderHoles then
"holes"
else
"noholes"
]
|> String.join "__"
tailPiece : TailPieceProps -> Item msg
tailPiece props =
let
{ offset, highlighting, parameters, renderFlap, renderHoles } =
props
shoulderWidth =
toMM parameters.shoulderWidth
length =
toMM parameters.longPiece.length
taperUntil =
Basics.min
(toMM parameters.longPiece.buckleHole.distance
- (toMM parameters.longPiece.buckleHole.interval * toFloat (parameters.longPiece.buckleHole.adjustments + 1))
)
(length / 2)
taper =
case parameters.profile of
Straight ->
0.0
Tapered to ->
shoulderWidth - toMM to
buckleWidth =
shoulderWidth - taper
curvedSectionLength =
case parameters.longPiece.tip of
Round ->
buckleWidth / 2
Pointed _ ->
buckleWidth
flap =
if renderFlap then
toMM parameters.caseSideFlap
else
0.0
localId : String -> String
localId body =
tailPieceGraphicId props ++ "__" ++ body
in
{ size =
{ width = Exactly (shoulderWidth - offset * 2)
, height = Exactly (length - offset + flap)
}
, element =
\{ x, y } _ ->
Svg.g
[]
[ defs
[]
[ Svg.path
[ id (localId "body")
, Path.d
(MoveTo Absolute ( x - offset, y - offset )
:: VerticalLineTo Relative flap
:: VerticalLineTo Relative offset
:: LineTo Relative ( taper / 2, taperUntil )
:: VerticalLineTo Relative (length - curvedSectionLength - taperUntil)
:: (case parameters.longPiece.tip of
Round ->
[ EllipticalArcCurve Relative
{ rx = buckleWidth / 2
, ry = buckleWidth / 2
, angle = 0.0
, largeArcFlag = LargeArc
, sweepFlag = Counterclockwise
, x = buckleWidth
, y = 0
}
[]
]
Pointed sharpness ->
let
sy =
buckleWidth * (toFloat (100 - sharpness) / 100)
in
[ CubicBezierCurve
Relative
( ( 0, sy )
, ( buckleWidth / 2, buckleWidth )
, ( buckleWidth / 2, buckleWidth )
)
[]
, CubicBezierCurve
Relative
( ( 0, 0 )
, ( buckleWidth / 2, -(buckleWidth - sy) )
, ( buckleWidth / 2, -buckleWidth )
)
[]
]
)
++ [ VerticalLineTo Relative -(length - curvedSectionLength - taperUntil)
, LineTo Relative ( taper / 2, -taperUntil )
, VerticalLineTo Relative -offset
, VerticalLineTo Relative -flap
, ClosePath
]
)
]
[]
]
, if offset > 0 then
g
[]
[ Svg.mask [ id (localId "mask"), attribute "mask-type" "luminance" ]
[ use
[ attribute "href" ("#" ++ localId "body")
, stroke "black"
, strokeWidth (offset * 2 |> String.fromFloat)
, fill "white"
]
[]
]
, use
[ attribute "href" ("#" ++ localId "body")
, Svg.Attributes.mask ("url(#" ++ localId "mask" ++ ")")
, fill "none"
, stroke "currentColor"
, strokeWidth (offset * 2 + toMM parameters.rendering.lineWidth * 2 |> String.fromFloat)
]
[]
]
else
use
[ attribute "href" ("#" ++ localId "body")
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM parameters.rendering.lineWidth |> String.fromFloat)
, highlightStroke
(highlighting
== Just LongPieceLength
|| highlighting
== Just ShoulderWidth
|| highlighting
== Just Profile
|| highlighting
== Just TaperTo
|| (case parameters.profile of
Straight ->
False
Tapered _ ->
highlighting
== Just BuckleHoleDistance
|| highlighting
== Just BuckleHoleInterval
)
)
]
[]
, Svg.path
(Path.d
[ MoveTo Absolute ( x + shoulderWidth / 2 - offset, y )
, VerticalLineTo Relative (length - offset + flap)
]
:: highlightStroke (highlighting == Just LongPieceLength)
:: guideStroke
)
[]
, if renderFlap then
Svg.path
(Path.d
[ MoveTo Absolute ( x + offset, y + flap )
, HorizontalLineTo Relative shoulderWidth
]
:: guideStroke
)
[]
else
g [] []
, case parameters.longPiece.tip of
Round ->
g
[]
[]
Pointed _ ->
Svg.path
(Path.d
[ MoveTo Absolute ( x + taper / 2, y + (length - curvedSectionLength) + flap )
, HorizontalLineTo Relative (buckleWidth - offset * 2)
]
:: guideStroke
)
[]
, case parameters.profile of
Straight ->
g [] []
Tapered _ ->
Svg.path
(Path.d
[ MoveTo Absolute ( x + taper / 2, y + taperUntil + flap )
, HorizontalLineTo Relative (buckleWidth - offset * 2)
]
:: highlightStroke
(highlighting
== Just ShoulderWidth
|| highlighting
== Just TaperTo
|| highlighting
== Just BuckleHoleDistance
|| highlighting
== Just BuckleHoleAdjustments
|| highlighting
== Just BuckleHoleInterval
|| highlighting
== Just BuckleHoleDiameter
)
:: guideStroke
)
[]
, if renderHoles then
g
[]
(List.range 0 (parameters.longPiece.buckleHole.adjustments * 2)
|> List.map
(\index ->
let
{ buckleHole } =
parameters.longPiece
distance =
toMM buckleHole.distance
interval =
toMM buckleHole.interval
in
hole
parameters
buckleHole.diameter
( x + shoulderWidth / 2
, y + flap + distance + interval * toFloat (index - parameters.longPiece.buckleHole.adjustments)
)
[ highlightStroke
(highlighting
== Just BuckleHoleDiameter
|| highlighting
== Just BuckleHoleDistance
|| highlighting
== Just BuckleHoleInterval
|| highlighting
== Just BuckleHoleAdjustments
)
]
)
)
else
g [] []
]
}
hole : Parameters -> Length -> ( Float, Float ) -> List (Svg.Attribute msg) -> Svg msg
hole params diameter ( cx, cy ) attrs =
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)
:: attrs
)
[]
, g
[ fill "none"
, stroke "currentColor"
, strokeWidth "0.1"
]
[ Svg.path
[ Path.d
[ MoveTo Absolute ( cx - radius / 2, cy )
, HorizontalLineTo Relative radius
]
]
[]
, Svg.path
[ Path.d
[ MoveTo Absolute ( cx, cy - radius / 2 )
, VerticalLineTo Relative radius
]
]
[]
]
]
type alias HeadPieceProps =
{ offset : Float
, highlighting : Maybe Key
, parameters : Parameters
, renderFlaps : Bool
}
headPieceGraphicId : HeadPieceProps -> String
headPieceGraphicId { offset, renderFlaps } =
[ "head"
, offset * 100 |> ceiling |> String.fromInt
, if renderFlaps then
"flap"
else
"noflap"
]
|> String.join "__"
headPiece : HeadPieceProps -> Item msg
headPiece props =
let
{ offset, highlighting, parameters, renderFlaps } =
props
shoulderWidth =
toMM parameters.shoulderWidth
length =
toMM parameters.shortPiece.length
taperUntil =
Basics.min
(toMM parameters.longPiece.buckleHole.distance
- (toMM parameters.longPiece.buckleHole.interval * toFloat (parameters.longPiece.buckleHole.adjustments + 1))
)
(length - 10)
taper =
case parameters.profile of
Straight ->
0.0
Tapered to ->
shoulderWidth - toMM to
caseFlap =
if renderFlaps then
toMM parameters.caseSideFlap
else
0.0
buckleFlap =
if renderFlaps then
toMM parameters.shortPiece.buckleSideFlap
else
0.0
localId : String -> String
localId body =
headPieceGraphicId props ++ "__" ++ body
in
{ size =
{ width = Exactly (shoulderWidth - offset * 2)
, height = Exactly (length + caseFlap + buckleFlap)
}
, element =
\{ x, y } _ ->
g
[]
[ defs []
[ Svg.path
[ id (localId "body")
, Path.d
[ MoveTo Absolute ( x - offset, y - offset )
, VerticalLineTo Relative caseFlap
, VerticalLineTo Relative offset
, LineTo Relative ( taper / 2, taperUntil )
, VerticalLineTo Relative (length - taperUntil)
, VerticalLineTo Relative offset
, VerticalLineTo Relative buckleFlap
, HorizontalLineTo Relative (shoulderWidth - taper)
, VerticalLineTo Relative -buckleFlap
, VerticalLineTo Relative -offset
, VerticalLineTo Relative -(length - taperUntil)
, LineTo Relative ( taper / 2, -taperUntil )
, VerticalLineTo Relative -offset
, VerticalLineTo Relative -caseFlap
, ClosePath
]
]
[]
]
, if offset > 0 then
g
[]
[ Svg.mask [ id (localId "mask"), attribute "mask-type" "luminance" ]
[ use
[ attribute "href" ("#" ++ localId "body")
, stroke "black"
, strokeWidth (offset * 2 |> String.fromFloat)
, fill "white"
]
[]
]
, use
[ attribute "href" ("#" ++ localId "body")
, Svg.Attributes.mask ("url(#" ++ localId "mask" ++ ")")
, fill "none"
, stroke "currentColor"
, strokeWidth (offset * 2 + toMM parameters.rendering.lineWidth * 2 |> String.fromFloat)
]
[]
]
else
use
[ attribute "href" ("#" ++ localId "body")
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM parameters.rendering.lineWidth |> String.fromFloat)
, highlightStroke
(highlighting
== Just ShortPieceLength
|| highlighting
== Just ShoulderWidth
|| highlighting
== Just Profile
|| highlighting
== Just TaperTo
|| (case parameters.profile of
Straight ->
False
Tapered _ ->
highlighting
== Just BuckleHoleDistance
|| highlighting
== Just BuckleHoleInterval
)
)
]
[]
, Svg.path
(Path.d
[ MoveTo Absolute ( x + shoulderWidth / 2 - offset, y )
, VerticalLineTo Relative (length + caseFlap + buckleFlap)
]
:: highlightStroke (highlighting == Just ShortPieceLength)
:: guideStroke
)
[]
, if renderFlaps then
g
[]
[ Svg.path
(Path.d
[ MoveTo Absolute ( x, y + caseFlap )
, HorizontalLineTo Relative shoulderWidth
]
:: guideStroke
)
[]
, Svg.path
(Path.d
[ MoveTo Absolute ( x + taper / 2, y + length + caseFlap )
, HorizontalLineTo Relative (shoulderWidth - taper - offset * 2)
]
:: guideStroke
)
[]
]
else
g [] []
, case parameters.profile of
Straight ->
g [] []
Tapered _ ->
Svg.path
(Path.d
[ MoveTo Absolute ( x + taper / 2, y + caseFlap + taperUntil )
, HorizontalLineTo Relative (shoulderWidth - taper - offset * 2)
]
:: guideStroke
)
[]
]
}
fixedLoop : Parameters -> Maybe Key -> Maybe (Item msg)
fixedLoop params highlighting =
Maybe.map
(\{ width, length } ->
{ size =
{ width = Exactly (toMM length)
, height =
Exactly
(case params.shortPiece.loops.style of
Simple ->
toMM width
Folded ->
toMM width * 2
)
}
, element =
\p size ->
g
[]
[ Svg.path
[ Path.d
[ MoveTo Absolute ( p.x, p.y )
, HorizontalLineTo Relative size.width
, VerticalLineTo Relative size.height
, HorizontalLineTo Relative -size.width
, ClosePath
]
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, highlightStroke (highlighting == Just FixedLoopWidth || highlighting == Just FixedLoopLength)
]
[]
, case params.shortPiece.loops.style of
Simple ->
g [] []
Folded ->
g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, strokeDasharray "1 0.5"
]
[ Svg.path
[ Path.d
[ MoveTo Absolute ( p.x, p.y + toMM width / 2 )
, HorizontalLineTo Relative size.width
]
]
[]
, Svg.path
[ Path.d
[ MoveTo Absolute ( p.x, p.y + toMM width * 1.5 )
, HorizontalLineTo Relative size.width
]
]
[]
]
]
}
)
params.shortPiece.loops.fixed
freeLoop : Parameters -> Maybe Key -> Maybe (Item msg)
freeLoop params highlighting =
Maybe.map
(\{ width, length, overlap } ->
{ size =
{ width = Exactly (toMM length + toMM overlap)
, height =
Exactly
(case params.shortPiece.loops.style of
Simple ->
toMM width
Folded ->
toMM width * 2
)
}
, element =
\p size ->
g
[]
[ Svg.path
[ Path.d
[ MoveTo Absolute ( p.x, p.y )
, HorizontalLineTo Relative size.width
, VerticalLineTo Relative size.height
, HorizontalLineTo Relative -size.width
, ClosePath
]
, fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, highlightStroke
(highlighting
== Just FreeLoopWidth
|| highlighting
== Just FreeLoopLength
|| highlighting
== Just FreeLoopOverlap
)
]
[]
, g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, strokeDasharray "1 0.5"
]
[ Svg.path
[ Path.d
[ MoveTo Absolute ( p.x + toMM overlap, p.y )
, VerticalLineTo Relative size.height
]
]
[]
, Svg.path
[ Path.d
[ MoveTo Absolute ( p.x + size.width - toMM overlap, p.y )
, VerticalLineTo Relative size.height
]
]
[]
]
, case params.shortPiece.loops.style of
Simple ->
g [] []
Folded ->
g
[ fill "none"
, stroke "currentColor"
, strokeWidth (toMM params.rendering.lineWidth |> String.fromFloat)
, strokeDasharray "1 0.5"
]
[ Svg.path
[ Path.d
[ MoveTo Absolute ( p.x, p.y + toMM width / 2 )
, HorizontalLineTo Relative size.width
]
]
[]
, Svg.path
[ Path.d
[ MoveTo Absolute ( p.x, p.y + toMM width * 1.5 )
, HorizontalLineTo Relative size.width
]
]
[]
]
]
}
)
params.shortPiece.loops.free