From cd736098132c992d019498d12aa231934714a56b Mon Sep 17 00:00:00 2001 From: Seiji Matsuoka Date: Wed, 10 Jan 2024 16:29:42 +0900 Subject: [PATCH] fix shapes of wedges in 2D drawing --- src/draw/cairo.jl | 105 +++++++++++++++++++++++++-------------------- src/draw/draw2d.jl | 8 ++-- src/draw/svg.jl | 84 ++++++++++++++++++++---------------- 3 files changed, 108 insertions(+), 89 deletions(-) diff --git a/src/draw/cairo.jl b/src/draw/cairo.jl index b39968b6..91c4b954 100644 --- a/src/draw/cairo.jl +++ b/src/draw/cairo.jl @@ -7,47 +7,54 @@ import Cairo mutable struct CairoCanvas <: Canvas - fontweight::String - fontfamily::String - fontsize::Float64 - fontscalef::Float64 - bgcolor::Color - bgopacity::Float64 - + scaleunit::Float64 mbwidthf::Float64 wedgewidthf::Float64 wavewidthf::Float64 triminnerf::Float64 trimoverlapf::Float64 + linehlwidthf::Float64 annotsizef::Float64 - scalef::Float64 - paddingX::Float64 - paddingY::Float64 + hlsizef::Float64 + paddingXf::Float64 + paddingYf::Float64 + + fontweight::String + fontfamily::String + fontsize::Float64 + bgcolor::Color + bgopacity::Float64 + cairoscalef::Float64 surface::Cairo.CairoSurface context::Cairo.CairoContext coords::Matrix{Float64} - valid::Bool function CairoCanvas(width::Int, height::Int, bgcolor::Color, bgopacity::Float64) canvas = new() - canvas.fontweight = "Normal" - canvas.fontfamily = "Sans" - canvas.fontsize = 11.0 - canvas.fontscalef = 1 - canvas.bgcolor = bgcolor - canvas.bgopacity = bgopacity + # Geometry + canvas.scaleunit = 30.0 canvas.mbwidthf = 0.15 canvas.wedgewidthf = 0.3 canvas.wavewidthf = 0.2 canvas.triminnerf = 0.2 canvas.trimoverlapf = 0.3 + canvas.linehlwidthf = 0.3 canvas.annotsizef = 0.7 - canvas.scalef = 30.0 # suitable for fontsize=11 and default line width - canvas.paddingX = 30.0 - canvas.paddingY = 30.0 + canvas.hlsizef = 1.2 + canvas.paddingXf = 1.0 + canvas.paddingYf = 1.0 + + # Appearance + canvas.fontweight = "Normal" + canvas.fontfamily = "Sans" + canvas.fontsize = 11.0 + canvas.bgcolor = bgcolor + canvas.bgopacity = bgopacity + # Canvas state + canvas.cairoscalef = 1 canvas.surface = Cairo.CairoARGBSurface(width, height) canvas.context = Cairo.CairoContext(canvas.surface) @@ -105,20 +112,18 @@ Move and adjust the size of the molecule for drawing. function initcanvas!( canvas::CairoCanvas, coords::AbstractArray{Float64}, boundary::Tuple) (top, left, width, height, unit) = boundary - sf = canvas.scalef / unit - pd = [canvas.paddingX canvas.paddingY] + sf = canvas.scaleunit / unit + pd = [canvas.paddingXf canvas.paddingYf] * canvas.scaleunit canvas.coords = (coords .- [left top]) .* [1 -1] .* sf .+ pd - viewboxW = width * sf + canvas.paddingX * 2 - viewboxH = height * sf + canvas.paddingY * 2 - x_scale = canvas.surface.width / viewboxW - y_scale = canvas.surface.height / viewboxH + canvasW = canvas.surface.width + canvasH = canvas.surface.height + x_scale, y_scale = [canvasW canvasH] ./ (([width height] * sf) .+ (pd * 2)) min_scale = min(x_scale, y_scale) # keep aspect ratio - xoff = x_scale > y_scale ? (1 - y_scale / x_scale) / 2 * canvas.surface.width : 0 # x centering - yoff = y_scale > x_scale ? (1 - x_scale / y_scale) / 2 * canvas.surface.height : 0 # y centering + xoff = x_scale > y_scale ? (1 - y_scale / x_scale) / 2 * canvasW : 0 # x centering + yoff = y_scale > x_scale ? (1 - x_scale / y_scale) / 2 * canvasH : 0 # y centering Cairo.translate(canvas.context, xoff, yoff) Cairo.scale(canvas.context, min_scale, min_scale) - canvas.fontscalef = min_scale - canvas.valid = true + canvas.cairoscalef = min_scale return end @@ -131,7 +136,7 @@ atommarkupright(canvas::CairoCanvas, symbol, charge, implicith) = atomhtml( function drawtextcairo!(canvas::CairoCanvas, pos, text, color, fxoff, halign) - fs = round(canvas.fontsize * canvas.fontscalef, digits=1) + fs = round(canvas.fontsize * canvas.cairoscalef, digits=1) Cairo.set_font_face(canvas.context, join([canvas.fontfamily, canvas.fontweight, fs], " ")) Cairo.set_source_rgba(canvas.context, color.r / 255, color.g / 255, color.b / 255, 1) cext = Cairo.text_extents(canvas.context, "C") @@ -155,7 +160,7 @@ drawtextright!(canvas::CairoCanvas, pos, text, color) = drawtextcairo!( function drawtextannot!(canvas::CairoCanvas, pos, text, color, bgcolor) - size = round(canvas.fontsize * canvas.fontscalef * canvas.annotsizef, digits=1) + size = round(canvas.fontsize * canvas.annotsizef * canvas.cairoscalef, digits=1) Cairo.set_font_face(canvas.context, join([canvas.fontfamily, canvas.fontweight, size], " ")) Cairo.set_source_rgba(canvas.context, bgcolor.r / 255, bgcolor.g / 255, bgcolor.b / 255, 1) Cairo.arc(canvas.context, pos.x + size, pos.y + size, size, 0, 2pi) @@ -166,7 +171,7 @@ function drawtextannot!(canvas::CairoCanvas, pos, text, color, bgcolor) end function drawtexthighlight!(canvas::CairoCanvas, pos, color) - size = round(Int, canvas.fontsize * canvas.fontscalef * 1.2) + size = round(Int, canvas.fontsize * canvas.hlsizef * canvas.cairoscalef) Cairo.set_source_rgba(canvas.context, color.r / 255, color.g / 255, color.b / 255, 1) Cairo.arc(canvas.context, pos.x, pos.y, size, 0, 2pi) Cairo.fill(canvas.context) @@ -176,7 +181,7 @@ end function drawline!(canvas::CairoCanvas, seg, color; isdashed=false) Cairo.set_source_rgba(canvas.context, color.r / 255, color.g / 255, color.b / 255, 1) - Cairo.set_line_width(canvas.context, canvas.fontscalef) + Cairo.set_line_width(canvas.context, canvas.cairoscalef) isdashed && Cairo.set_dash(canvas.context, [10, 10], 0) Cairo.move_to(canvas.context, seg.u.x, seg.u.y) Cairo.line_to(canvas.context, seg.v.x, seg.v.y) @@ -187,7 +192,7 @@ end function drawline!(canvas::CairoCanvas, seg, ucolor, vcolor; isdashed=false) ucolor == vcolor && return drawline!(canvas, seg, ucolor, isdashed=isdashed) mid = midpoint(seg) - Cairo.set_line_width(canvas.context, canvas.fontscalef) + Cairo.set_line_width(canvas.context, canvas.cairoscalef) isdashed && Cairo.set_dash(canvas.context, [10, 10], 0) Cairo.set_source_rgba(canvas.context, ucolor.r / 255, ucolor.g / 255, ucolor.b / 255, 1) Cairo.move_to(canvas.context, seg.u.x, seg.u.y) @@ -216,12 +221,13 @@ end function drawwedge!(canvas::CairoCanvas, seg, color) """ u ◀︎ v """ d = distance(seg) - scalef = Point2D(d, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d, canvas.wedgewidthf / 2 * canvas.scaleunit * canvas.cairoscalef) rotatef = unitvector(seg) translf = seg.u Cairo.save(canvas.context) cairo_transform!(canvas.context, scalef, rotatef, translf) Cairo.set_source_rgba(canvas.context, color.r / 255, color.g / 255, color.b / 255, 1) + # draw '◀︎', length 1, width 2 Cairo.move_to(canvas.context, 0, 0) Cairo.line_to(canvas.context, 1, 1) Cairo.line_to(canvas.context, 1, -1) @@ -235,12 +241,13 @@ function drawwedge!(canvas::CairoCanvas, seg, ucolor, vcolor) """ u ◀︎ v """ ucolor == vcolor && return drawwedge!(canvas, seg, ucolor) d = distance(seg) - scalef = Point2D(d, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d, canvas.wedgewidthf / 2 * canvas.scaleunit * canvas.cairoscalef) rotatef = unitvector(seg) translf = seg.u Cairo.save(canvas.context) cairo_transform!(canvas.context, scalef, rotatef, translf) Cairo.set_source_rgba(canvas.context, ucolor.r / 255, ucolor.g / 255, ucolor.b / 255, 1) + # draw '◀︎', length 1, width 2 Cairo.move_to(canvas.context, 0, 0) Cairo.line_to(canvas.context, 0.5, 0.5) Cairo.line_to(canvas.context, 0.5, -0.5) @@ -261,13 +268,14 @@ end function drawdashedwedge!(canvas::CairoCanvas, seg, color) """ u ◁ v """ d = distance(seg) - scalef = Point2D(d / 7, d / 14 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 16 * canvas.scaleunit * canvas.cairoscalef) rotatef = unitvector(seg) translf = seg.u Cairo.save(canvas.context) cairo_transform!(canvas.context, scalef, rotatef, translf) - Cairo.set_line_width(canvas.context, d / 20 * canvas.fontscalef) # 16 seems a bit thick + Cairo.set_line_width(canvas.context, d / 20 * canvas.cairoscalef) # 16 seems a bit thick Cairo.set_source_rgba(canvas.context, color.r / 255, color.g / 255, color.b / 255, 1) + # draw '◀︎', length 7, width 16 for i in 1:8 Cairo.move_to(canvas.context, i - 1, i) Cairo.line_to(canvas.context, i - 1, -i) @@ -281,13 +289,14 @@ function drawdashedwedge!(canvas::CairoCanvas, seg, ucolor, vcolor) """ u ◁ v """ ucolor == vcolor && return drawdashedwedge!(canvas, seg, ucolor) d = distance(seg) - scalef = Point2D(d / 7, d / 14 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 16 * canvas.scaleunit * canvas.cairoscalef) rotatef = unitvector(seg) translf = seg.u Cairo.save(canvas.context) cairo_transform!(canvas.context, scalef, rotatef, translf) - Cairo.set_line_width(canvas.context, d / 20 * canvas.fontscalef) # 16 seems a bit thick + Cairo.set_line_width(canvas.context, d / 20 * canvas.cairoscalef) # 16 seems a bit thick Cairo.set_source_rgba(canvas.context, ucolor.r / 255, ucolor.g / 255, ucolor.b / 255, 1) + # draw '◀︎', length 7, width 16 for i in 1:4 Cairo.move_to(canvas.context, i - 1, i) Cairo.line_to(canvas.context, i - 1, -i) @@ -306,13 +315,14 @@ end function drawwave!(canvas::CairoCanvas, seg, color) d = distance(seg) - scalef = Point2D(d / 7, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 2 * canvas.scaleunit * canvas.cairoscalef) rotatef = unitvector(seg) translf = seg.u Cairo.save(canvas.context) cairo_transform!(canvas.context, scalef, rotatef, translf) - Cairo.set_line_width(canvas.context, canvas.fontscalef) + Cairo.set_line_width(canvas.context, canvas.cairoscalef) Cairo.set_source_rgba(canvas.context, color.r / 255, color.g / 255, color.b / 255, 1) + # draw '~', length 7, width 2 Cairo.move_to(canvas.context, 0, 0) Cairo.line_to(canvas.context, 0.5, 0) Cairo.line_to(canvas.context, 1, 1) @@ -331,13 +341,14 @@ end function drawwave!(canvas::CairoCanvas, seg, ucolor, vcolor) ucolor == vcolor && return drawwave!(canvas, seg, ucolor) d = distance(seg) - scalef = Point2D(d / 7, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 2 * canvas.scaleunit * canvas.cairoscalef) rotatef = unitvector(seg) translf = seg.u Cairo.save(canvas.context) cairo_transform!(canvas.context, scalef, rotatef, translf) - Cairo.set_line_width(canvas.context, canvas.fontscalef) + Cairo.set_line_width(canvas.context, canvas.cairoscalef) Cairo.set_source_rgba(canvas.context, ucolor.r / 255, ucolor.g / 255, ucolor.b / 255, 1) + # draw '~', length 7, width 2 Cairo.move_to(canvas.context, 0, 0) Cairo.line_to(canvas.context, 0.5, 0) Cairo.line_to(canvas.context, 1, 1) @@ -359,7 +370,7 @@ end function drawlinehighlight!(canvas::CairoCanvas, seg, color) - w = round(Int, 10 * canvas.fontscalef) + w = round(Int, canvas.linehlwidthf * canvas.scaleunit * canvas.cairoscalef) Cairo.set_source_rgba(canvas.context, color.r / 255, color.g / 255, color.b / 255, 1) Cairo.set_line_cap(canvas.context, Cairo.CAIRO_LINE_CAP_ROUND) Cairo.set_line_width(canvas.context, w) @@ -367,4 +378,4 @@ function drawlinehighlight!(canvas::CairoCanvas, seg, color) Cairo.line_to(canvas.context, seg.v.x, seg.v.y) Cairo.stroke(canvas.context) return -end \ No newline at end of file +end diff --git a/src/draw/draw2d.jl b/src/draw/draw2d.jl index 7632cdb8..e37820c7 100644 --- a/src/draw/draw2d.jl +++ b/src/draw/draw2d.jl @@ -240,7 +240,7 @@ function wavesingle!(canvas::Canvas, u, v, ucolor, vcolor, uvis, vvis) end function doublebond!(canvas::Canvas, u, v, ucolor, vcolor, uvis, vvis) - dist = canvas.scalef * canvas.mbwidthf / 2 + dist = canvas.scaleunit * canvas.mbwidthf / 2 seg = trimbond(canvas, Segment{Point2D}(canvas.coords, u, v), uvis, vvis) seg1 = translate(seg, pi / 2, dist) seg2 = translate(seg, -pi / 2, dist) @@ -250,7 +250,7 @@ function doublebond!(canvas::Canvas, u, v, ucolor, vcolor, uvis, vvis) end function crossdouble!(canvas::Canvas, u, v, ucolor, vcolor, uvis, vvis) - dist = canvas.scalef * canvas.mbwidthf / 2 + dist = canvas.scaleunit * canvas.mbwidthf / 2 seg = trimbond(canvas, Segment{Point2D}(canvas.coords, u, v), uvis, vvis) cw = translate(seg, pi / 2, dist) ccw = translate(seg, -pi / 2, dist) @@ -261,7 +261,7 @@ end function ringdouble!(canvas::Canvas, u, v, ucolor, vcolor, uvis, vvis, direction) seg = trimbond(canvas, Segment{Point2D}(canvas.coords, u, v), uvis, vvis) - dist = canvas.scalef * canvas.mbwidthf + dist = canvas.scaleunit * canvas.mbwidthf segin = trim_uv(translate(seg, direction, dist), canvas.triminnerf) drawline!(canvas, seg, ucolor, vcolor) drawline!(canvas, segin, ucolor, vcolor) @@ -275,7 +275,7 @@ counterdouble!(canvas::Canvas, u, v, ucolor, vcolor, uvis, vvis ) = ringdouble!(canvas, u, v, ucolor, vcolor, uvis, vvis, -pi / 2) function triplebond!(canvas::Canvas, u, v, ucolor, vcolor, uvis, vvis) - dist = canvas.scalef * canvas.mbwidthf + dist = canvas.scaleunit * canvas.mbwidthf seg = trimbond(canvas, Segment{Point2D}(canvas.coords, u, v), uvis, vvis) seg1 = translate(seg, pi / 2, dist) seg2 = translate(seg, -pi / 2, dist) diff --git a/src/draw/svg.jl b/src/draw/svg.jl index 849b0ab4..46cb7983 100644 --- a/src/draw/svg.jl +++ b/src/draw/svg.jl @@ -5,52 +5,59 @@ mutable struct SvgCanvas <: Canvas - fontweight::String - fontfamily::String - fontsize::Float64 - bgcolor::Color - bgopacity::Float64 - + scaleunit::Float64 mbwidthf::Float64 wedgewidthf::Float64 wavewidthf::Float64 triminnerf::Float64 trimoverlapf::Float64 + linehlwidthf::Float64 annotsizef::Float64 - scalef::Float64 - paddingX::Float64 - paddingY::Float64 + hlsizef::Float64 + paddingXf::Float64 + paddingYf::Float64 + + fontweight::String + fontfamily::String + fontsize::Float64 + bgcolor::Color + bgopacity::Float64 viewboxW::Float64 viewboxH::Float64 - bgelements::Vector{String} elements::Vector{String} coords::Matrix{Float64} - valid::Bool function SvgCanvas(bgcolor::Color, bgopacity::Float64) canvas = new() - canvas.fontweight = "normal" - canvas.fontfamily = "Helvetica" - canvas.fontsize = 14.0 - canvas.bgcolor = bgcolor - canvas.bgopacity = bgopacity + # Geometry + canvas.scaleunit = 30.0 canvas.mbwidthf = 0.15 canvas.wedgewidthf = 0.3 canvas.wavewidthf = 0.2 canvas.triminnerf = 0.2 canvas.trimoverlapf = 0.3 + canvas.linehlwidthf = 0.3 canvas.annotsizef = 0.7 - canvas.scalef = 30.0 # suitable for fontsize=14 and default line width - canvas.paddingX = 30.0 - canvas.paddingY = 30.0 + canvas.hlsizef = 1.2 + canvas.paddingXf = 1.0 + canvas.paddingYf = 1.0 + + # Appearance + canvas.fontweight = "normal" + canvas.fontfamily = "Helvetica" + canvas.fontsize = 14.0 + canvas.bgcolor = bgcolor + canvas.bgopacity = bgopacity + # Canvas state canvas.viewboxW = 1.0 canvas.viewboxH = 1.0 canvas.bgelements = [] canvas.elements = [] + return canvas end end @@ -167,12 +174,12 @@ Base.show(io::IO, m::MIME"text/html", mols::Vector{<:SimpleMolGraph{<:Integer,<: function initcanvas!( canvas::SvgCanvas, coords::AbstractArray{Float64}, boundary::Tuple) (top, left, width, height, unit) = boundary - sf = canvas.scalef / unit - pd = [canvas.paddingX canvas.paddingY] + sf = canvas.scaleunit / unit + pd = [canvas.paddingXf canvas.paddingYf] * canvas.scaleunit canvas.coords = (coords .- [left top]) .* [1 -1] .* sf .+ pd - canvas.viewboxW = width * sf + canvas.paddingX * 2 - canvas.viewboxH = height * sf + canvas.paddingY * 2 - canvas.valid = true + viewbox = ([width height] * sf) .+ (pd * 2) + canvas.viewboxW = viewbox[1] + canvas.viewboxH = viewbox[2] return end @@ -227,7 +234,7 @@ function drawtextannot!(canvas::SvgCanvas, pos, text, color, bgcolor) end function drawtexthighlight!(canvas::SvgCanvas, pos, color) - size = round(Int, canvas.fontsize * 1.2) + size = round(Int, canvas.fontsize * canvas.hlsizef) xy = svgcoords(pos - (size / 2, size / 2)) c = svgcolor(color) elem = """ @@ -269,13 +276,13 @@ drawdashedline!(canvas::SvgCanvas, seg, ucolor, vcolor) = drawline!( function drawwedge!(canvas::SvgCanvas, seg, color) """ u ◀︎ v """ d = distance(seg) - scalef = Point2D(d, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d, canvas.wedgewidthf / 2 * canvas.scaleunit) rotatef = unitvector(seg) translf = seg.u svgtf = svgtransform(transformmatrix(scalef, rotatef, translf)) c = svgcolor(color) elem = """ - """ + """ # length: 1, width: 2 push!(canvas.elements, elem) return end @@ -284,7 +291,7 @@ function drawwedge!(canvas::SvgCanvas, seg, ucolor, vcolor) """ u ◀︎ v """ ucolor == vcolor && return drawwedge!(canvas, seg, ucolor) d = distance(seg) - scalef = Point2D(d, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d, canvas.wedgewidthf / 2 * canvas.scaleunit) rotatef = unitvector(seg) translf = seg.u svgtf = svgtransform(transformmatrix(scalef, rotatef, translf)) @@ -294,7 +301,7 @@ function drawwedge!(canvas::SvgCanvas, seg, ucolor, vcolor) - """ + """ # length: 1, width: 2 push!(canvas.elements, elem) return end @@ -303,7 +310,7 @@ end function drawdashedwedge!(canvas::SvgCanvas, seg, color) """ u ◁ v """ d = distance(seg) - scalef = Point2D(d / 7, d / 14 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 16 * canvas.scaleunit) rotatef = unitvector(seg) translf = seg.u svgtf = svgtransform(transformmatrix(scalef, rotatef, translf)) @@ -318,7 +325,7 @@ function drawdashedwedge!(canvas::SvgCanvas, seg, color) - """ + """ # length: 7, width: 16 push!(canvas.elements, elem) return end @@ -327,7 +334,7 @@ function drawdashedwedge!(canvas::SvgCanvas, seg, ucolor, vcolor) """ u ◁ v """ ucolor == vcolor && return drawdashedwedge!(canvas, seg, ucolor) d = distance(seg) - scalef = Point2D(d / 7, d / 14 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 16 * canvas.scaleunit) rotatef = unitvector(seg) translf = seg.u svgtf = svgtransform(transformmatrix(scalef, rotatef, translf)) @@ -343,7 +350,7 @@ function drawdashedwedge!(canvas::SvgCanvas, seg, ucolor, vcolor) - """ + """ # length: 7, width: 16 push!(canvas.elements, elem) return end @@ -351,14 +358,14 @@ end function drawwave!(canvas::SvgCanvas, seg, color) d = distance(seg) - scalef = Point2D(d / 7, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 2 * canvas.scaleunit) rotatef = unitvector(seg) translf = seg.u svgtf = svgtransform(transformmatrix(scalef, rotatef, translf)) c = svgcolor(color) elem = """ - """ + """ # length: 7, width: 2 push!(canvas.elements, elem) return end @@ -366,7 +373,7 @@ end function drawwave!(canvas::SvgCanvas, seg, ucolor, vcolor) ucolor == vcolor && return drawwave!(canvas, seg, ucolor) d = distance(seg) - scalef = Point2D(d / 7, d / 2 * canvas.wedgewidthf) + scalef = Point2D(d / 7, canvas.wedgewidthf / 2 * canvas.scaleunit) rotatef = unitvector(seg) translf = seg.u svgtf = svgtransform(transformmatrix(scalef, rotatef, translf)) @@ -376,7 +383,7 @@ function drawwave!(canvas::SvgCanvas, seg, ucolor, vcolor) - """ + """ # length: 7, width: 2 push!(canvas.elements, elem) return end @@ -385,7 +392,8 @@ end function drawlinehighlight!(canvas::SvgCanvas, seg, color) cds = svgcoords(seg) c = svgcolor(color) - elem = """ + w = canvas.linehlwidthf * canvas.scaleunit + elem = """ """ push!(canvas.bgelements, elem) return