Skip to content

Commit

Permalink
mark: Move mark shapes to extra file
Browse files Browse the repository at this point in the history
  • Loading branch information
johannes-wolf committed Dec 20, 2023
1 parent 5cea472 commit d04cd26
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 75 deletions.
74 changes: 74 additions & 0 deletions src/mark-shapes.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#import "drawable.typ"
#import "path-util.typ"

// Calculate triangular tip offset, depending on the strokes
// join type.
//
// The angle is calculated for an isosceles triangle of base style.widh
// and height style.length
#let calculate-tip-offset(style) = {
if style.stroke.join == "round" {
return style.stroke.thickness / 2
}

if style.length == 0 {
return 0
}

let angle = calc.atan(style.width / (2 * style.length) / if style.harpoon { 2 } else { 1 } ) * 2
// If the miter length divided by the stroke width exceeds
// the stroke miter limit then the miter join is converted to a bevel.
// See: https://svgwg.org/svg2-draft/painting.html#LineJoin
if style.stroke.join == "miter" {
let angle = calc.abs(angle)
let miter-limit = 1 / calc.sin(angle / 2)
if miter-limit <= style.stroke.miter-limit {
return miter-limit * (style.stroke.thickness / 2)
}
}

// style.stroke.join must be "bevel"
return calc.sin(angle/2) * (style.stroke.thickness / 2)
}


// Dictionary of built-in mark styles
//
// (style) => (drawables:, tip-offset:, distance:)
#let marks = (
triangle: (style) => (
drawables: drawable.path(
path-util.line-segment(
(
(0, 0),
(style.length, style.width/2),
if style.harpoon { (style.length, 0) } else { (style.length, -style.width/2) }
)
),
close: true,
fill: style.fill,
stroke: style.stroke
),
tip-offset: calculate-tip-offset(style),
distance: style.length
),
stealth: (style) => (
drawables: drawable.path(
path-util.line-segment(
(
(0, 0),
(style.length, style.width/2),
(style.length - style.inset, 0),
if not style.harpoon {
(style.length, -style.width/2)
}
).filter(c => c != none)
),
stroke: style.stroke,
close: true,
fill: style.fill
),
distance: style.length - style.inset,
tip-offset: calculate-tip-offset(style)
)
)
78 changes: 3 additions & 75 deletions src/mark.typ
Original file line number Diff line number Diff line change
@@ -1,86 +1,14 @@
#let typst-length = length
#import "bezier.typ"
#import "drawable.typ"
#import "vector.typ"
#import "matrix.typ"
#import "util.typ"
#import "path-util.typ"
#import "styles.typ"
#import "mark-shapes.typ"

#let check-mark(style) = (style.start, style.end, style.symbol).any(v => v != none)

// Calculate triangular tip offset, depending on the strokes
// join type.
//
// The angle is calculated for an isosceles triangle of base style.widh
// and height style.length
#let calculate-tip-offset(style) = {
if style.stroke.join == "round" {
return style.stroke.thickness / 2
}

if style.length == 0 {
return 0
}

let angle = calc.atan(style.width / (2 * style.length) / if style.harpoon { 2 } else { 1 } ) * 2
// If the miter length divided by the stroke width exceeds
// the stroke miter limit then the miter join is converted to a bevel.
// See: https://svgwg.org/svg2-draft/painting.html#LineJoin
if style.stroke.join == "miter" {
let angle = calc.abs(angle)
let miter-limit = 1 / calc.sin(angle / 2)
if miter-limit <= style.stroke.miter-limit {
return miter-limit * (style.stroke.thickness / 2)
}
}

// style.stroke.join must be "bevel"
return calc.sin(angle/2) * (style.stroke.thickness / 2)
}

// Dictionary of built-in mark styles
//
// (style) => (drawables:, tip-offset:, distance:)
#let marks = (
triangle: (style) => (
drawables: drawable.path(
path-util.line-segment(
(
(0, 0),
(style.length, style.width/2),
if style.harpoon { (style.length, 0) } else { (style.length, -style.width/2) }
)
),
close: true,
fill: style.fill,
stroke: style.stroke
),
tip-offset: calculate-tip-offset(style),
distance: style.length
),
stealth: (style) => (
drawables: drawable.path(
path-util.line-segment(
(
(0, 0),
(style.length, style.width/2),
(style.length - style.inset, 0),
if not style.harpoon {
(style.length, -style.width/2)
}
).filter(c => c != none)
),
stroke: style.stroke,
close: true,
fill: style.fill
),
distance: style.length - style.inset,
tip-offset: calculate-tip-offset(style)
)
)


#let process-style(ctx, style, root) = {
let base-style = (
symbol: auto,
Expand Down Expand Up @@ -180,7 +108,7 @@
/// - drawables (drawables): The transformed drawables of the mark.
/// - distance: The distance between the tip of the mark and the end.
#let place-mark(style, pos, angle) = {
let (drawables, distance, tip-offset) = (marks.at(style.symbol))(style)
let (drawables, distance, tip-offset) = (mark-shapes.marks.at(style.symbol))(style)

return (
drawables: drawable.apply-transform(
Expand All @@ -205,7 +133,7 @@
if style.symbol == none {
continue
}
let mark = (marks.at(style.symbol))(style)
let mark = (mark-shapes.marks.at(style.symbol))(style)
mark.length = mark.distance + mark.tip-offset

let pos = path-util.point-on-path(
Expand Down

0 comments on commit d04cd26

Please sign in to comment.