diff --git a/src/nanim/core.nim b/src/nanim/core.nim index dcdff17..fd80e16 100644 --- a/src/nanim/core.nim +++ b/src/nanim/core.nim @@ -17,11 +17,30 @@ type AngleMode* = enum amDegrees, amRadians + StyleMode* = enum + smSolidColor, smPaintPattern, smNone + + Style* = ref tuple + fillMode: StyleMode + fillColor: Color + fillPattern: proc(context: NVGContext): Paint + + strokeMode: StyleMode + strokeColor: Color + strokePattern: proc(context: NVGContext): Paint + strokeWidth: float + + winding: PathWinding + lineCap: LineCapJoin + lineJoin: LineCapJoin + compositeOperation: CompositeOperation + Entity* = ref object of RootObj points*: seq[Vec3[float]] tension*: float cornerRadius*: float + style*: Style position*: Vec3[float] rotation*: float @@ -58,6 +77,62 @@ type done*: bool +func newStyle*(): Style = + new(result) + result.fillMode = smPaintPattern + result.fillColor = rgb(255, 56, 116) + result.fillPattern = defaultPattern + + result.strokeMode = smSolidColor + result.strokeColor = rgb(230, 26, 94) + result.strokePattern = defaultPattern + result.strokeWidth = 2.0 + + result.winding = pwCCW + result.lineCap = lcjRound + result.lineJoin = lcjMiter + result.compositeOperation = coSourceOver + + +proc setStyle*(context: NVGContext, style: Style) = + case style.fillMode: + of smSolidColor: + context.fillColor(style.fillColor) + of smPaintPattern: + context.fillPaint(style.fillPattern(context)) + of smNone: discard + + context.strokeWidth(style.strokeWidth) + case style.strokeMode: + of smSolidColor: + context.strokeColor(style.strokeColor) + of smPaintPattern: + context.strokePaint(style.strokePattern(context)) + of smNone: context.strokeWidth(0.0) + + context.pathWinding(style.winding) + context.lineJoin(style.lineJoin) + context.lineCap(style.lineCap) + context.globalCompositeOperation(style.compositeOperation) + + +proc executeStyle*(context: NVGContext, style: Style) = + case style.fillMode: + of smSolidColor, smPaintPattern: + context.fill() + of smNone: discard + + case style.strokeMode: + of smSolidColor, smPaintPattern: + context.stroke() + of smNone: discard + + +proc applyStyle*(context: NVGContext, style: Style) = + context.setStyle(style) + context.executeStyle(style) + + const defaultTrackId* = 0 defaultAngleMode*: AngleMode = amDegrees @@ -75,26 +150,22 @@ proc `$`*(entity: Entity): string = method draw*(entity: Entity, scene: Scene) {.base.} = let context = scene.context - context.beginPath() + context.save() + context.beginPath() if entity.tension > 0: context.drawPointsWithTension(entity.points, entity.tension) else: context.drawPointsWithRoundedCornerRadius(entity.points, entity.cornerRadius) - context.closePath() - # context.fillColor(rgb(255, 56, 116)) - context.fillPaint(context.gridPattern()) - context.fill() - - context.strokeColor(rgb(230, 26, 94)) - context.strokeWidth(5) - context.stroke() + context.applyStyle(entity.style) + context.restore() func init*(entity: Entity) = entity.points = @[] + entity.style = newStyle() entity.children = @[] entity.tension = 0.0 entity.cornerRadius = 20.0 @@ -343,6 +414,16 @@ proc setCornerRadius*(entity: Entity, cornerRadius: float = 0.0): Tween = result = newTween(interpolators) +proc fill(context: NVGContext, width: cfloat, height: cfloat, color: Color = rgb(255, 255, 255)) = + context.save() + context.fillColor(color) + context.beginPath() + context.resetTransform() + context.rect(0, 0, width, height) + context.closePath() + context.fill() + context.restore() + proc init(scene: Scene) = scene.time = 0.0 scene.restartTime = 0.0 @@ -356,7 +437,9 @@ proc init(scene: Scene) = vec4[float](0,0,1,0), vec4[float](0,0,0,1)) scene.done = false - scene.background = proc(scene: Scene) = clearWithColor(rgb(255, 255, 255)) + scene.background = + proc(scene: Scene) = scene.context.fill(scene.width.cfloat, scene.height.cfloat, rgb(255, 255, 255)) + scene.foreground = proc(scene: Scene) = discard diff --git a/src/nanim/drawing.nim b/src/nanim/drawing.nim index 82fe60e..8ecb145 100644 --- a/src/nanim/drawing.nim +++ b/src/nanim/drawing.nim @@ -2,6 +2,7 @@ import nanovg, opengl, + glfw, glm @@ -57,9 +58,9 @@ proc drawPointsWithRoundedCornerRadius*(context: NVGContext, points: seq[Vec], c context.arcTo(p1.x, p1.y, midPoint.x, midPoint.y, cornerRadius) -proc defaultPattern(context: NVGContext) = +proc defaultPatternDrawer(context: NVGContext, width: float, height: float) = context.beginPath() - context.circle(5, 1440-5, 3) + context.circle(width/2, height/2, width/2) context.closePath() context.fillColor(rgb(20, 20, 20)) @@ -70,45 +71,57 @@ var patternPaint: Paint hasGatheredPattern = false -proc gridPattern*(context: NVGContext, patternDrawer: proc(context: NVGContext) = defaultPattern, width: cint = 10, height: cint = 10): Paint = + +proc offset(some: pointer; b: int): pointer {.inline.} = + result = cast[pointer](cast[int](some) + b) + + +proc gridPattern*(context: NVGContext, patternDrawer: proc(context: NVGContext, width: float, height: float) = defaultPatternDrawer, width: cint = 10, height: cint = 10): Paint = # Impure, but worth it for the performance benefit... if hasGatheredPattern: return patternPaint let - bufferSize = width*height*4 - oldTransformMatrix = context.currentTransform() + bufferSize = width * height * 4 + tempContext = nvgCreateContext({nifStencilStrokes, nifDebug}) - var - oldData = alloc0(bufferSize) - imageData = alloc0(bufferSize) + var imageData = alloc0(bufferSize) - context.endFrame() - - glPixelStorei(GL_PACK_ALIGNMENT, 1) - glReadBuffer(GL_BACK) - - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, oldData) + # clear the region glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, imageData) - context.beginFrame(2880.cfloat, 1440.cfloat, 1) - patternDrawer(context) - context.endFrame() + # draw the pattern + let + window = currentContext() + (frameBufferWidth, frameBufferHeight) = window.framebufferSize + + tempContext.beginFrame(frameBufferWidth.cfloat, frameBufferHeight.cfloat, 1) + clearWithColor() + tempContext.translate(0, frameBufferHeight.float - height.float) + patternDrawer(tempContext, width.float, height.float) + tempContext.endFrame() + # read the region glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, imageData) - var pixels: seq[uint8] = newSeq[uint8](bufferSize) - copyMem(pixels[0].unsafeAddr, imageData, bufferSize) + + var pixels = newSeq[uint8](bufferSize) + + # Flip the image + for y in 0..