# SPDX-FileCopyrightText: ☭ Emery Hemingway # SPDX-License-Identifier: Unlicense import std/[deques, streams] import bumpy, pixie, sdl2 type Rect = bumpy.Rect Pane* = ref object spans: seq[Span] arrangement: Arrangement image: Image texture: TexturePtr Well* = ref object panes: Deque[Pane] width, height: int textWidth, textHeight: int using pane: Pane well: Well proc newWell*(width, height: int): Well = result = Well(width: width, height: height) result.panes.addFirst Pane() proc dimensions(well): Vec2 = vec2(float well.width, float well.height) proc margins(well): tuple[w: int, h: int] = (well.width div 9, well.height div 9) proc textArea(well): tuple[w: int, h: int] = ( well.width - ((well.width div 9) shl 1), well.height - ((well.height div 9) shl 1), ) proc vec2(t: (int, int)): Vec2 = vec2(float t[0], float t[1]) proc rect*(well): Rect = result.wh = well.dimensions proc rectAt(well; i: int): Rect = if i < well.panes.len: let (quo, rem) = divmod(i, 3) shiftOff = succ quo fullWh = well.dimensions textArea = well.textArea paneArea = vec2(float(well.width shr shiftOff), float(well.height shr shiftOff)) result.w = float(textArea.w shr shiftOff) result.h = float(textArea.h shr shiftOff) case rem of 0: result.xy = fullWh - (paneArea * 2.0) of 1: result.x = fullWh.x - paneArea.x result.y = fullWh.y - (paneArea.y * 2.0) of 2: result.x = fullWh.x - (paneArea.x * 2.0) result.y = fullWh.y - paneArea.y else: discard result.xy = result.xy + (result.wh / 9.0) proc quadAt(well; i: int): Rect = if i < well.panes.len: let (quo, rem) = divmod(i, 3) shiftOff = succ quo fullWh = well.dimensions assert rem == 0 result.wh = vec2(float(well.width shr shiftOff), float(well.height shr shiftOff)) result.xy = fullWh - result.wh proc place(well; offset, width, height: int): (Pane, Rect) = ## Return the `Pane` at `offset` from the top of `well` or `nil` if ## the offset is too deep. The position and size of the `Pane` is ## returned as well. if offset < well.panes.len: result[0] = well.panes[offset] result[1] = well.rectAt(offset) proc append*(well; text: string; font: Font) = assert well.panes.len > 0 let span = newSpan(text & "\n", font) while true: let pane = well.panes.peekLast() pane.spans.add(span) var textArea = well.textArea.vec2 arrangement = typeset(pane.spans, textArea) bounds = layoutBounds arrangement if bounds.y <= textArea.y: doAssert pane.spans.len > 0, "text does not find on a single pane - " & $bounds & $well.dimensions pane.arrangement = arrangement break else: discard pane.spans.pop() well.panes.addLast Pane() proc append*(well; stream: Stream; font: Font) = var line: string while readLine(stream, line): append(well, line, font) type Intersection = tuple src, dst: Rect index: int const amask = uint32 0xff000000 rmask = uint32 0x000000ff gmask = uint32 0x0000ff00 bmask = uint32 0x00ff0000 proc texture*(well; index: int; renderer: RendererPtr): TexturePtr = if index < well.panes.len: let pane = well.panes[index] if pane.texture.isNil and not pane.arrangement.isNil: assert pane.image.isNil let textArea = well.textArea pane.image = newImage(textArea.w, textArea.h) pane.image.fill(rgba(255, 255, 255, 255)) pane.image.fillText(pane.arrangement) var surface = createRGBSurfaceFrom( pane.image.data[0].addr, pane.image.width.cint, pane.image.height.cint, cint 32, pane.image.width.cint shl 2, rmask, gmask, bmask, amask, ) assert(not surface.isNil, $getError()) pane.texture = createTextureFromSurface(renderer, surface) destroy(surface) assert(not pane.texture.isNil, $getError()) result = pane.texture iterator intersectingPanes*(well; view: Rect): Intersection = var intersect: Intersection zoom = 1.0 while intersect.index < well.panes.len: var paneRect = well.rectAt(intersect.index) let stepDown = (intersect.index mod 3) == 0 if stepDown: zoom = zoom * 2.0 if view.overlaps paneRect: intersect.dst = view and paneRect intersect.src = rect(intersect.dst.xy - paneRect.xy, intersect.dst.wh) * zoom yield(intersect) if stepDown: let quad = well.quadAt(intersect.index) if not view.overlaps quad: echo "view ", view, " does not overlap quadrant ", quad # all further panes are non-intersecting break inc(intersect.index)