context isPointInPath isPointInStroke
This commit is contained in:
parent
5ccfdb574e
commit
d097fda55b
|
@ -1,4 +1,4 @@
|
|||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
author = "Andre von Houck and Ryan Oldenburg"
|
||||
description = "Full-featured 2d graphics library for Nim."
|
||||
license = "MIT"
|
||||
|
|
|
@ -647,3 +647,55 @@ proc arcTo*(ctx: Context, x1, y1, x2, y2, radius: float32) =
|
|||
proc arcTo*(ctx: Context, a, b: Vec2, r: float32) =
|
||||
## Adds a circular arc using the given control points and radius.
|
||||
ctx.path.arcTo(a, b, r)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, path: Path, pos: Vec2, windingRule = wrNonZero
|
||||
): bool =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
path.fillOverlaps(pos, ctx.mat, windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, path: Path, x, y: float32, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(path, vec2(x, y), windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, pos: Vec2, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(ctx.path, pos, windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, x, y: float32, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(ctx.path, vec2(x, y), windingRule)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, path: Path, pos: Vec2): bool =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
path.strokeOverlaps(
|
||||
pos,
|
||||
ctx.mat,
|
||||
ctx.lineWidth,
|
||||
ctx.lineCap,
|
||||
ctx.lineJoin,
|
||||
ctx.miterLimit,
|
||||
ctx.lineDash
|
||||
)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, path: Path, x, y: float32): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(path, vec2(x, y))
|
||||
|
||||
proc isPointInStroke*(ctx: Context, pos: Vec2): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(ctx.path, pos)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, x, y: float32): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(ctx.path, vec2(x, y))
|
||||
|
|
|
@ -1029,7 +1029,7 @@ proc requiresAntiAliasing(segments: seq[(Segment, int16)]): bool =
|
|||
template hasFractional(v: float32): bool =
|
||||
v - trunc(v) != 0
|
||||
|
||||
for i in 0 ..< segments.len: # For arc
|
||||
for i in 0 ..< segments.len: # For gc:arc
|
||||
let segment = segments[i][0]
|
||||
if segment.at.x != segment.to.x or
|
||||
segment.at.x.hasFractional() or # at.x and to.x are the same
|
||||
|
@ -1045,7 +1045,7 @@ proc computePixelBounds(segments: seq[(Segment, int16)]): Rect =
|
|||
xMax = float32.low
|
||||
yMin = float32.high
|
||||
yMax = float32.low
|
||||
for i in 0 ..< segments.len: # For arc
|
||||
for i in 0 ..< segments.len: # For gc:arc
|
||||
let segment = segments[i][0]
|
||||
xMin = min(xMin, min(segment.at.x, segment.to.x))
|
||||
xMax = max(xMax, max(segment.at.x, segment.to.x))
|
||||
|
@ -1153,7 +1153,7 @@ iterator walk(
|
|||
var
|
||||
prevAt: float32
|
||||
count: int32
|
||||
for i in 0 ..< numHits:
|
||||
for i in 0 ..< numHits: # For gc:arc
|
||||
let (at, winding) = hits[i]
|
||||
if windingRule == wrNonZero and
|
||||
(count != 0) == (count + winding != 0) and
|
||||
|
@ -1201,13 +1201,13 @@ proc computeCoverages(
|
|||
scanline.a.y = yLine
|
||||
scanline.b.y = yLine
|
||||
numHits = 0
|
||||
for i in 0 ..< partitioning.partitions[partitionIndex].len: # For arc
|
||||
for i in 0 ..< partitioning.partitions[partitionIndex].len: # For gc:arc
|
||||
let
|
||||
segment = partitioning.partitions[partitionIndex][i][0]
|
||||
winding = partitioning.partitions[partitionIndex][i][1]
|
||||
if segment.at.y <= scanline.a.y and segment.to.y >= scanline.a.y:
|
||||
var at: Vec2
|
||||
if segment.to != at and scanline.intersects(segment, at):
|
||||
if scanline.intersects(segment, at) and segment.to != at:
|
||||
if numHits == hits.len:
|
||||
hits.setLen(hits.len * 2)
|
||||
hits[numHits] = (min(at.x, size.x), winding)
|
||||
|
@ -1889,5 +1889,71 @@ proc strokePath*(
|
|||
fill.draw(mask)
|
||||
image.draw(fill, blendMode = paint.blendMode)
|
||||
|
||||
proc overlaps(
|
||||
shapes: seq[seq[Vec2]],
|
||||
test: Vec2,
|
||||
windingRule: WindingRule
|
||||
): bool =
|
||||
var hits: seq[(float32, int16)]
|
||||
|
||||
let
|
||||
scanline = line(vec2(0, test.y), vec2(1000, test.y))
|
||||
segments = shapes.shapesToSegments()
|
||||
for i in 0 ..< segments.len: # For gc:arc
|
||||
let
|
||||
segment = segments[i][0]
|
||||
winding = segments[i][1]
|
||||
if segment.at.y <= scanline.a.y and segment.to.y >= scanline.a.y:
|
||||
var at: Vec2
|
||||
if scanline.intersects(segment, at) and segment.to != at:
|
||||
hits.add((at.x, winding))
|
||||
|
||||
if hits.len > 32:
|
||||
quickSort(hits, 0, hits.high)
|
||||
else:
|
||||
insertionSort(hits, hits.high)
|
||||
|
||||
var count: int
|
||||
for i in 0 ..< hits.len: # For gc:arc
|
||||
let (at, winding) = hits[i]
|
||||
if at > test.x:
|
||||
result = shouldFill(windingRule, count)
|
||||
break
|
||||
count += winding
|
||||
|
||||
proc fillOverlaps*(
|
||||
path: Path,
|
||||
test: Vec2,
|
||||
transform: Vec2 | Mat3 = vec2(), ## Applied to the path, not the test point.
|
||||
windingRule = wrNonZero
|
||||
): bool =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
var shapes = parseSomePath(path, true, transform.pixelScale())
|
||||
shapes.transform(transform)
|
||||
shapes.overlaps(test, windingRule)
|
||||
|
||||
proc strokeOverlaps*(
|
||||
path: Path,
|
||||
test: Vec2,
|
||||
transform: Vec2 | Mat3 = vec2(), ## Applied to the path, not the test point.
|
||||
strokeWidth = 1.0,
|
||||
lineCap = lcButt,
|
||||
lineJoin = ljMiter,
|
||||
miterLimit = defaultMiterLimit,
|
||||
dashes: seq[float32] = @[],
|
||||
): bool =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
var strokeShapes = strokeShapes(
|
||||
parseSomePath(path, false, transform.pixelScale()),
|
||||
strokeWidth,
|
||||
lineCap,
|
||||
lineJoin,
|
||||
miterLimit,
|
||||
dashes
|
||||
)
|
||||
strokeShapes.transform(transform)
|
||||
strokeShapes.overlaps(test, wrNonZero)
|
||||
|
||||
when defined(release):
|
||||
{.pop.}
|
||||
|
|
|
@ -602,3 +602,32 @@ block:
|
|||
rhino = readImage("tests/images/rhino.png")
|
||||
ctx.drawImage(rhino, rect(33, 71, 104, 124), rect(21, 20, 87, 104));
|
||||
image.writeFile("tests/images/context/draw_image_rhino2.png")
|
||||
|
||||
block:
|
||||
let
|
||||
image = newImage(100, 100)
|
||||
ctx = newContext(image)
|
||||
ctx.rect(10, 10, 100, 100)
|
||||
doAssert ctx.isPointInPath(30, 70)
|
||||
|
||||
block:
|
||||
let
|
||||
image = newImage(300, 150)
|
||||
ctx = newContext(image)
|
||||
ctx.arc(150, 75, 50, 0, 2 * PI)
|
||||
doAssert ctx.isPointInPath(150, 50)
|
||||
|
||||
block:
|
||||
let
|
||||
image = newImage(100, 100)
|
||||
ctx = newContext(image)
|
||||
ctx.rect(10, 10, 100, 100)
|
||||
doAssert ctx.isPointInStroke(50, 10)
|
||||
|
||||
block:
|
||||
let
|
||||
image = newImage(300, 150)
|
||||
ctx = newContext(image)
|
||||
ctx.ellipse(150, 75, 40, 60)
|
||||
ctx.lineWidth = 25
|
||||
doAssert ctx.isPointInStroke(110, 75)
|
||||
|
|
|
@ -530,3 +530,41 @@ block:
|
|||
ctx.stroke();
|
||||
|
||||
surface.writeFile("tests/images/paths/arcTo3.png")
|
||||
|
||||
block:
|
||||
var path: Path
|
||||
path.rect(0, 0, 10, 10)
|
||||
|
||||
doAssert path.fillOverlaps(vec2(5, 5))
|
||||
doAssert path.fillOverlaps(vec2(0, 0))
|
||||
doAssert path.fillOverlaps(vec2(9, 0))
|
||||
doAssert path.fillOverlaps(vec2(0, 9))
|
||||
doAssert not path.fillOverlaps(vec2(10, 10))
|
||||
|
||||
block:
|
||||
var path: Path
|
||||
path.ellipse(20, 20, 20, 10)
|
||||
|
||||
doAssert not path.fillOverlaps(vec2(0, 0))
|
||||
doAssert path.fillOverlaps(vec2(20, 20))
|
||||
doAssert path.fillOverlaps(vec2(10, 20))
|
||||
doAssert path.fillOverlaps(vec2(30, 20))
|
||||
|
||||
block:
|
||||
var path: Path
|
||||
path.rect(10, 10, 10, 10)
|
||||
|
||||
doAssert path.strokeOverlaps(vec2(10, 10))
|
||||
doAssert path.strokeOverlaps(vec2(20.1, 20.1))
|
||||
doAssert not path.strokeOverlaps(vec2(5, 5))
|
||||
|
||||
block:
|
||||
var path: Path
|
||||
path.ellipse(20, 20, 20, 10)
|
||||
|
||||
doAssert not path.strokeOverlaps(vec2(0, 0))
|
||||
doAssert not path.strokeOverlaps(vec2(20, 20))
|
||||
doAssert path.strokeOverlaps(vec2(0, 20))
|
||||
doAssert path.strokeOverlaps(vec2(40, 20))
|
||||
doAssert path.strokeOverlaps(vec2(19.8, 30.2))
|
||||
doAssert not path.strokeOverlaps(vec2(19.4, 30.6))
|
||||
|
|
Loading…
Reference in a new issue