forked from HEL/circuiteria
		
	
		
			
				
	
	
		
			177 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Typst
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Typst
		
	
	
	
	
	
| #import "@preview/cetz:0.3.2": draw
 | |
| #import "../util.typ": rotate-anchor
 | |
| 
 | |
| #let get-port-side(elmt, port) = {
 | |
|   for (side, ports) in elmt.ports {
 | |
|     for p in ports {
 | |
|       if p.id == port {
 | |
|         return side
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   panic("Unknown port " + port + " on element " + elmt.id)
 | |
| }
 | |
| 
 | |
| #let get-port-idx(elmt, port, side: auto) = {
 | |
|   if side == auto {
 | |
|     side = get-port-side(elmt, port)
 | |
|   }
 | |
|   assert(
 | |
|     side in elmt.ports,
 | |
|     message: "No ports on side '" + side + "' of element '" + elmt.id + "'"
 | |
|   )
 | |
|   let i = elmt.ports.at(side).position(p => p.id == port)
 | |
|   assert(
 | |
|     i != none,
 | |
|     message: "Could not find port '" + port + "' on side '" + side + "' of element '" + elmt.id + "'"
 | |
|   )
 | |
|   return i
 | |
| }
 | |
| 
 | |
| #let get-port-pos(elmt, bounds, side, port, port-i) = {
 | |
|   let (pt0, pt1) = bounds.ports.at(side)
 | |
|   let side-len = if side in ("north", "south") {
 | |
|     pt1.at(0) - pt0.at(0)
 | |
|   } else {
 | |
|     pt0.at(1) - pt1.at(1)
 | |
|   }
 | |
| 
 | |
|   let offset = if (
 | |
|     elmt.ports-pos == auto or
 | |
|     elmt.ports-pos.at(side, default: auto) == auto
 | |
|   ) {
 | |
|     let space = 100% / (elmt.ports.at(side, default: ()).len() + 1)
 | |
|     (port-i + 1) * space
 | |
|   } else {
 | |
|     assert(
 | |
|       side in elmt.ports-pos,
 | |
|       message: "Could not reliably compute port position (missing side)"
 | |
|     )
 | |
|     let side-pos = elmt.ports-pos.at(side)
 | |
|     if type(side-pos) == function {
 | |
|       (side-pos)(side-len, port-i)
 | |
|     } else if type(side-pos) == array {
 | |
|       (side-pos.at(i))(side-len)
 | |
|     } else if type(side-pos) == dictionary {
 | |
|       assert(
 | |
|         port in side-pos,
 | |
|         message: "Could not reliably compute port position (missing port)"
 | |
|       )
 | |
|       (side-pos.at(port))(side-len)
 | |
|     } else {
 | |
|       panic("Could not reliably compute port position (invalid type)")
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if type(offset) == ratio {
 | |
|     offset = offset * side-len / 100%
 | |
|   }
 | |
|   return offset
 | |
| }
 | |
| 
 | |
| #let add-port(
 | |
|   elmt-id, side, port, pos,
 | |
|   prev: none,
 | |
|   next: none,
 | |
|   debug: false
 | |
| ) = {
 | |
|   let name = port.at("name", default: "")
 | |
|   let name-rotate = port.at("vertical", default: false)
 | |
| 
 | |
|   if (port.at("clock", default: false)) {
 | |
|     if prev == none or next == none {
 | |
|       panic("Clock port must have previous and next positions")
 | |
|     }
 | |
|     
 | |
|     let size = if port.at("small", default: false) {8pt} else {1em}
 | |
|     let offset
 | |
|     if      (side == "north") { offset = (    0, -size) }
 | |
|     else if (side == "east")  { offset = (-size,     0) }
 | |
|     else if (side == "south") { offset = (    0,  size) }
 | |
|     else if (side == "west")  { offset = ( size,     0) }
 | |
| 
 | |
|     let pos1 = (rel: offset, to: pos)
 | |
| 
 | |
|     // TODO: use context or vectors to have the height relative to the width
 | |
|     draw.line(prev, pos1, next)
 | |
|   }
 | |
|   draw.content(
 | |
|     pos,
 | |
|     anchor: if name-rotate {rotate-anchor(side)} else {side},
 | |
|     padding: 2pt,
 | |
|     angle: if name-rotate {90deg} else {0deg},
 | |
|     name
 | |
|   )
 | |
| 
 | |
|   if debug {
 | |
|     draw.circle(
 | |
|       pos,
 | |
|       radius: .1,
 | |
|       stroke: none,
 | |
|       fill: red
 | |
|     )
 | |
|     
 | |
|   } else {
 | |
|     draw.hide(draw.circle(
 | |
|       pos,
 | |
|       radius: 0,
 | |
|       stroke: none
 | |
|     ))
 | |
|   }
 | |
|   draw.anchor(port.id, pos)
 | |
| }
 | |
| 
 | |
| #let add-ports(
 | |
|   elmt,
 | |
|   bounds
 | |
| ) = {
 | |
|   let debug = elmt.debug.at("ports", default: false)
 | |
| 
 | |
|   if type(elmt.ports) != dictionary {
 | |
|     return
 | |
|   }
 | |
| 
 | |
|   for (side, props) in bounds.ports {
 | |
|     let side-ports = elmt.ports.at(side, default: ())
 | |
|     let space = 100% / (side-ports.len() + 1)
 | |
|     
 | |
|     let (pt0, pt1) = props
 | |
|     let side-len = if side in ("north", "south") {
 | |
|       pt1.at(0) - pt0.at(0)
 | |
|     } else {
 | |
|       pt0.at(1) - pt1.at(1)
 | |
|     }
 | |
|     for (i, port) in side-ports.enumerate() {
 | |
|       let offset = get-port-pos(elmt, bounds, side, port, i)
 | |
| 
 | |
|       let pos = (pt0, offset, pt1)
 | |
|       let offset-prev = if type(offset) == ratio {
 | |
|         offset - space / 2
 | |
|       } else {
 | |
|         offset - space * side-len / 200%
 | |
|       }
 | |
|       let offset-next = if type(offset) == ratio {
 | |
|         offset + space / 2
 | |
|       } else {
 | |
|         offset + space * side-len / 200%
 | |
|       }
 | |
|       let pos-prev = (pt0, offset-prev, pt1)
 | |
|       let pos-next = (pt0, offset-next, pt1)
 | |
| 
 | |
|       if port.at("small", default: false) {
 | |
|         pos-prev = (pos, 4pt, pt0)
 | |
|         pos-next = (pos, 4pt, pt1)
 | |
|       }
 | |
| 
 | |
|       add-port(
 | |
|         elmt.id,
 | |
|         side,
 | |
|         port,
 | |
|         pos,
 | |
|         prev: pos-prev,
 | |
|         next: pos-next,
 | |
|         debug: debug
 | |
|       )
 | |
|     }
 | |
|   }
 | |
| } |