1+ package org.openrndr.extra.textoncontour
2+
3+ import org.openrndr.color.ColorRGBa
4+ import org.openrndr.draw.Drawer
5+ import org.openrndr.draw.font.internal.ShapeResult
6+ import org.openrndr.draw.font.internal.TextShapingDriver
7+ import org.openrndr.draw.slug.SlugCommand
8+ import org.openrndr.draw.slug.SlugDrawer
9+ import org.openrndr.draw.slug.SlugGlyphMap
10+ import org.openrndr.draw.slug.StrokeMode
11+ import org.openrndr.draw.slug.TextSpan
12+ import org.openrndr.draw.slug.TextStyle
13+ import org.openrndr.extra.shapes.rectify.RectifiedContour
14+ import org.openrndr.math.Matrix44
15+ import org.openrndr.math.Vector4
16+ import org.openrndr.math.transforms.transform
17+
18+ fun RectifiedContour.positionOnlyPose (t : Double ): Matrix44 {
19+ return Matrix44 .fromColumnVectors(Vector4 .UNIT_X * - 1.0 , Vector4 .UNIT_Y * - 1.0 , Vector4 .UNIT_Z , position(t).xy01)
20+ }
21+
22+ fun slugTextOnContour (
23+ drawer : Drawer ,
24+ slugDrawer : SlugDrawer ,
25+ slugGlyphMap : SlugGlyphMap ,
26+ baseStyle : TextStyle ,
27+ spans : List <TextSpan >,
28+ contour : RectifiedContour ,
29+ startT : Double = 0.0
30+ ) {
31+ val fixOrientation = Matrix44 .fromColumnVectors(
32+ Vector4 (- 1.0 , 0.0 , 0.0 , 0.0 ),
33+ Vector4 (0.0 , - 1.0 , 0.0 , 0.0 ),
34+ Vector4 .UNIT_Z ,
35+ Vector4 .UNIT_W
36+ )
37+
38+ val shaper = TextShapingDriver .instance
39+
40+ var textWidth = 0.0
41+
42+ class WordInfo (val word : String , val width : Double , val shaped : List <ShapeResult >, val style : TextStyle )
43+
44+ val items = mutableListOf<WordInfo >()
45+
46+
47+ for (span in spans) {
48+ val spanStyle = baseStyle.cascade(span.style)
49+ val face = spanStyle.face ? : error(" face not set in span" )
50+
51+ val words = mutableListOf<String >()
52+ val currentWord = StringBuilder ()
53+
54+ for (ch in span.text) {
55+ when (ch) {
56+ ' ' , ' \t ' -> {
57+ if (currentWord.isNotEmpty()) {
58+ words.add(currentWord.toString())
59+ currentWord.clear()
60+ }
61+ words.add(" " )
62+ }
63+
64+ ' \n ' -> {
65+ if (currentWord.isNotEmpty()) {
66+ words.add(currentWord.toString())
67+ currentWord.clear()
68+ }
69+ words.add(" \n " )
70+ }
71+
72+ else -> {
73+ currentWord.append(ch)
74+ }
75+ }
76+ }
77+ if (currentWord.isNotEmpty()) {
78+ words.add(currentWord.toString())
79+ }
80+
81+ val spaceGlyph = face.glyphForCharacter(' ' )
82+ val spaceWidth = spaceGlyph.advanceWidth() * spanStyle.textWidthFactor!! * spanStyle.sizeInEm!!
83+
84+ for (word in words) {
85+ when (word) {
86+ " " , " \t " -> {
87+ items.add(WordInfo (" " , spaceWidth, emptyList(), spanStyle))
88+ textWidth + = spaceWidth
89+ }
90+
91+ else -> {
92+ val features = spanStyle.features
93+
94+ val shaped = shaper.shape(face, word, features)
95+ var wordWidth = 0.0
96+ for (sr in shaped) {
97+ wordWidth + = sr.advance.x * spanStyle.textWidthFactor!! * spanStyle.sizeInEm!!
98+ }
99+
100+ items.add(WordInfo (word, wordWidth, shaped, spanStyle))
101+ textWidth + = wordWidth
102+ }
103+ }
104+ }
105+ var cursorT = startT
106+
107+ val dt = 1.0 / contour.contour.length
108+
109+ val commands = mutableListOf<SlugCommand >()
110+ for (item in items) {
111+ if (item.word == " " ) {
112+ cursorT + = item.width * dt
113+ } else {
114+
115+ val itemTransform = transform {
116+ scale(item.style.sizeInEm!! * item.style.textWidthFactor!! , item.style.sizeInEm!! )
117+ }
118+
119+ val hscale = item.style.sizeInEm!! * item.style.textWidthFactor!!
120+ for (shape in item.shaped) {
121+ val slugIndex = slugGlyphMap.getSlugForGlyphIndex(item.style.face!! , shape.glyphIndex)
122+
123+ val t =
124+ if (contour.contour.closed) (cursorT + shape.offset.x * hscale * dt).mod(1.0 ) else cursorT + shape.offset.x * hscale * dt
125+
126+ val pose =
127+ contour.pose(t) * itemTransform * fixOrientation
128+
129+ val cmd = SlugCommand (
130+ slugIndex,
131+ pose,
132+ item.style.fill ? : ColorRGBa .TRANSPARENT ,
133+ item.style.stroke ? : ColorRGBa .TRANSPARENT ,
134+ strokeWeight = item.style.strokeWeight ? : 0.0 ,
135+ (item.style.strokeMode ? : StrokeMode .CENTER ).mode
136+ )
137+ commands.add(cmd)
138+ cursorT + = shape.advance.x * hscale * dt
139+ }
140+ }
141+ }
142+ slugDrawer.prepare(slugGlyphMap.slugMap, commands)
143+ slugDrawer.draw(drawer, slugGlyphMap.slugMap)
144+ }
145+ }
0 commit comments