clip_test.go raw
1 package rendertest
2
3 import (
4 "image"
5 "math"
6 "testing"
7
8 "golang.org/x/image/colornames"
9
10 "github.com/p9c/p9/pkg/gel/gio/f32"
11 "github.com/p9c/p9/pkg/gel/gio/op"
12 "github.com/p9c/p9/pkg/gel/gio/op/clip"
13 "github.com/p9c/p9/pkg/gel/gio/op/paint"
14 )
15
16 func TestPaintRect(t *testing.T) {
17 run(t, func(o *op.Ops) {
18 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
19 }, func(r result) {
20 r.expect(0, 0, colornames.Red)
21 r.expect(49, 0, colornames.Red)
22 r.expect(50, 0, transparent)
23 r.expect(10, 50, transparent)
24 })
25 }
26
27 func TestPaintClippedRect(t *testing.T) {
28 run(t, func(o *op.Ops) {
29 clip.RRect{Rect: f32.Rect(25, 25, 60, 60)}.Add(o)
30 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
31 }, func(r result) {
32 r.expect(0, 0, transparent)
33 r.expect(24, 35, transparent)
34 r.expect(25, 35, colornames.Red)
35 r.expect(50, 0, transparent)
36 r.expect(10, 50, transparent)
37 })
38 }
39
40 func TestPaintClippedCircle(t *testing.T) {
41 run(t, func(o *op.Ops) {
42 r := float32(10)
43 clip.RRect{Rect: f32.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Add(o)
44 clip.Rect(image.Rect(0, 0, 30, 50)).Add(o)
45 paint.Fill(o, red)
46 }, func(r result) {
47 r.expect(21, 21, transparent)
48 r.expect(25, 30, colornames.Red)
49 r.expect(31, 30, transparent)
50 })
51 }
52
53 func TestPaintArc(t *testing.T) {
54 run(t, func(o *op.Ops) {
55 p := new(clip.Path)
56 p.Begin(o)
57 p.Move(f32.Pt(0, 20))
58 p.Line(f32.Pt(10, 0))
59 p.Arc(f32.Pt(10, 0), f32.Pt(40, 0), math.Pi)
60 p.Line(f32.Pt(30, 0))
61 p.Line(f32.Pt(0, 25))
62 p.Arc(f32.Pt(-10, 5), f32.Pt(10, 15), -math.Pi)
63 p.Line(f32.Pt(0, 25))
64 p.Arc(f32.Pt(10, 10), f32.Pt(10, 10), 2*math.Pi)
65 p.Line(f32.Pt(-10, 0))
66 p.Arc(f32.Pt(-10, 0), f32.Pt(-40, 0), -math.Pi)
67 p.Line(f32.Pt(-10, 0))
68 p.Line(f32.Pt(0, -10))
69 p.Arc(f32.Pt(-10, -20), f32.Pt(10, -5), math.Pi)
70 p.Line(f32.Pt(0, -10))
71 p.Line(f32.Pt(-50, 0))
72 p.Close()
73 clip.Outline{
74 Path: p.End(),
75 }.Op().Add(o)
76
77 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op())
78 }, func(r result) {
79 r.expect(0, 0, transparent)
80 r.expect(0, 25, colornames.Red)
81 r.expect(0, 15, transparent)
82 })
83 }
84
85 func TestPaintAbsolute(t *testing.T) {
86 run(t, func(o *op.Ops) {
87 p := new(clip.Path)
88 p.Begin(o)
89 p.Move(f32.Pt(100, 100)) // offset the initial pen position to test "MoveTo"
90
91 p.MoveTo(f32.Pt(20, 20))
92 p.LineTo(f32.Pt(80, 20))
93 p.QuadTo(f32.Pt(80, 80), f32.Pt(20, 80))
94 p.Close()
95 clip.Outline{
96 Path: p.End(),
97 }.Op().Add(o)
98
99 paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op())
100 }, func(r result) {
101 r.expect(0, 0, transparent)
102 r.expect(30, 30, colornames.Red)
103 r.expect(79, 79, transparent)
104 r.expect(90, 90, transparent)
105 })
106 }
107
108 func TestPaintTexture(t *testing.T) {
109 run(t, func(o *op.Ops) {
110 squares.Add(o)
111 scale(80.0/512, 80.0/512).Add(o)
112 paint.PaintOp{}.Add(o)
113 }, func(r result) {
114 r.expect(0, 0, colornames.Blue)
115 r.expect(79, 10, colornames.Green)
116 r.expect(80, 0, transparent)
117 r.expect(10, 80, transparent)
118 })
119 }
120
121 func TestTexturedStrokeClipped(t *testing.T) {
122 run(t, func(o *op.Ops) {
123 smallSquares.Add(o)
124 op.Offset(f32.Pt(50, 50)).Add(o)
125 clip.Stroke{
126 Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o),
127 Style: clip.StrokeStyle{
128 Width: 10,
129 },
130 }.Op().Add(o)
131 clip.RRect{Rect: f32.Rect(-30, -30, 60, 60)}.Add(o)
132 op.Offset(f32.Pt(-10, -10)).Add(o)
133 paint.PaintOp{}.Add(o)
134 }, func(r result) {
135 })
136 }
137
138 func TestTexturedStroke(t *testing.T) {
139 run(t, func(o *op.Ops) {
140 smallSquares.Add(o)
141 op.Offset(f32.Pt(50, 50)).Add(o)
142 clip.Stroke{
143 Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o),
144 Style: clip.StrokeStyle{
145 Width: 10,
146 },
147 }.Op().Add(o)
148 op.Offset(f32.Pt(-10, -10)).Add(o)
149 paint.PaintOp{}.Add(o)
150 }, func(r result) {
151 })
152 }
153
154 func TestPaintClippedTexture(t *testing.T) {
155 run(t, func(o *op.Ops) {
156 squares.Add(o)
157 clip.RRect{Rect: f32.Rect(0, 0, 40, 40)}.Add(o)
158 scale(80.0/512, 80.0/512).Add(o)
159 paint.PaintOp{}.Add(o)
160 }, func(r result) {
161 r.expect(40, 40, transparent)
162 r.expect(25, 35, colornames.Blue)
163 })
164 }
165
166 func TestStrokedPathBevelFlat(t *testing.T) {
167 run(t, func(o *op.Ops) {
168 p := newStrokedPath(o)
169 clip.Stroke{
170 Path: p,
171 Style: clip.StrokeStyle{
172 Width: 2.5,
173 Cap: clip.FlatCap,
174 Join: clip.BevelJoin,
175 },
176 }.Op().Add(o)
177
178 paint.Fill(o, red)
179 }, func(r result) {
180 r.expect(0, 0, transparent)
181 r.expect(10, 50, colornames.Red)
182 })
183 }
184
185 func TestStrokedPathBevelRound(t *testing.T) {
186 run(t, func(o *op.Ops) {
187 p := newStrokedPath(o)
188 clip.Stroke{
189 Path: p,
190 Style: clip.StrokeStyle{
191 Width: 2.5,
192 Cap: clip.RoundCap,
193 Join: clip.BevelJoin,
194 },
195 }.Op().Add(o)
196
197 paint.Fill(o, red)
198 }, func(r result) {
199 r.expect(0, 0, transparent)
200 r.expect(10, 50, colornames.Red)
201 })
202 }
203
204 func TestStrokedPathBevelSquare(t *testing.T) {
205 run(t, func(o *op.Ops) {
206 p := newStrokedPath(o)
207 clip.Stroke{
208 Path: p,
209 Style: clip.StrokeStyle{
210 Width: 2.5,
211 Cap: clip.SquareCap,
212 Join: clip.BevelJoin,
213 },
214 }.Op().Add(o)
215
216 paint.Fill(o, red)
217 }, func(r result) {
218 r.expect(0, 0, transparent)
219 r.expect(10, 50, colornames.Red)
220 })
221 }
222
223 func TestStrokedPathRoundRound(t *testing.T) {
224 run(t, func(o *op.Ops) {
225 p := newStrokedPath(o)
226 clip.Stroke{
227 Path: p,
228 Style: clip.StrokeStyle{
229 Width: 2.5,
230 Cap: clip.RoundCap,
231 Join: clip.RoundJoin,
232 },
233 }.Op().Add(o)
234
235 paint.Fill(o, red)
236 }, func(r result) {
237 r.expect(0, 0, transparent)
238 r.expect(10, 50, colornames.Red)
239 })
240 }
241
242 func TestStrokedPathFlatMiter(t *testing.T) {
243 run(t, func(o *op.Ops) {
244 {
245 stk := op.Save(o)
246 p := newZigZagPath(o)
247 clip.Stroke{
248 Path: p,
249 Style: clip.StrokeStyle{
250 Width: 10,
251 Cap: clip.FlatCap,
252 Join: clip.BevelJoin,
253 Miter: 5,
254 },
255 }.Op().Add(o)
256 paint.Fill(o, red)
257 stk.Load()
258 }
259 {
260 stk := op.Save(o)
261 p := newZigZagPath(o)
262 clip.Stroke{
263 Path: p,
264 Style: clip.StrokeStyle{
265 Width: 2,
266 Cap: clip.FlatCap,
267 Join: clip.BevelJoin,
268 },
269 }.Op().Add(o)
270 paint.Fill(o, black)
271 stk.Load()
272 }
273
274 }, func(r result) {
275 r.expect(0, 0, transparent)
276 r.expect(40, 10, colornames.Black)
277 r.expect(40, 12, colornames.Red)
278 })
279 }
280
281 func TestStrokedPathFlatMiterInf(t *testing.T) {
282 run(t, func(o *op.Ops) {
283 {
284 stk := op.Save(o)
285 p := newZigZagPath(o)
286 clip.Stroke{
287 Path: p,
288 Style: clip.StrokeStyle{
289 Width: 10,
290 Cap: clip.FlatCap,
291 Join: clip.BevelJoin,
292 Miter: float32(math.Inf(+1)),
293 },
294 }.Op().Add(o)
295 paint.Fill(o, red)
296 stk.Load()
297 }
298 {
299 stk := op.Save(o)
300 p := newZigZagPath(o)
301 clip.Stroke{
302 Path: p,
303 Style: clip.StrokeStyle{
304 Width: 2,
305 Cap: clip.FlatCap,
306 Join: clip.BevelJoin,
307 },
308 }.Op().Add(o)
309 paint.Fill(o, black)
310 stk.Load()
311 }
312
313 }, func(r result) {
314 r.expect(0, 0, transparent)
315 r.expect(40, 10, colornames.Black)
316 r.expect(40, 12, colornames.Red)
317 })
318 }
319
320 func TestStrokedPathZeroWidth(t *testing.T) {
321 run(t, func(o *op.Ops) {
322 {
323 stk := op.Save(o)
324 p := new(clip.Path)
325 p.Begin(o)
326 p.Move(f32.Pt(10, 50))
327 p.Line(f32.Pt(50, 0))
328 clip.Stroke{
329 Path: p.End(),
330 Style: clip.StrokeStyle{
331 Width: 2,
332 Cap: clip.FlatCap,
333 Join: clip.BevelJoin,
334 },
335 }.Op().Add(o)
336
337 paint.Fill(o, black)
338 stk.Load()
339 }
340
341 {
342 stk := op.Save(o)
343 p := new(clip.Path)
344 p.Begin(o)
345 p.Move(f32.Pt(10, 50))
346 p.Line(f32.Pt(30, 0))
347 clip.Stroke{
348 Path: p.End(),
349 }.Op().Add(o) // width=0, disable stroke
350
351 paint.Fill(o, red)
352 stk.Load()
353 }
354
355 }, func(r result) {
356 r.expect(0, 0, transparent)
357 r.expect(10, 50, colornames.Black)
358 r.expect(30, 50, colornames.Black)
359 r.expect(65, 50, transparent)
360 })
361 }
362
363 func TestDashedPathFlatCapEllipse(t *testing.T) {
364 run(t, func(o *op.Ops) {
365 {
366 stk := op.Save(o)
367 p := newEllipsePath(o)
368
369 var dash clip.Dash
370 dash.Begin(o)
371 dash.Dash(5)
372 dash.Dash(3)
373
374 clip.Stroke{
375 Path: p,
376 Style: clip.StrokeStyle{
377 Width: 10,
378 Cap: clip.FlatCap,
379 Join: clip.BevelJoin,
380 Miter: float32(math.Inf(+1)),
381 },
382 Dashes: dash.End(),
383 }.Op().Add(o)
384
385 paint.Fill(
386 o,
387 red,
388 )
389 stk.Load()
390 }
391 {
392 stk := op.Save(o)
393 p := newEllipsePath(o)
394 clip.Stroke{
395 Path: p,
396 Style: clip.StrokeStyle{
397 Width: 2,
398 },
399 }.Op().Add(o)
400
401 paint.Fill(
402 o,
403 black,
404 )
405 stk.Load()
406 }
407
408 }, func(r result) {
409 r.expect(0, 0, transparent)
410 r.expect(0, 62, colornames.Red)
411 r.expect(0, 65, colornames.Black)
412 })
413 }
414
415 func TestDashedPathFlatCapZ(t *testing.T) {
416 run(t, func(o *op.Ops) {
417 {
418 stk := op.Save(o)
419 p := newZigZagPath(o)
420 var dash clip.Dash
421 dash.Begin(o)
422 dash.Dash(5)
423 dash.Dash(3)
424
425 clip.Stroke{
426 Path: p,
427 Style: clip.StrokeStyle{
428 Width: 10,
429 Cap: clip.FlatCap,
430 Join: clip.BevelJoin,
431 Miter: float32(math.Inf(+1)),
432 },
433 Dashes: dash.End(),
434 }.Op().Add(o)
435 paint.Fill(o, red)
436 stk.Load()
437 }
438
439 {
440 stk := op.Save(o)
441 p := newZigZagPath(o)
442 clip.Stroke{
443 Path: p,
444 Style: clip.StrokeStyle{
445 Width: 2,
446 Cap: clip.FlatCap,
447 Join: clip.BevelJoin,
448 },
449 }.Op().Add(o)
450 paint.Fill(o, black)
451 stk.Load()
452 }
453 }, func(r result) {
454 r.expect(0, 0, transparent)
455 r.expect(40, 10, colornames.Black)
456 r.expect(40, 12, colornames.Red)
457 r.expect(46, 12, transparent)
458 })
459 }
460
461 func TestDashedPathFlatCapZNoDash(t *testing.T) {
462 run(t, func(o *op.Ops) {
463 {
464 stk := op.Save(o)
465 p := newZigZagPath(o)
466 var dash clip.Dash
467 dash.Begin(o)
468 dash.Phase(1)
469
470 clip.Stroke{
471 Path: p,
472 Style: clip.StrokeStyle{
473 Width: 10,
474 Cap: clip.FlatCap,
475 Join: clip.BevelJoin,
476 Miter: float32(math.Inf(+1)),
477 },
478 Dashes: dash.End(),
479 }.Op().Add(o)
480 paint.Fill(o, red)
481 stk.Load()
482 }
483 {
484 stk := op.Save(o)
485 clip.Stroke{
486 Path: newZigZagPath(o),
487 Style: clip.StrokeStyle{
488 Width: 2,
489 Cap: clip.FlatCap,
490 Join: clip.BevelJoin,
491 },
492 }.Op().Add(o)
493 paint.Fill(o, black)
494 stk.Load()
495 }
496 }, func(r result) {
497 r.expect(0, 0, transparent)
498 r.expect(40, 10, colornames.Black)
499 r.expect(40, 12, colornames.Red)
500 r.expect(46, 12, colornames.Red)
501 })
502 }
503
504 func TestDashedPathFlatCapZNoPath(t *testing.T) {
505 run(t, func(o *op.Ops) {
506 {
507 stk := op.Save(o)
508 var dash clip.Dash
509 dash.Begin(o)
510 dash.Dash(0)
511 clip.Stroke{
512 Path: newZigZagPath(o),
513 Style: clip.StrokeStyle{
514 Width: 10,
515 Cap: clip.FlatCap,
516 Join: clip.BevelJoin,
517 Miter: float32(math.Inf(+1)),
518 },
519 Dashes: dash.End(),
520 }.Op().Add(o)
521 paint.Fill(o, red)
522 stk.Load()
523 }
524 {
525 stk := op.Save(o)
526 p := newZigZagPath(o)
527 clip.Stroke{
528 Path: p,
529 Style: clip.StrokeStyle{
530 Width: 2,
531 Cap: clip.FlatCap,
532 Join: clip.BevelJoin,
533 },
534 }.Op().Add(o)
535 paint.Fill(o, black)
536 stk.Load()
537 }
538 }, func(r result) {
539 r.expect(0, 0, transparent)
540 r.expect(40, 10, colornames.Black)
541 r.expect(40, 12, transparent)
542 r.expect(46, 12, transparent)
543 })
544 }
545
546 func newStrokedPath(o *op.Ops) clip.PathSpec {
547 p := new(clip.Path)
548 p.Begin(o)
549 p.Move(f32.Pt(10, 50))
550 p.Line(f32.Pt(10, 0))
551 p.Arc(f32.Pt(10, 0), f32.Pt(20, 0), math.Pi)
552 p.Line(f32.Pt(10, 0))
553 p.Line(f32.Pt(10, 10))
554 p.Arc(f32.Pt(0, 30), f32.Pt(0, 30), 2*math.Pi)
555 p.Line(f32.Pt(-20, 0))
556 p.Quad(f32.Pt(-10, -10), f32.Pt(-30, 30))
557 return p.End()
558 }
559
560 func newZigZagPath(o *op.Ops) clip.PathSpec {
561 p := new(clip.Path)
562 p.Begin(o)
563 p.Move(f32.Pt(40, 10))
564 p.Line(f32.Pt(50, 0))
565 p.Line(f32.Pt(-50, 50))
566 p.Line(f32.Pt(50, 0))
567 p.Quad(f32.Pt(-50, 20), f32.Pt(-50, 50))
568 p.Line(f32.Pt(50, 0))
569 return p.End()
570 }
571
572 func newEllipsePath(o *op.Ops) clip.PathSpec {
573 p := new(clip.Path)
574 p.Begin(o)
575 p.Move(f32.Pt(0, 65))
576 p.Line(f32.Pt(20, 0))
577 p.Arc(f32.Pt(20, 0), f32.Pt(70, 0), 2*math.Pi)
578 return p.End()
579 }
580