Skip to content

Commit

Permalink
Started working updating vsl.plot
Browse files Browse the repository at this point in the history
  • Loading branch information
ulises-jeremias committed Oct 21, 2023
1 parent 708b7cd commit 965fe80
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 84 deletions.
10 changes: 5 additions & 5 deletions plot/annotation.v
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ pub struct Annotation {
pub mut:
x f64
y f64
text string [omitempty]
showarrow bool [omitempty]
arrowhead int [omitempty]
arrowcolor string [omitempty]
align string [omitempty]
text string
showarrow bool
arrowhead int
arrowcolor string
align string
font Font
}
8 changes: 4 additions & 4 deletions plot/layout.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ module plot
pub struct Layout {
pub mut:
title string
title_x f64 = 0.5
autosize bool = true
width int = 550
height int = 550
title_x f64
autosize bool
width int = 550
height int = 550
xaxis Axis
yaxis Axis
annotations []Annotation
Expand Down
171 changes: 99 additions & 72 deletions plot/show.v
Original file line number Diff line number Diff line change
Expand Up @@ -2,118 +2,145 @@ module plot

import json
import net
import net.html
import net.http
import os
import time

type TracesWithTypeValue = Trace | string

struct PlotlyHandler {
plot Plot
mut:
server &http.Server [str: skip] = unsafe { nil }
}

fn (mut handler PlotlyHandler) handle(req http.Request) http.Response {
mut r := http.Response{
body: handler.plot.plotly()
header: req.header
}
r.set_status(.ok)
r.set_version(req.version)
go fn [mut handler] () {
time.sleep(300 * time.millisecond)
handler.server.close()
}()
return r
// PlotConfig is a configuration for the Plotly plot.
[params]
pub struct PlotConfig {
use_cdn bool
}

// show starts a web server and opens a browser window to display the plot.
pub fn (plot Plot) show() ! {
pub fn (plot Plot) show(config PlotConfig) ! {
$if test ? {
println('Ignoring plot.show() because we are running in test mode')
} $else {
mut handler := PlotlyHandler{
use_cdn: true
plot: plot
}
listener := net.listen_tcp(net.AddrFamily.ip, ':0')!
mut server := &http.Server{
accept_timeout: 1 * time.second
listener: listener
port: 0
handler: handler
}
handler.server = server
t := spawn server.listen_and_serve()
for server.status() != .running {
time.sleep(10 * time.millisecond)
}
server.wait_till_running()!
os.open_uri('http://${server.addr}')!
t.wait()
}
}

// TODO: This is a hack to allow the json encoder to work with sum types
fn encode[T](obj T) string {
strings_to_replace := [
',"[]f64"',
'"[]f64"',
',"[]string"',
'"[]string"',
]
mut obj_json := json.encode(obj)
for string_to_replace in strings_to_replace {
obj_json = obj_json.replace(string_to_replace, '')
}
return obj_json
// Plot is a plotly plot.
type TracesWithTypeValue = Trace | string

// PlotlyScriptConfig is a configuration for the Plotly plot script.
[params]
pub struct PlotlyScriptConfig {
PlotConfig
}

fn (plot Plot) plotly() string {
// get_plotly_script returns the plot script as an html tag.
pub fn (plot Plot) get_plotly_script(element_id string, config PlotlyScriptConfig) &html.Tag {
traces_with_type := plot.traces.map({
'type': TracesWithTypeValue(it.trace_type())
'trace': TracesWithTypeValue(it)
})
traces_with_type_json := encode(traces_with_type)
layout_json := encode(plot.layout)

plot_script := &html.Tag{
name: 'script'
attributes: {
'type': 'module'
}
content: 'import "https://cdn.plot.ly/plotly-2.26.2.min.js";
function removeEmptyFieldsDeeply(obj) {
if (Array.isArray(obj)) {
return obj.map(removeEmptyFieldsDeeply);
}
if (typeof obj === "object") {
const newObj = Object.fromEntries(
Object.entries(obj)
.map(([key, value]) => [key, removeEmptyFieldsDeeply(value)])
.filter(([_, value]) => !!value)
);
return Object.keys(newObj).length > 0 ? newObj : undefined;
}
return obj;
}
const layout = ${layout_json};
const traces_with_type_json = ${traces_with_type_json};
const data = [...traces_with_type_json]
.map(({ type, trace: { CommonTrace, _type, ...trace } }) => ({ type, ...CommonTrace, ...trace }));
const payload = {
data: removeEmptyFieldsDeeply(data),
layout: removeEmptyFieldsDeeply(layout),
};
Plotly.newPlot("${element_id}", payload);'
}

return plot_script
}

fn (plot Plot) get_html(element_id string, config PlotConfig) string {
title := if plot.layout.title == '' { 'VSL Plot' } else { plot.layout.title }
plot_script := plot.get_plotly_script(element_id, use_cdn: config.use_cdn)

return '<!DOCTYPE html>
<html>
<head>
<title>VSL Plot</title>
<title>${title}</title>
</head>
<body>
<div id="gd"></div>
<script type="module">
import "https://cdn.plot.ly/plotly-2.26.2.min.js";
function removeEmptyFieldsDeeply(obj) {
if (Array.isArray(obj)) {
return obj.map(removeEmptyFieldsDeeply);
}
if (typeof obj === "object") {
const newObj = Object.fromEntries(
Object.entries(obj)
.map(([key, value]) => [key, removeEmptyFieldsDeeply(value)])
.filter(([_, value]) => !!value)
);
return Object.keys(newObj).length > 0 ? newObj : undefined;
}
return obj;
}
const layout = ${layout_json};
const traces_with_type_json = ${traces_with_type_json};
const data = [...traces_with_type_json]
.map(({ type, trace: { CommonTrace, _type, ...trace } }) => ({ type, ...CommonTrace, ...trace }));
const payload = {
data: removeEmptyFieldsDeeply(data),
layout: removeEmptyFieldsDeeply(layout),
};
Plotly.newPlot("gd", payload);
</script>
<div id="${element_id}"></div>
${*plot_script}
</body>
</html>'
}

struct PlotlyHandler {
PlotlyScriptConfig
plot Plot
mut:
server &http.Server [str: skip] = unsafe { nil }
}

fn (mut handler PlotlyHandler) handle(req http.Request) http.Response {
mut r := http.Response{
body: handler.plot.get_html('gd', use_cdn: handler.use_cdn)
header: req.header
}
r.set_status(.ok)
r.set_version(req.version)
go fn [mut handler] () {
time.sleep(300 * time.millisecond)
handler.server.close()
}()
return r
}

// TODO: This is a hack to allow the json encoder to work with sum types
fn encode[T](obj T) string {
strings_to_replace := [
',"[]f64"',
'"[]f64"',
',"[]string"',
'"[]string"',
]
mut obj_json := json.encode(obj)
for string_to_replace in strings_to_replace {
obj_json = obj_json.replace(string_to_replace, '')
}
return obj_json
}
8 changes: 8 additions & 0 deletions plot/static/plotly-2.26.2.min.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions plot/trace.v
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ pub enum TraceType {
}

// XType is a type for x-axis data
pub type XType = []f64 | []string
pub type XType = []f64 | []int | []string

// YType is a type for y-axis data
pub type YType = []f64 | []string
pub type YType = []f64 | []int | []string

// ZType is a type for z-axis data
pub type ZType = [][]f64 | []f64
pub type ZType = [][]f64 | [][]int | []f64 | []int

// CommonTrace is a struct for common trace properties
[params]
Expand Down

0 comments on commit 965fe80

Please sign in to comment.