-
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
-- 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 Models.PWS01.Template.InfoArea exposing (infoArea)
import Html.Attributes exposing (attribute)
import Length exposing (toMM)
import Models.PWS01.Parameters exposing (..)
import Models.PWS01.Parameters.Key as Key exposing (Key(..))
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 2
|> Container.build
[ columns
|> gapped 5
|> Container.build
[ Item
{ width = AtLeast 20, height = Exactly 5 }
(\p size ->
text_
[ x (String.fromFloat p.x)
, y (String.fromFloat (p.y + size.height / 2))
, fontSize "4"
, fontWeight "700"
, textAnchor "left"
, dominantBaseline "middle"
, fill "currentColor"
]
[ text "CLT-PWS01" ]
)
, 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 8 }
, 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 =
{ key : Key
, value : Item msg
}
parameter : Parameter msg -> Item msg
parameter v =
columns
|> gapped 1
|> aligned Container.Center
|> Container.build
[ label (Key.toLabel v.key), 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
{ key = ShoulderWidth
, value = lengthValue params.shoulderWidth
}
, parameter
{ key = PaddingOffset
, value = lengthValue params.paddingOffset
}
, parameter
{ key = LongPieceLength
, value = lengthValue params.longPiece.length
}
, parameter
{ key = ShortPieceLength
, value = lengthValue params.shortPiece.length
}
]
, case params.profile of
Straight ->
[ parameter
{ key = Profile
, value = value "Straight"
}
]
Tapered to ->
[ parameter
{ key = Profile
, value = value "Tapered"
}
, parameter
{ key = TaperTo
, value = lengthValue to
}
]
, case params.longPiece.tip of
Round ->
[ parameter
{ key = TipStyle
, value = value "Round"
}
]
Pointed sharpness ->
[ parameter
{ key = TipStyle
, value = value "Pointed"
}
, parameter
{ key = TipSharpness
, value = value (String.fromInt sharpness)
}
]
, [ parameter
{ key = BuckleHoleDistance
, value = lengthValue params.longPiece.buckleHole.distance
}
, parameter
{ key = BuckleHoleAdjustments
, value = value (String.fromInt params.longPiece.buckleHole.adjustments)
}
, parameter
{ key = BuckleHoleDiameter
, value = lengthValue params.longPiece.buckleHole.diameter
}
, parameter
{ key = BuckleHoleInterval
, value = lengthValue params.longPiece.buckleHole.interval
}
]
, if params.shortPiece.loops.fixed == Nothing && params.shortPiece.loops.free == Nothing then
[]
else
[ parameter
{ key = LoopStyle
, value =
value
(case params.shortPiece.loops.style of
Simple ->
"Simple"
Folded ->
"Folded"
)
}
]
, case params.shortPiece.loops.fixed of
Just { width, length } ->
[ parameter
{ key = FixedLoopWidth
, value = lengthValue width
}
, parameter
{ key = FixedLoopLength
, value = lengthValue length
}
]
Nothing ->
[]
, case params.shortPiece.loops.free of
Just { width, length, overlap } ->
[ parameter
{ key = FreeLoopWidth
, value = lengthValue width
}
, parameter
{ key = FreeLoopLength
, value = lengthValue length
}
, parameter
{ key = FreeLoopOverlap
, 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
)
)