Skip to content

Commit 14211d0

Browse files
committed
Add clipping
1 parent 7b1a5c5 commit 14211d0

File tree

3 files changed

+57
-14
lines changed

3 files changed

+57
-14
lines changed

geometryutils.nimble

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version = "1.0.0"
1+
version = "1.1.0"
22
author = "pseudo-random <josh.leh.2018@gmail.com>"
33
license = "MIT"
44
description = "A collection of geometry utilities for nim"

src/geometryutils/utils.nim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,9 @@ proc union*[T](a, b: BoundingBox[T]): BoundingBox[T] =
773773
max: max(a.max, b.max)
774774
)
775775

776+
proc hash*[T](box: BoundingBox[T]): Hash =
777+
return !$(box.min.hash() !& box.max.hash())
778+
776779
proc new_bounding_box*[T](points: seq[T]): BoundingBox[T] =
777780
result.min = points[0]
778781
result.max = points[0]

src/geometryutils/vectorrender2.nim

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
# SOFTWARE.
2222

23-
import xmltree, strtabs, sequtils, strutils, sugar
23+
import xmltree, strtabs, sequtils, strutils, sugar, tables
2424
import utils
2525

2626
type
@@ -48,6 +48,8 @@ type
4848
fill: Color
4949
stroke: Color
5050
stroke_width: float64
51+
clip: Box2
52+
has_clip: bool
5153
case kind: ShapeKind:
5254
of ShapeRect:
5355
rect_pos: Vec2
@@ -76,10 +78,12 @@ type
7678
VectorRender2* = object
7779
size*: Index2
7880
shapes: seq[Shape]
79-
81+
8082
stroke*: Color
8183
stroke_width*: float64
8284
fill*: Color
85+
has_clip*: bool
86+
clip*: Box2
8387

8488
font_size*: float64
8589
font_family*: string
@@ -101,6 +105,9 @@ proc add_style(shape: Shape, ren: VectorRender2): Shape =
101105
result.fill = ren.fill
102106
result.stroke = ren.stroke
103107
result.stroke_width = ren.stroke_width
108+
if ren.has_clip:
109+
result.has_clip = true
110+
result.clip = ren.clip
104111

