-
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
-- 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.InfoArea exposing (infoArea)
import Html.Attributes exposing (attribute)
import Length exposing (toMM)
import Parameters exposing (Parameters, Profile(..))
import QRCode
import Svg exposing (..)
import Svg.Attributes as Attrs exposing (..)
import Svg.Path as Path exposing (..)
import Template.Layout.Container as Container exposing (aligned, columns, gapped, noGrow, padded, rows, withOutline)
import Template.Layout.Coordinate exposing (Request(..))
import Template.Layout.Item exposing (Item)
import Url exposing (Url)
infoArea : Url -> Parameters -> Item msg
infoArea url params =
rows
|> withOutline
{ color = "currentColor"
, width = 0.2
, radius = 0.3
}
|> Container.build
[ columns
|> Container.build
([ rows
|> aligned Container.Stretch
|> padded 2
|> gapped 5
|> Container.build
[ columns
|> gapped 5
|> Container.build
[ scaleChcker
, legends params |> noGrow
]
|> noGrow
, columns
|> gapped 10
|> Container.build
[ rows
|> gapped 2
|> Container.build
[ parameters params
, Item { width = Exactly 0, height = AtLeast 0 } (\_ _ -> g [] [])
]
]
, legal
]
|> Just
, if params.rendering.qrCode then
qrCode url
else
Nothing
]
|> List.filterMap identity
)
]
scaleChcker : Item msg
scaleChcker =
{ size = { width = Exactly 10, height = Exactly 5 }
, element =
\p _ ->
let
x : Float -> String
x n =
String.fromFloat (p.x + n)
y : Float -> String
y n =
String.fromFloat (p.y + n)
in
g
[]
[ g
[ stroke "currentColor", strokeWidth "0.2" ]
[ line
[ x1 (x 0), y1 (y 3), x2 (x 0), y2 (y 5) ]
[]
, line
[ x1 (x 0), y1 (y 4), x2 (x 10), y2 (y 4) ]
[]
, line
[ x1 (x 10), y1 (y 3), x2 (x 10), y2 (y 5) ]
[]
]
, text_
[ Attrs.x (x 5)
, Attrs.y (y 0)
, fontSize "3"
, fontWeight "100"
, textAnchor "middle"
, dominantBaseline "hanging"
, fill "currentColor"
]
[ text "10mm" ]
]
}
label : String -> Item msg
label t =
{ size = { width = Exactly 23, height = Exactly 3 }
, element =
\p size ->
text_
[ x (String.fromFloat p.x)
, y (String.fromFloat p.y)
, fontSize (String.fromFloat size.height)
, fontWeight "700"
, textAnchor "left"
, dominantBaseline "hanging"
, fill "currentColor"
]
[ text t, text ":" ]
}
value : String -> Item msg
value t =
{ size = { width = Exactly 12, height = Exactly 3 }
, element =
\p size ->
text_
[ x (String.fromFloat p.x)
, y (String.fromFloat p.y)
, fontSize (String.fromFloat size.height)
, fontWeight "100"
, textAnchor "left"
, dominantBaseline "hanging"
, fill "currentColor"
, attribute "text-overflow" "ellipses"
]
[ text t ]
}
lengthValue : Length.Length -> Item msg
lengthValue l =
value (String.fromFloat (toMM l) ++ "mm")
type alias Parameter msg =
{ label : Item msg
, value : Item msg
}
parameter : Parameter msg -> Item msg
parameter v =
columns
|> gapped 1
|> aligned Container.Center
|> Container.build
[ v.label, v.value ]
parametersRow : List (Item msg) -> Item msg
parametersRow children =
columns
|> gapped 2
|> aligned Container.Center
|> Container.build children
gridify : Int -> List a -> List (List a)
gridify n list =
case List.take n list of
[] ->
[]
xs ->
xs :: gridify n (List.drop n list)
parameters : Parameters -> Item msg
parameters params =
rows
|> gapped 1.2
|> aligned Container.Start
|> Container.build
([ [ parameter
{ label = label "Shoulder width"
, value = lengthValue params.shoulderWidth
}
, parameter
{ label = label "Padding offset"
, value = lengthValue params.paddingOffset
}
, parameter
{ label = label "Bottom Length"
, value = lengthValue params.longPiece.length
}
, parameter
{ label = label "Top Length"
, value = lengthValue params.shortPiece.length
}
]
, case params.profile of
Straight ->
[ parameter
{ label = label "Profile"
, value = value "Straight"
}
]
Tapered to ->
[ parameter
{ label = label "Profile"
, value = value "Tapered"
}
, parameter
{ label = label "Taper to"
, value = lengthValue to
}
]
, case params.longPiece.tip of
Parameters.Round ->
[ parameter
{ label = label "Tip style"
, value = value "Round"
}
]
Parameters.Pointed sharpness ->
[ parameter
{ label = label "Tip style"
, value = value "Pointed"
}
, parameter
{ label = label "Tip sharpness"
, value = value (String.fromInt sharpness)
}
]
, [ parameter
{ label = label "Center hole at"
, value = lengthValue params.longPiece.buckleHole.distance
}
, parameter
{ label = label "Adjustments lv."
, value = value (String.fromInt params.longPiece.buckleHole.adjustments)
}
, parameter
{ label = label "Hole diameter"
, value = lengthValue params.longPiece.buckleHole.diameter
}
, parameter
{ label = label "Hole interval"
, value = lengthValue params.longPiece.buckleHole.interval
}
]
, if params.shortPiece.loops.fixed == Nothing && params.shortPiece.loops.free == Nothing then
[]
else
[ parameter
{ label = label "Loop style"
, value =
value
(case params.shortPiece.loops.style of
Parameters.Simple ->
"Simple"
Parameters.Folded ->
"Folded"
)
}
]
, case params.shortPiece.loops.fixed of
Just { width, length } ->
[ parameter
{ label = label "Fixed loop width"
, value = lengthValue width
}
, parameter
{ label = label "Fixed loop length"
, value = lengthValue length
}
]
Nothing ->
[]
, case params.shortPiece.loops.free of
Just { width, length, overlap } ->
[ parameter
{ label = label "Free loop width"
, value = lengthValue width
}
, parameter
{ label = label "Free loop length"
, value = lengthValue length
}
, parameter
{ label = label "Free loop overlap"
, value = lengthValue overlap
}
]
Nothing ->
[]
]
|> List.concat
|> gridify
(if params.rendering.qrCode then
4
else
5
)
|> List.map parametersRow
)
legal : Item msg
legal =
rows
|> gapped 0.5
|> aligned Container.End
|> Container.build
[ Item
{ width = Exactly 33.2, height = Exactly 2 }
(\p size ->
text_
[ x (String.fromFloat p.x)
, y (String.fromFloat p.y)
, fontSize (String.fromFloat size.height)
, fontWeight "100"
, textAnchor "left"
, dominantBaseline "hanging"
, fill "currentColor"
]
[ text "© Shota FUJI, licensed under CC BY 4.0" ]
)
, Item
{ width = Exactly 61, height = Exactly 2 }
(\p size ->
text_
[ x (String.fromFloat p.x)
, y (String.fromFloat p.y)
, fontSize (String.fromFloat size.height)
, fontWeight "100"
, textAnchor "left"
, dominantBaseline "hanging"
, fill "currentColor"
]
[ text "Barlow font © 2017 The Barlow Project Authors, SIL Open Font License 1.1" ]
)
]
legends : Parameters -> Item msg
legends params =
columns
|> gapped 4
|> aligned Container.Start
|> Container.build
[ columns
|> gapped 2
|> aligned Container.Center
|> Container.build
[ Item
{ width = Exactly 8, height = Exactly 5 }
(\p size ->
Svg.path
[ Path.d
[ MoveTo Absolute ( p.x, p.y + size.height / 2 )
, HorizontalLineTo Relative size.width
]
, stroke "currentColor"
, strokeWidth (params.rendering.lineWidth |> toMM |> String.fromFloat)
]
[]
)
, Item
{ width = Exactly 9, height = Exactly 3 }
(\p size ->
text_
[ x (String.fromFloat p.x)
, y (String.fromFloat p.y)
, fontSize (String.fromFloat size.height)
, fontWeight "100"
, textAnchor "left"
, dominantBaseline "hanging"
, fill "currentColor"
]
[ text "Cut line" ]
)
]
, columns
|> gapped 2
|> aligned Container.Center
|> Container.build
[ Item
{ width = Exactly 5, height = Exactly 5 }
(\p size ->
g
[ fill "none"
, stroke "currentColor"
, strokeWidth "0.1"
]
[ Svg.path
[ Path.d
[ MoveTo Absolute ( p.x + (size.width - size.height) / 2, p.y + size.height / 2 )
, HorizontalLineTo Relative size.height
]
]
[]
, Svg.path
[ Path.d
[ MoveTo Absolute ( p.x + size.width / 2, p.y )
, VerticalLineTo Relative size.height
]
]
[]
]
)
, Item
{ width = Exactly 15, height = Exactly 3 }
(\p size ->
text_
[ x (String.fromFloat p.x)
, y (String.fromFloat p.y)
, fontSize (String.fromFloat size.height)
, fontWeight "100"
, textAnchor "left"
, dominantBaseline "hanging"
, fill "currentColor"
]
[ text "Hole center" ]
)
]
]
qrCode : Url -> Maybe (Item msg)
qrCode url =
QRCode.fromString (Url.toString url)
|> Result.toMaybe
|> Maybe.map
(\pixels ->
Item
{ width = Exactly 50, height = Exactly 50 }
(\p size ->
QRCode.toSvg
[ width (String.fromFloat size.width)
, height (String.fromFloat size.height)
, x (String.fromFloat p.x)
, y (String.fromFloat p.y)
, stroke "currentColor"
]
pixels
)
)