forked from HEL/rivet-typst
		
	ported rivet to typst
This commit is contained in:
		
							
								
								
									
										467
									
								
								src/renderer.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										467
									
								
								src/renderer.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,467 @@ | ||||
| #import "@preview/cetz:0.2.2": canvas, draw | ||||
|  | ||||
| #import "range.typ" as rng | ||||
| #import "structure.typ" | ||||
| #import "vec.typ" | ||||
|  | ||||
| #let draw-rect(color, x, y, width, height, thickness: 0) = { | ||||
|   let fill = none | ||||
|   let stroke = color + thickness * 1pt | ||||
|   if thickness == 0 { | ||||
|     fill = color | ||||
|     stroke = none | ||||
|   } | ||||
|   draw.rect((x, -y), (x + width, -y - height), fill: fill, stroke: stroke) | ||||
| } | ||||
|  | ||||
| #let draw-text( | ||||
|   txt, | ||||
|   color, | ||||
|   x, | ||||
|   y, | ||||
|   anchor: "center", | ||||
|   font: none, | ||||
|   italic: false, | ||||
|   size: 1em, | ||||
|   fill: none | ||||
| ) = { | ||||
|   let text-params = (:) | ||||
|   if font != none { | ||||
|     text-params.insert("font", font) | ||||
|   } | ||||
|   if italic { | ||||
|     text-params.insert("style", "italic") | ||||
|   } | ||||
|    | ||||
|   let content-params = (:) | ||||
|   if fill != none { | ||||
|     content-params.insert("fill", fill) | ||||
|     content-params.insert("frame", "rect") | ||||
|     content-params.insert("padding", 4pt) | ||||
|   } | ||||
|  | ||||
|   draw.content( | ||||
|     (x, -y), | ||||
|     text(txt, fill: color, size: size, ..text-params), | ||||
|     anchor: anchor, | ||||
|     stroke: none, | ||||
|     ..content-params | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #let draw-line(color, a, b) = { | ||||
|   let (x0, y0) = a | ||||
|   let (x1, y1) = b | ||||
|   draw.line((x0, -y0), (x1, -y1), stroke: color) | ||||
| } | ||||
|  | ||||
| #let draw-lines(color, ..pts) = { | ||||
|   let pts = pts.pos().map(pt => (pt.at(0), -pt.at(1))) | ||||
|   draw.line(..pts, stroke: color) | ||||
| } | ||||
|  | ||||
| #let draw-poly(color, ..pts, thickness: 0) = { | ||||
|   let pts = pts.pos().map(pt => (pt.at(0), -pt.at(1))) | ||||
|   let params = ( | ||||
|     stroke: (paint: color, thickness: thickness), | ||||
|     fill: none | ||||
|   ) | ||||
|   if thickness == 0 { | ||||
|     params = ( | ||||
|       stroke: none, | ||||
|       fill: color | ||||
|     ) | ||||
|   } | ||||
|   draw.line(..pts, ..params) | ||||
| } | ||||
|  | ||||
| #let draw-underbracket(config, start, end, bits-y) = { | ||||
|   let bit-w = config.bit-width | ||||
|   let bit-h = config.bit-height | ||||
|  | ||||
|   let x0 = start + bit-w / 2 | ||||
|   let x1 = end - bit-w / 2 | ||||
|   let y0 = bits-y + bit-h * 1.25 | ||||
|   let y1 = bits-y + bit-h * 1.5 | ||||
|  | ||||
|   let col = config.link-color | ||||
|   draw-lines(col, (x0, y0), (x0, y1), (x1, y1), (x1, y0)) | ||||
| } | ||||
|  | ||||
| #let draw-link( | ||||
|   config, | ||||
|   start-x, | ||||
|   start-y, | ||||
|   end-x, | ||||
|   end-y | ||||
| ) = { | ||||
|   let bit-h = config.bit-height | ||||
|   let arrow-margin = config.arrow-margin | ||||
|  | ||||
|   if end-x > start-x { | ||||
|     end-x -= arrow-margin | ||||
|   } else { | ||||
|     end-x += arrow-margin | ||||
|   } | ||||
|  | ||||
|   draw-lines( | ||||
|     config.link-color, | ||||
|     (start-x, start-y + bit-h * 1.5), | ||||
|     (start-x, end-y + bit-h / 2), | ||||
|     (end-x, end-y + bit-h / 2), | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #let draw-values(config, values, desc-x, desc-y) = { | ||||
|   let shapes = () | ||||
|   let txt-col = config.text-color | ||||
|   let bit-w = config.bit-height  // Why ? I don't remember | ||||
|   let gap = config.values-gap | ||||
|    | ||||
|   for (val, desc) in values.pairs().sorted(key: p => p.first()) { | ||||
|     desc-y += gap | ||||
|     let txt = val + " = " + desc | ||||
|     shapes += draw-text( | ||||
|       txt, txt-col, desc-x + bit-w / 2, desc-y, | ||||
|       anchor: "north-west", | ||||
|       font: config.italic-font-family, | ||||
|       italic: true, | ||||
|       size: config.italic-font-size | ||||
|     ) | ||||
|     desc-y += 8  // TODO: change this | ||||
|   } | ||||
|  | ||||
|   return (shapes, desc-x, desc-y) | ||||
| } | ||||
|  | ||||
| #let draw-description( | ||||
|   config, | ||||
|   range_, | ||||
|   start-x, | ||||
|   start-y, | ||||
|   width, | ||||
|   desc-x, | ||||
|   desc-y | ||||
| ) = { | ||||
|   let shapes = () | ||||
|   let bit-w = config.bit-width | ||||
|   let bit-h = config.bit-height | ||||
|  | ||||
|   if config.left-labels { | ||||
|     desc-x = calc.min(desc-x, start-x + width / 2 - bit-w) | ||||
|   } else { | ||||
|     desc-x = calc.max(desc-x, start-x + width / 2 + bit-w) | ||||
|   } | ||||
|  | ||||
|   shapes += draw-underbracket(config, start-x, start-x + width, start-y) | ||||
|  | ||||
|   let mid-x = start-x + width / 2 | ||||
|   shapes += draw-link(config, mid-x, start-y, desc-x, desc-y) | ||||
|  | ||||
|   let txt-anchor = "west" | ||||
|  | ||||
|   if config.left-labels { | ||||
|     txt-anchor -= "east" | ||||
|   } | ||||
|  | ||||
|   shapes += draw-text( | ||||
|     range_.description, | ||||
|     config.text-color, | ||||
|     desc-x, desc-y + bit-h / 2, | ||||
|     anchor: "west" | ||||
|   ) | ||||
|  | ||||
|   // TODO: change this | ||||
|   desc-y += 18 | ||||
|  | ||||
|   if range_.values != none and range_.depends-on == none { | ||||
|     let shapes_ | ||||
|     (shapes_, _, desc-y) = draw-values(config, range_.values, desc-x, desc-y) | ||||
|     shapes += shapes_ | ||||
|   } | ||||
|  | ||||
|   desc-y += config.description-margin | ||||
|  | ||||
|   return (shapes, desc-x, desc-y) | ||||
| } | ||||
|  | ||||
| #let draw-arrow(config, start-x, start-y, end-x, end-y, label: "") = { | ||||
|   let shapes = () | ||||
|   let dash-len = config.dash-length | ||||
|   let dash-space = config.dash-space | ||||
|   let arrow-size = config.arrow-size | ||||
|   let link-col = config.link-color | ||||
|   let txt-col = config.text-color | ||||
|   let arrow-label-dist = config.arrow-label-distance | ||||
|  | ||||
|   let start = vec.vec(start-x, start-y) | ||||
|   let end = vec.vec(end-x, end-y) | ||||
|   let start-end = vec.sub(end, start) | ||||
|   let d = vec.normalize(start-end) | ||||
|  | ||||
|   let dashes = int(vec.mag(start-end) / (dash-len + dash-space)) | ||||
|  | ||||
|   for i in range(dashes) { | ||||
|     let a = vec.add( | ||||
|       start, | ||||
|       vec.mul(d, i * (dash-len + dash-space)) | ||||
|     ) | ||||
|     let b = vec.add( | ||||
|       a, | ||||
|       vec.mul(d, dash-len) | ||||
|     ) | ||||
|  | ||||
|     shapes += draw-line(link-col, (a.x, a.y), (b.x, b.y)) | ||||
|   } | ||||
|  | ||||
|   let n = vec.vec(d.y, -d.x) | ||||
|   let width = arrow-size / 1.5 | ||||
|   let p1 = vec.sub( | ||||
|     end, | ||||
|     vec.sub( | ||||
|       vec.mul(d, arrow-size), | ||||
|       vec.mul(n, width) | ||||
|     ) | ||||
|   ) | ||||
|   let p2 = vec.sub( | ||||
|     end, | ||||
|     vec.add( | ||||
|       vec.mul(d, arrow-size), | ||||
|       vec.mul(n, width) | ||||
|     ) | ||||
|   ) | ||||
|  | ||||
|   shapes += draw-poly( | ||||
|     link-col, | ||||
|     (end.x, end.y), | ||||
|     (p1.x, p1.y), | ||||
|     (p2.x, p2.y) | ||||
|   ) | ||||
|  | ||||
|   if label != "" { | ||||
|     shapes += draw-text( | ||||
|       label, | ||||
|       txt-col, | ||||
|       (start.x + end.x) / 2, | ||||
|       (start.y + end.y) / 2 + arrow-label-dist, | ||||
|       anchor: "north" | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   return shapes | ||||
| } | ||||
|  | ||||
| #let draw-dependency( | ||||
|   draw-struct, config, | ||||
|   struct, structures, bits-x, bits-y, range_, desc-x, desc-y | ||||
| ) = { | ||||
|   let shapes = () | ||||
|  | ||||
|   let bit-w = config.bit-width | ||||
|   let bit-h = config.bit-height | ||||
|   let arrow-margin = config.arrow-margin | ||||
|  | ||||
|   let start-i = struct.bits - range_.end - 1 | ||||
|   let start-x = bits-x + start-i * bit-w | ||||
|   let width = rng.bits(range_) * bit-w | ||||
|  | ||||
|   shapes += draw-underbracket(config, start-x, start-x + width, bits-y) | ||||
|   let depend-range = struct.ranges.at(rng.key(..range_.depends-on)) | ||||
|   let prev-range-y = bits-y + bit-h * 1.5 | ||||
|  | ||||
|   let prev-depend-y = if depend-range.last-value-y == -1 { | ||||
|     bits-y + bit-h * 1.5 | ||||
|   } else { | ||||
|     depend-range.last-value-y | ||||
|   } | ||||
|  | ||||
|   let depend-start-i = struct.bits - depend-range.end - 1 | ||||
|   let depend-start-x = bits-x + depend-start-i * bit-w | ||||
|   let depend-width = rng.bits(depend-range) * bit-w | ||||
|   let depend-mid = depend-start-x + depend-width / 2 | ||||
|   shapes += draw-underbracket(config, depend-start-x, depend-start-x + depend-width, bits-y) | ||||
|  | ||||
|   for (val, data) in range_.values.pairs().sorted(key: p => p.first()) { | ||||
|     shapes += draw-arrow(config, depend-mid, prev-depend-y, depend-mid, desc-y - arrow-margin) | ||||
|  | ||||
|     let val-ranges = (:) | ||||
|     for i in range(rng.bits(depend-range)) { | ||||
|       val-ranges.insert( | ||||
|         str(depend-range.end - i), | ||||
|         (name: val.at(i)) | ||||
|       ) | ||||
|     } | ||||
|  | ||||
|     let val-struct = ( | ||||
|       bits: rng.bits(depend-range), | ||||
|       start: depend-range.start, | ||||
|       ranges: val-ranges | ||||
|     ) | ||||
|     val-struct = structure.load("", val-struct) | ||||
|  | ||||
|     let shapes_ | ||||
|     (shapes_, ..) = draw-struct(config, val-struct, structures, ox: depend-start-x, oy: desc-y) | ||||
|     shapes += shapes_ | ||||
|  | ||||
|     let y = desc-y + bit-h * 1.5 | ||||
|  | ||||
|     let x1 | ||||
|     let x2 | ||||
|  | ||||
|     // Arrow from left to right | ||||
|     if depend-range.end > range_.start { | ||||
|       x1 = depend-start-x + depend-width + arrow-margin | ||||
|       x2 = start-x - arrow-margin | ||||
|      | ||||
|     // Arrow from right to left | ||||
|     } else { | ||||
|       x1 = depend-start-x - arrow-margin | ||||
|       x2 = start-x + width + arrow-margin | ||||
|     } | ||||
|  | ||||
|     shapes += draw-arrow(config, x1, y, x2, y, label: data.description) | ||||
|     shapes += draw-arrow(config, | ||||
|       start-x + width - bit-w, | ||||
|       prev-range-y, | ||||
|       start-x + width - bit-w, | ||||
|       desc-y + bit-h - arrow-margin | ||||
|     ) | ||||
|  | ||||
|     prev-depend-y = desc-y + bit-h * 2 + arrow-margin | ||||
|     prev-range-y = prev-depend-y | ||||
|     depend-range.last-value-y = prev-depend-y | ||||
|  | ||||
|     (shapes_, desc-y) = draw-struct(config, structures.at(data.structure), structures, ox: start-x, oy: desc-y) | ||||
|     shapes += shapes_ | ||||
|   } | ||||
|  | ||||
|   return (shapes, desc-x, desc-y) | ||||
| } | ||||
|  | ||||
| #let draw-structure(config, struct, structures, ox: 0, oy: 0) = { | ||||
|   let shapes | ||||
|   let bg-col = config.background | ||||
|   let txt-col = config.text-color | ||||
|   let border-col = config.border-color | ||||
|   let bit-w = config.bit-width | ||||
|   let bit-h = config.bit-height | ||||
|  | ||||
|   let (bits-x, bits-y) = (ox, oy + bit-h) | ||||
|   let bits-width = struct.bits * bit-w | ||||
|   let start-bit = struct.start | ||||
|  | ||||
|   // Draw rectangle around structure | ||||
|   shapes += draw-rect(border-col, bits-x, bits-y, bits-width, bit-h, thickness: 2) | ||||
|  | ||||
|   for i in range(struct.bits) { | ||||
|     let bit-x = ox + i * bit-w | ||||
|     shapes += draw-text( | ||||
|       str(struct.bits - i - 1 + start-bit), | ||||
|       txt-col, | ||||
|       bit-x + bit-w / 2, | ||||
|       oy + bit-h / 2  | ||||
|     ) | ||||
|  | ||||
|     // Draw separator | ||||
|     if i != 0 { | ||||
|       shapes += draw-line(border-col, (bit-x, bits-y), (bit-x, bits-y + bit-h)) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let ranges = structure.get-sorted-ranges(struct) | ||||
|   if config.left-labels { | ||||
|     ranges = ranges.rev() | ||||
|   } | ||||
|  | ||||
|   let desc-x | ||||
|   if config.force-descs-on-side { | ||||
|     desc-x = config.margins.at(3) + structures.main.bits * bit-w | ||||
|     if config.left-labels { | ||||
|       desc-x = config.width - desc-x | ||||
|     } | ||||
|   } else { | ||||
|     desc-x = ox | ||||
|     if config.left-labels { | ||||
|       desc-x += struct.bits * bit-w | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let desc-y = bits-y + bit-h * 2 | ||||
|  | ||||
|   // Names + simple descriptions | ||||
|   for range_ in ranges { | ||||
|     let start-i = struct.bits - range_.end + start-bit - 1 | ||||
|     let start-x = bits-x + start-i * bit-w | ||||
|     let width = rng.bits(range_) * bit-w | ||||
|  | ||||
|     let name-x = start-x + width / 2 | ||||
|     let name-y = bits-y + bit-h / 2 | ||||
|      | ||||
|     shapes += draw-text(range_.name, txt-col, name-x, name-y, fill: bg-col) | ||||
|      | ||||
|     if range_.description != "" { | ||||
|       //draw.circle((desc-x, -desc-y), radius: 5, fill: red) | ||||
|       let shapes_ | ||||
|       (shapes_, desc-x, desc-y) = draw-description( | ||||
|         config, range_, start-x, bits-y, width, desc-x, desc-y | ||||
|       ) | ||||
|       shapes += shapes_ | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Dependencies | ||||
|   for range_ in ranges { | ||||
|     if range_.values() != none and range_.depends-on != none { | ||||
|       let shapes_ | ||||
|       (shapes_, desc-x, desc-y) = draw-dependency( | ||||
|         draw-structure, config, | ||||
|         struct, structures, bits-x, bits-y, range_, desc-x, desc-y | ||||
|       ) | ||||
|       shapes += shapes_ | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return (shapes, desc-y) | ||||
| } | ||||
|  | ||||
| #let render(config, structures) = { | ||||
|   set text( | ||||
|     font: config.default-font-family, | ||||
|     size: config.default-font-size | ||||
|   ) | ||||
|  | ||||
|   let main = structures.main | ||||
|   let ox = config.margins.at(3) | ||||
|   if config.left-labels { | ||||
|     ox = config.width - ox - main.bits * config.bit-width | ||||
|   } | ||||
|  | ||||
|   let params = if config.full-page { | ||||
|     ( | ||||
|       width: auto, | ||||
|       height: auto, | ||||
|       fill: config.background | ||||
|     ) | ||||
|   } else { | ||||
|     (:) | ||||
|   } | ||||
|  | ||||
|   set page(..params) | ||||
|  | ||||
|   canvas(length: 1pt, background: config.background, { | ||||
|     let (shapes, _) = draw-structure( | ||||
|       config, main, structures, | ||||
|       ox: ox, | ||||
|       oy: config.margins.at(0) | ||||
|     ) | ||||
|     shapes | ||||
|     //draw.circle((300, -3000), fill: red, radius: 2) | ||||
|   }) | ||||
| } | ||||
|  | ||||
| #let make(config) = { | ||||
|   return ( | ||||
|     config: config, | ||||
|     render: render.with(config) | ||||
|   ) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user