105112
proc background*(ren: var VectorRender2, color: Color) =
106113
ren.shapes.add(Shape(kind: ShapeBackground, fill: color))
@@ -129,7 +136,7 @@ proc text*(ren: var VectorRender2,
129136
pos: Vec2,
130137
text: string,
131138
x_align: Align = AlignStart,
132-
y_align: Align = AlignEnd,
139+
y_align: Align = AlignStart,
133140
rotation: Deg = Deg(0)) =
134141
ren.shapes.add(Shape(kind: ShapeText,
135142
text_pos: pos, text: text,
@@ -151,6 +158,10 @@ proc end_path*(ren: var VectorRender2, close: bool = false) =
151158
ren.shapes.add(Shape(kind: ShapePath, path: ren.path).add_style(ren))
152159
ren.path = @[]
153160

161+
proc clip_region*(ren: var VectorRender2, clip: Box2) =
162+
ren.clip = clip
163+
ren.has_clip = true
164+
154165
proc to_svg*(color: Color): string =
155166
if color.a == 0:
156167
return "none"
@@ -168,11 +179,27 @@ proc to_svg*(color: Color): string =
168179
result &= $(color.a * 100) & "%"
169180
result &= ")"
170181

171-
proc add_style(node: XmlNode, shape: Shape): XmlNode =
182+
proc add_style(node: XmlNode,
183+
shape: Shape,
184+
defs: var seq[XmlNode],
185+
clips: var Table[Box2, string]): XmlNode =
172186
result = node
173187
result.attrs["stroke-width"] = $shape.stroke_width
174188
result.attrs["stroke"] = shape.stroke.to_svg()
175189
result.attrs["fill"] = shape.fill.to_svg()
190+
if shape.has_clip:
191+
if shape.clip notin clips:
192+
let name = "clip" & $clips.len
193+
defs.add(new_xml_tree("clipPath", [
194+
new_xml_tree("rect", [], to_xml_attributes({
195+
"x": $shape.clip.min.x,
196+
"y": $shape.clip.min.y,
197+
"width": $shape.clip.size.x,
198+
"height": $shape.clip.size.y
199+
}))
200+
], to_xml_attributes({"id": name})))
201+
clips[shape.clip] = name
202+
result.attrs["clip-path"] = "url(#" & clips[shape.clip] & ")"
176203

177204
proc to_text_anchor(align: Align): string =
178205
case align:
@@ -195,28 +222,31 @@ proc to_svg(point: PathPoint): string =
195222
of PathClose:
196223
return "Z"
197224

198-
proc to_svg*(shape: Shape, size: Index2): XmlNode =
225+
proc to_svg*(shape: Shape,
226+
size: Index2,
227+
defs: var seq[XmlNode],
228+
clips: var Table[Box2, string]): XmlNode =
199229
case shape.kind:
200230
of ShapeCircle:
201231
return new_xml_tree("circle", [], to_xml_attributes({
202232
"cx": $shape.circle_pos.x,
203233
"cy": $shape.circle_pos.y,
204234
"r": $shape.circle_radius,
205-
})).add_style(shape)
235+
})).add_style(shape, defs, clips)
206236
of ShapeRect:
207237
return new_xml_tree("rect", [], to_xml_attributes({
208238
"x": $shape.rect_pos.x,
209239
"y": $shape.rect_pos.y,
210240
"width": $shape.rect_size.x,
211241
"height": $shape.rect_size.y,
212-
})).add_style(shape)
242+
})).add_style(shape, defs, clips)
213243
of ShapeLine:
214244
return new_xml_tree("line", [], to_xml_attributes({
215245
"x1": $shape.line_a.x,
216246
"y1": $shape.line_a.y,
217247
"x2": $shape.line_b.x,
218248
"y2": $shape.line_b.y,
219-
})).add_style(shape)
249+
})).add_style(shape, defs, clips)
220250
of ShapeText:
221251
var attrs = to_xml_attributes({
222252
"font-size": $shape.font_size,
@@ -230,26 +260,36 @@ proc to_svg*(shape: Shape, size: Index2): XmlNode =
230260
else:
231261
attrs["x"] = $shape.text_pos.x
232262
attrs["y"] = $shape.text_pos.y
233-
return new_xml_tree("text", [new_text(shape.text)], attrs).add_style(shape)
263+
return new_xml_tree("text", [new_text(shape.text)], attrs).add_style(shape, defs, clips)
234264
of ShapePath:
235265
return new_xml_tree("path", [], to_xml_attributes({
236266
"d": shape.path.map(to_svg).join(" ")
237-
})).add_style(shape)
267+
})).add_style(shape, defs, clips)
238268
of ShapeBackground:
239269
return new_xml_tree("rect", [], to_xml_attributes({
240270
"x": "0", "y": "0",
241271
"width": $size.x, "height": $size.y,
242-
})).add_style(shape)
272+
})).add_style(shape, defs, clips)
243273
of ShapeEllipse:
244274
return new_xml_tree("ellipse", [], to_xml_attributes({
245275
"cx": $shape.ellipse_pos.x,
246276
"cy": $shape.ellipse_pos.y,
247277
"rx": $shape.ellipse_size.x,
248278
"ry": $shape.ellipse_size.y
249-
})).add_style(shape)
279+
})).add_style(shape, defs, clips)
250280

251281
proc to_svg*(ren: VectorRender2): XmlNode =
252-
new_xml_tree("svg", ren.shapes.map(shape => to_svg(shape, ren.size)), to_xml_attributes({
282+
var
283+
defs: seq[XmlNode] = @[]
284+
clips = init_table[Box2, string]()
285+
body: seq[XmlNode] = @[]
286+
for shape in ren.shapes:
287+
body.add(to_svg(shape, ren.size, defs, clips))
288+
if defs.len > 0:
289+
let defs_section = new_xml_tree("defs", defs)
290+
body = @[defs_section] & body
291+
292+
return new_xml_tree("svg", body, to_xml_attributes({
253293
"width": $ren.size.x,
254294
"height": $ren.size.y,
255295
"xmlns": "http://www.w3.org/2000/svg"

0 commit comments

Comments
 (0)