mirror of
https://gitlab.kit.edu/uskyk/kv.git
synced 2024-09-07 21:21:51 +00:00
WASM port + misc. changes
This commit is contained in:
parent
24139814cc
commit
4b0541c67a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/target
|
||||
/dist
|
||||
|
166
Cargo.lock
generated
166
Cargo.lock
generated
@ -1,5 +1,43 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
|
||||
dependencies = [
|
||||
"log",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
@ -15,14 +53,142 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kv"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"itertools",
|
||||
"log",
|
||||
"svg_fmt",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svg_fmt"
|
||||
version = "0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -7,5 +7,10 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1.6"
|
||||
console_log = "0.2.0"
|
||||
itertools = "0.10.0"
|
||||
log = "0.4.14"
|
||||
svg_fmt = { path = "../rust_debug/svg_fmt" }
|
||||
wasm-bindgen = "0.2.71"
|
||||
web-sys = { version = "0.3.48", features = ["Window", "Document", "Location", "HtmlCollection", "HtmlElement", "CssStyleDeclaration", "HtmlTextAreaElement", "HtmlInputElement"] }
|
||||
|
23
base.js
Normal file
23
base.js
Normal file
@ -0,0 +1,23 @@
|
||||
function handleTextChange(event) {
|
||||
const target = event.target;
|
||||
const value = target.value;
|
||||
if (value.length == 0) {
|
||||
target.value = "-";
|
||||
} else {
|
||||
target.value = value.substr(value.length - 1);
|
||||
}
|
||||
}
|
||||
function handleTextScroll(event) {
|
||||
const target = event.target;
|
||||
const value = target.value;
|
||||
if (event.deltaY < 0) {
|
||||
target.value = "1"; // scroll up
|
||||
} else {
|
||||
target.value = "0"; // scroll down
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
export function attachTypeListener(element) {
|
||||
element.addEventListener("input", handleTextChange);
|
||||
element.addEventListener("wheel", handleTextScroll);
|
||||
}
|
49
index.html
Normal file
49
index.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<title>KV diagram calculator</title>
|
||||
<link data-trunk rel="rust" data-bin="svg-wasm"/>
|
||||
<style>
|
||||
textarea {
|
||||
resize: none;
|
||||
font-size: 32px;
|
||||
line-height: 64px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
padding-left: 24px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
#input-container {
|
||||
transform: translate(0px, 0px); /* new absolute context */
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
.solution {
|
||||
display: inline-block;
|
||||
}
|
||||
.solution span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="settings">
|
||||
<label>Number of variables: <input type="number" value="3" id="variables"></label><button id="apply-variables">Apply</button>
|
||||
</div>
|
||||
<p id="hints">
|
||||
Change cells by scrolling (up => 1, down => 0), typing (=> 1, 0) or deleting (=> -).
|
||||
</p>
|
||||
<p id="examples">
|
||||
Load <a href="#--111100">example 1</a>, <a href="#-1111000-1-01---">example 2</a>, <a href="#11101111111111111111101111111101">example 3</a>.
|
||||
</p>
|
||||
<div id="input-container"></div>
|
||||
<button id="calculate">Run</button>
|
||||
<div id="output-help"></div>
|
||||
<div id="output-container"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
243
src/bin/svg-wasm.rs
Normal file
243
src/bin/svg-wasm.rs
Normal file
@ -0,0 +1,243 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use log::{Level, info};
|
||||
|
||||
use kv::*;
|
||||
use web_sys::{EventTarget, HtmlElement, HtmlInputElement, HtmlTextAreaElement};
|
||||
|
||||
macro_rules! web {
|
||||
($($x:ident)*) => {
|
||||
web_sys::window().unwrap()$(.$x().unwrap())*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! web2 {
|
||||
($($x:ident)*) => {
|
||||
web_sys::window().unwrap()$(.$x())*
|
||||
}
|
||||
}
|
||||
|
||||
const HINT_TEXT: &'static str = "Output: core blocks, prime blocks, solution 1, solution 2, ... (solutions only displayed if not equal to core or prime blocks)";
|
||||
|
||||
fn main() {
|
||||
console_log::init_with_level(Level::Debug).unwrap();
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
info!("init");
|
||||
init_settings();
|
||||
if !parse_hash() {
|
||||
let grid = grid(get_var_number());
|
||||
update_input_grid(&grid);
|
||||
}
|
||||
let closure = Closure::wrap(Box::new(|| { parse_hash(); }) as Box<dyn FnMut()>);
|
||||
web!().add_event_listener_with_callback("hashchange", closure.as_ref().unchecked_ref()).unwrap();
|
||||
closure.forget();
|
||||
}
|
||||
|
||||
fn parse_hash() -> bool {
|
||||
let hash = web!(document location hash);
|
||||
if hash.len() >= 2 {
|
||||
let func_spec = &hash[1..];
|
||||
let var_count = (func_spec.len() as f32).log2() as usize;
|
||||
if 2usize.pow(var_count as u32) == func_spec.len() {
|
||||
set_var_number(var_count);
|
||||
let grid = grid(var_count);
|
||||
update_input_grid(&grid);
|
||||
set_input_function(func_spec);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[wasm_bindgen(module = "/base.js")]
|
||||
extern "C" {
|
||||
pub fn attachTypeListener(element: &HtmlElement);
|
||||
}
|
||||
|
||||
fn get_output_container() -> HtmlElement {
|
||||
web!(document).get_element_by_id("output-container").unwrap().unchecked_into::<HtmlElement>()
|
||||
}
|
||||
|
||||
fn get_output_help() -> HtmlElement {
|
||||
web!(document).get_element_by_id("output-help").unwrap().unchecked_into::<HtmlElement>()
|
||||
}
|
||||
|
||||
fn p(text: &str) -> HtmlElement {
|
||||
let p = web!(document).create_element("p").unwrap().unchecked_into::<HtmlElement>();
|
||||
p.set_inner_text(text);
|
||||
p
|
||||
}
|
||||
|
||||
fn span(text: &str) -> HtmlElement {
|
||||
let p = web!(document).create_element("span").unwrap().unchecked_into::<HtmlElement>();
|
||||
p.set_inner_text(text);
|
||||
p
|
||||
}
|
||||
|
||||
pub fn run() {
|
||||
let document = web!(document);
|
||||
|
||||
let var_count = get_var_number();
|
||||
let vars = (0..var_count).map(|x| 2usize.pow(x as u32)).collect::<Vec<_>>();
|
||||
let function: FunctionSpec = (0..2usize.pow(var_count as u32)).map(|idx| get_input(idx)).collect();
|
||||
info!("function: {:?}", function);
|
||||
let hash = Some('#').into_iter().chain(function.iter().map::<char, _>(|&x| x.into())).collect::<String>();
|
||||
web2!(location).set_hash(&hash).unwrap();
|
||||
let groups = find_groups(&function, &vars, One);
|
||||
let (prime, other) = find_core(&function, &vars, One, &groups);
|
||||
let solutions = real_solutions_idxed(function.clone(), &vars, One, &prime, &other);
|
||||
|
||||
let output_container = get_output_container();
|
||||
if output_container.children().length() == 0 {
|
||||
let output_help = get_output_help();
|
||||
output_help.append_child(&p(HINT_TEXT)).unwrap();
|
||||
output_container.append_child(&document.create_element("hr").unwrap()).unwrap();
|
||||
}
|
||||
let output_sub = document.create_element("div").unwrap().unchecked_into::<HtmlElement>();
|
||||
|
||||
let (_grid, w, h) = grid(var_count);
|
||||
|
||||
macro_rules! render_svg {
|
||||
($sol:expr, $mask:expr) => {
|
||||
let svg = svg::get_svg(if var_count < 5 { 64 } else { 80 }, &function, &vars, $mask);
|
||||
let svg_container = document.create_element("div").unwrap();
|
||||
svg_container.set_class_name("solution");
|
||||
svg_container.set_inner_html(&svg);
|
||||
let svg = svg_container.children().item(0).unwrap().unchecked_into::<HtmlElement>();
|
||||
svg.style().set_property("width", &format!("{}px", w * 80)).unwrap();
|
||||
svg.style().set_property("height", &format!("{}px", h * 80)).unwrap();
|
||||
let mut text = String::new();
|
||||
for x in $sol {
|
||||
if !text.is_empty() {
|
||||
text += " ∨ ";
|
||||
}
|
||||
text += &print_implicant(x);
|
||||
}
|
||||
if text.is_empty() {
|
||||
text += ".";
|
||||
}
|
||||
svg_container.append_child(&span(&text)).unwrap();
|
||||
output_sub.append_child(&svg_container).unwrap();
|
||||
};
|
||||
}
|
||||
|
||||
let mut block_masks: Vec<_> = prime.iter()
|
||||
.map(block_to_mask)
|
||||
.collect();
|
||||
render_svg!(&prime, &block_masks);
|
||||
|
||||
block_masks = prime.iter()
|
||||
.map(block_to_mask)
|
||||
.chain(other.iter().map(block_to_mask)) // also display non-core
|
||||
.collect();
|
||||
render_svg!(prime.iter().chain(other.iter()), &block_masks);
|
||||
|
||||
let mut size = usize::MAX;
|
||||
for sol in &solutions {
|
||||
if sol.len() > size {
|
||||
break;
|
||||
}
|
||||
size = sol.len();
|
||||
block_masks = prime.iter()
|
||||
.map(block_to_mask)
|
||||
.chain((0..other.len()).map(|idx| {
|
||||
if sol.contains(&idx) {
|
||||
block_to_mask(&other[idx])
|
||||
} else {
|
||||
(!0, 0)
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
let blocks = prime.iter()
|
||||
.chain(sol.iter().map(|&i| &other[i]));
|
||||
render_svg!(blocks, &block_masks);
|
||||
}
|
||||
|
||||
output_container.prepend_with_node_1(&output_sub).unwrap();
|
||||
output_container.prepend_with_node_1(&document.create_element("hr").unwrap()).unwrap();
|
||||
}
|
||||
|
||||
fn set_var_number(x: usize) {
|
||||
get_var_number_el().set_value(&x.to_string());
|
||||
}
|
||||
|
||||
fn init_settings() {
|
||||
let calculate = web!(document).get_element_by_id("calculate").unwrap().unchecked_into::<EventTarget>();
|
||||
let closure = Closure::wrap(Box::new(|| run()) as Box<dyn FnMut()>);
|
||||
calculate.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()).unwrap();
|
||||
closure.forget();
|
||||
let apply_count = web!(document).get_element_by_id("apply-variables").unwrap().unchecked_into::<EventTarget>();
|
||||
let closure = Closure::wrap(Box::new(|| update_input_grid(&grid(get_var_number()))) as Box<dyn FnMut()>);
|
||||
apply_count.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()).unwrap();
|
||||
closure.forget();
|
||||
}
|
||||
|
||||
fn get_var_number_el() -> HtmlInputElement {
|
||||
web!(document).get_element_by_id("variables").unwrap().unchecked_into()
|
||||
}
|
||||
|
||||
fn get_var_number() -> usize {
|
||||
get_var_number_el().value().parse().unwrap()
|
||||
}
|
||||
|
||||
fn get_input_container() -> HtmlElement {
|
||||
web!(document).get_element_by_id("input-container").unwrap().unchecked_into::<HtmlElement>()
|
||||
}
|
||||
|
||||
fn get_input_el(idx: usize) -> Option<HtmlElement> {
|
||||
web!(document).get_element_by_id(&format!("input{}", idx)).map(|x| x.unchecked_into::<HtmlElement>())
|
||||
}
|
||||
|
||||
fn create_input_el(idx: usize, x: usize, y: usize) -> HtmlElement {
|
||||
let input = web!(document).create_element("textarea").unwrap().unchecked_into::<HtmlTextAreaElement>();
|
||||
input.set_attribute("id", &format!("input{}", idx)).unwrap();
|
||||
input.set_value("-");
|
||||
let style = input.style();
|
||||
style.set_property("position", "absolute").unwrap();
|
||||
style.set_property("left", &format!("{}px", x)).unwrap();
|
||||
style.set_property("top", &format!("{}px", y)).unwrap();
|
||||
get_input_container().append_child(&input).unwrap();
|
||||
attachTypeListener(&input);
|
||||
input.into()
|
||||
}
|
||||
|
||||
fn get_input(idx: usize) -> Output {
|
||||
let el = get_input_el(idx).unwrap().unchecked_into::<HtmlTextAreaElement>();
|
||||
match &*el.value() {
|
||||
"0" => Zero,
|
||||
"1" => One,
|
||||
_ => Any
|
||||
}
|
||||
}
|
||||
|
||||
fn set_input(idx: usize, out: Output) {
|
||||
let el = get_input_el(idx).unwrap().unchecked_into::<HtmlTextAreaElement>();
|
||||
el.set_value(&out.to_string());
|
||||
}
|
||||
|
||||
fn set_input_function(func: &str) {
|
||||
for i in 0..func.len() {
|
||||
set_input(i, func[i..i+1].parse().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// also set in style
|
||||
const GRID_SPACING: usize = 64;
|
||||
|
||||
fn update_input_grid((grid, w, h): &(Vec<(usize, usize)>, usize, usize)) {
|
||||
let input_container = get_input_container();
|
||||
input_container.style().set_property("width", &format!("{}px", w * 64)).unwrap();
|
||||
input_container.style().set_property("height", &format!("{}px", h * 64)).unwrap();
|
||||
for i in 0..grid.len() {
|
||||
if get_input_el(i).is_none() {
|
||||
create_input_el(i, grid[i].0 * GRID_SPACING, grid[i].1 * GRID_SPACING);
|
||||
}
|
||||
}
|
||||
for i in grid.len().. {
|
||||
if let Some(el) = get_input_el(i) {
|
||||
el.remove();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
109
src/bin/svg.rs
109
src/bin/svg.rs
@ -1,32 +1,23 @@
|
||||
use std::env::args;
|
||||
|
||||
use svg_fmt::*;
|
||||
|
||||
use kv::*;
|
||||
|
||||
const SIZE_FACTOR: usize = 64;
|
||||
const SIZE_FONT: f32 = 20.0;
|
||||
const SIZE4: usize = SIZE_FACTOR / 4;
|
||||
|
||||
const COLORS: &'static [Color] = &[
|
||||
rgb(0, 0, 255),
|
||||
rgb(255, 0, 0),
|
||||
rgb(0, 255, 0),
|
||||
rgb(255, 255, 0),
|
||||
];
|
||||
|
||||
fn main() {
|
||||
let args = args().collect::<Vec<_>>();
|
||||
|
||||
let vars = [A, B, C];
|
||||
let function = vec![0, 0, 1, 1, 1, 1, 1, 0].into_iter().map(Into::into).collect();
|
||||
let vars = [A, B, C, D];
|
||||
//let function = vec![0, 0, 1, 1, 1, 1, 1, 0].into_iter().map(Into::into).collect();
|
||||
//let function = vec![0, 0, 1, 1, 1, 1, 1, 2].into_iter().map(Into::into).collect();
|
||||
let function = parse_function("-1111000-1-01---");
|
||||
let groups = find_groups(&function, &vars, One);
|
||||
eprintln!("all:");
|
||||
for x in &groups {
|
||||
eprintln!("{}", print_implicant(x));
|
||||
}
|
||||
eprintln!("prime:");
|
||||
let (prime, other) = find_prime(&function, &vars, One, &groups);
|
||||
let (prime, other) = find_core(&function, &vars, One, &groups);
|
||||
for x in &prime {
|
||||
eprintln!("{}", print_implicant(x));
|
||||
}
|
||||
@ -54,93 +45,5 @@ fn main() {
|
||||
.collect()
|
||||
};
|
||||
|
||||
let (grid, w, h) = grid(3);
|
||||
println!("{}", BeginSvg { w: w * SIZE_FACTOR + 3, h: h * SIZE_FACTOR + 3 });
|
||||
for (i, &(x, y)) in grid.iter().enumerate() {
|
||||
println!("{}",
|
||||
rectangle(1 + x * SIZE_FACTOR, 1 + y * SIZE_FACTOR, SIZE_FACTOR, SIZE_FACTOR)
|
||||
.fill(white())
|
||||
.stroke(Stroke::Color(black(), 2.0))
|
||||
);
|
||||
let mut rect = rectangle(1 + x * SIZE_FACTOR, 1 + y * SIZE_FACTOR, SIZE_FACTOR, SIZE_FACTOR)
|
||||
.fill(white())
|
||||
.stroke_opacity(0.7)
|
||||
.inflate(-1, -1);
|
||||
let mut ident = 1;
|
||||
for (idx, &(mask, inv_mask)) in block_masks.iter().enumerate() {
|
||||
rect = rect.inflate(-2, -2);
|
||||
ident += 2;
|
||||
if check_mask(i, mask, inv_mask) {
|
||||
// check cells around this one
|
||||
let mut up = false;
|
||||
let mut down = false;
|
||||
let mut left = false;
|
||||
let mut right = false;
|
||||
for &var in &vars {
|
||||
let j = i ^ var;
|
||||
if check_mask(j, mask, inv_mask) {
|
||||
let (x2, y2) = grid[j];
|
||||
if (x + 1) % w == x2 && y == y2 {
|
||||
right = true;
|
||||
}
|
||||
if x == x2 && (y + 1) % h == y2 {
|
||||
down = true;
|
||||
}
|
||||
if x == x2 && y == (y2 + 1) % h {
|
||||
up = true;
|
||||
}
|
||||
if x == (x2 + 1) % w && y == y2 {
|
||||
left = true;
|
||||
}
|
||||
if !up && !down && !left && !right {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut sides = rect.sides();
|
||||
for s in &mut sides {
|
||||
*s = s.opacity(0.9);
|
||||
}
|
||||
if up {
|
||||
println!("{}", sides[0].width(2.0).color(white()));
|
||||
println!("{}", sides[2].width(2.0).color(COLORS[idx]).offset(0.0, -ident - 1));
|
||||
println!("{}", sides[3].width(2.0).color(COLORS[idx]).offset(0.0, -ident - 1));
|
||||
} else {
|
||||
println!("{}", sides[0].width(2.0).color(COLORS[idx]));
|
||||
}
|
||||
if down {
|
||||
println!("{}", sides[1].width(2.0).color(white()));
|
||||
println!("{}", sides[2].width(2.0).color(COLORS[idx]).offset(0.0, ident + 1));
|
||||
println!("{}", sides[3].width(2.0).color(COLORS[idx]).offset(0.0, ident + 1));
|
||||
} else {
|
||||
println!("{}", sides[1].width(2.0).color(COLORS[idx]));
|
||||
}
|
||||
if left {
|
||||
println!("{}", sides[2].width(2.0).color(white()));
|
||||
println!("{}", sides[0].width(2.0).color(COLORS[idx]).offset(-ident - 1, 0));
|
||||
println!("{}", sides[1].width(2.0).color(COLORS[idx]).offset(-ident - 1, 0));
|
||||
} else {
|
||||
println!("{}", sides[2].width(2.0).color(COLORS[idx]));
|
||||
}
|
||||
if right {
|
||||
println!("{}", sides[3].width(1.0).color(white()));
|
||||
println!("{}", sides[0].width(2.0).color(COLORS[idx]).offset(ident + 1, 0));
|
||||
println!("{}", sides[1].width(2.0).color(COLORS[idx]).offset(ident + 1, 0));
|
||||
} else {
|
||||
println!("{}", sides[3].width(2.0).color(COLORS[idx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{}",
|
||||
text((x+1) * SIZE_FACTOR - SIZE4, (y+1) * SIZE_FACTOR - SIZE4 / 3, i.to_string())
|
||||
.size(SIZE_FONT)
|
||||
.color(black())
|
||||
);
|
||||
println!("{}",
|
||||
text(x * SIZE_FACTOR + SIZE4, (y+1) * SIZE_FACTOR - SIZE4, function[i].to_string())
|
||||
.size(SIZE_FONT * 2.0)
|
||||
.color(black())
|
||||
);
|
||||
}
|
||||
println!("{}", EndSvg);
|
||||
println!("{}", svg::get_svg(SIZE_FACTOR, &function, &vars, &block_masks));
|
||||
}
|
||||
|
164
src/lib.rs
164
src/lib.rs
@ -1,8 +1,9 @@
|
||||
use std::fmt;
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
mod ui;
|
||||
use log::{debug, trace, warn};
|
||||
pub use ui::*;
|
||||
|
||||
pub type Variable = usize;
|
||||
@ -12,6 +13,8 @@ pub type BlockRef<'a> = &'a [(Variable, Output)];
|
||||
pub const A: Variable = 1 << 0;
|
||||
pub const B: Variable = 1 << 1;
|
||||
pub const C: Variable = 1 << 2;
|
||||
pub const D: Variable = 1 << 3;
|
||||
pub const E: Variable = 1 << 4;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Output {
|
||||
@ -25,45 +28,86 @@ pub use Output::*;
|
||||
impl Output {
|
||||
fn to_num(self) -> usize {
|
||||
match self {
|
||||
Zero => 0,
|
||||
One => 1,
|
||||
Any => todo!()
|
||||
Zero => 0,
|
||||
One => 1,
|
||||
Any => todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn invert(self) -> Self {
|
||||
match self {
|
||||
Zero => One,
|
||||
One => Zero,
|
||||
Any => Any
|
||||
Zero => One,
|
||||
One => Zero,
|
||||
Any => Any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Output {
|
||||
fn from(x: usize) -> Self {
|
||||
match x {
|
||||
fn from(x: usize) -> Self {
|
||||
match x {
|
||||
0 => Zero,
|
||||
1 => One,
|
||||
_ => todo!()
|
||||
_ => Any
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Output {
|
||||
fn from(x: char) -> Self {
|
||||
match x {
|
||||
'0' => Zero,
|
||||
'1' => One,
|
||||
_ => Any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Output {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"0" => Zero,
|
||||
"1" => One,
|
||||
_ => Any
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Output {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Zero => write!(f, "0"),
|
||||
One => write!(f, "1"),
|
||||
Any => write!(f, "-")
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Zero => write!(f, "0"),
|
||||
One => write!(f, "1"),
|
||||
Any => write!(f, "-")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<char> for Output {
|
||||
fn into(self) -> char {
|
||||
match self {
|
||||
Zero => '0',
|
||||
One => '1',
|
||||
Any => '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_function(func: &str) -> FunctionSpec {
|
||||
func.chars().map(|x| x.into()).collect()
|
||||
}
|
||||
|
||||
pub type FunctionSpec = Vec<Output>;
|
||||
|
||||
pub fn find_groups(func: &FunctionSpec, vars: &[Variable], typ: Output) -> Vec<Vec<(Variable, Output)>> {
|
||||
let mut groups: Vec<Vec<(Variable, Output)>> = Vec::new();
|
||||
// k = 0
|
||||
if func.iter().all(|&x| x != typ.invert()) {
|
||||
groups.push(vec![]);
|
||||
return groups;
|
||||
}
|
||||
for k in 1..vars.len()+1 {
|
||||
for vars in vars.iter().combinations(k) {
|
||||
for var_bits in 0..(1 << k) {
|
||||
@ -82,15 +126,18 @@ pub fn find_groups(func: &FunctionSpec, vars: &[Variable], typ: Output) -> Vec<V
|
||||
inv_mask ^= if ((var_bits >> i) & 1) == 1 { 0 } else { var };
|
||||
}
|
||||
let mut fits = true;
|
||||
let mut found_typ = false;
|
||||
for input in 0..func.len() {
|
||||
if check_mask(input, mask, inv_mask) {
|
||||
if func[input] == typ.invert() {
|
||||
fits = false;
|
||||
break;
|
||||
} else if func[input] == typ {
|
||||
found_typ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if fits {
|
||||
if fits && found_typ {
|
||||
groups.push(var_setting);
|
||||
}
|
||||
}
|
||||
@ -99,7 +146,7 @@ pub fn find_groups(func: &FunctionSpec, vars: &[Variable], typ: Output) -> Vec<V
|
||||
groups
|
||||
}
|
||||
|
||||
pub fn find_prime<'a>(func: &FunctionSpec, vars: &[Variable], typ: Output, blocks: &'a [Block]) -> (Vec<&'a [(Variable, Output)]>, Vec<&'a [(Variable, Output)]>) {
|
||||
pub fn find_core<'a>(func: &FunctionSpec, vars: &[Variable], typ: Output, blocks: &'a [Block]) -> (Vec<&'a [(Variable, Output)]>, Vec<&'a [(Variable, Output)]>) {
|
||||
assert_eq!(func.len(), 2usize.pow(vars.len() as u32));
|
||||
|
||||
let block_masks = blocks.iter()
|
||||
@ -130,11 +177,39 @@ pub fn find_prime<'a>(func: &FunctionSpec, vars: &[Variable], typ: Output, block
|
||||
(a, b)
|
||||
}
|
||||
|
||||
pub fn all_solutions(mut func: FunctionSpec, vars: &[Variable], typ: Output, prime: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<Block>> {
|
||||
/// May return some incomplete solutions
|
||||
pub fn all_solutions(func: FunctionSpec, vars: &[Variable], typ: Output, core: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<Block>> {
|
||||
all_solutions_idxed(func, vars, typ, core, other)
|
||||
.into_iter()
|
||||
.map(|sol| sol.into_iter().map(|i| other[i].to_vec()).collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn real_solutions_idxed(func: FunctionSpec, vars: &[Variable], typ: Output, core: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<usize>> {
|
||||
let core_masks = core.iter()
|
||||
.map(block_to_mask)
|
||||
.collect::<Vec<_>>();
|
||||
let other_masks = other.iter()
|
||||
.map(block_to_mask)
|
||||
.collect::<Vec<_>>();
|
||||
let mut solutions = all_solutions_idxed(func.clone(), vars, typ, core, other);
|
||||
solutions.sort_unstable_by_key(|x| x.len());
|
||||
solutions.into_iter()
|
||||
.filter(|sol|
|
||||
(0..func.len())
|
||||
.filter(|&i| func[i] == typ)
|
||||
.all(|i|
|
||||
core_masks.iter()
|
||||
.chain(sol.iter().map(|&x| &other_masks[x]))
|
||||
.any(|&(mask, inv_mask)| check_mask(i, mask, inv_mask)))
|
||||
).collect()
|
||||
}
|
||||
|
||||
pub fn all_solutions_idxed(mut func: FunctionSpec, vars: &[Variable], typ: Output, core: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<usize>> {
|
||||
assert_eq!(func.len(), 2usize.pow(vars.len() as u32));
|
||||
|
||||
// first mark all inputs covered by prime blocks as Any
|
||||
let prime_masks = prime.iter()
|
||||
// first mark all inputs covered by core blocks as Any
|
||||
let prime_masks = core.iter()
|
||||
.map(block_to_mask)
|
||||
.collect::<Vec<_>>();
|
||||
'input: for input in 0..func.len() {
|
||||
@ -152,38 +227,46 @@ pub fn all_solutions(mut func: FunctionSpec, vars: &[Variable], typ: Output, pri
|
||||
let other_masks = other.iter()
|
||||
.map(block_to_mask)
|
||||
.collect::<Vec<_>>();
|
||||
all_recursive(func, vars, typ, other, &other_masks)
|
||||
all_recursive(func, vars, typ, other, &other_masks, 0)
|
||||
}
|
||||
|
||||
fn all_recursive(func: FunctionSpec, vars: &[Variable], typ: Output, other: &[BlockRef], other_masks: &[(Variable, Variable)]) -> Vec<Vec<Block>> {
|
||||
fn all_recursive(func: FunctionSpec, vars: &[Variable], typ: Output, other: &[BlockRef], other_masks: &[(Variable, Variable)], start: usize) -> Vec<Vec<usize>> {
|
||||
let mut all = Vec::new();
|
||||
for i in 0..other.len() {
|
||||
let block = &other[i];
|
||||
'block: for i in start..other.len() {
|
||||
trace!("trying {}", i);
|
||||
let (mask, inv_mask) = other_masks[i];
|
||||
for input in 0..func.len() {
|
||||
if func[input] != typ {
|
||||
continue;
|
||||
}
|
||||
if check_mask(input, mask, inv_mask) {
|
||||
trace!("success by idx {}", input);
|
||||
// this block is useful
|
||||
// mark inputs as done
|
||||
let mut func = func.clone();
|
||||
for input in 0..func.len() {
|
||||
if check_mask(input, mask, inv_mask) {
|
||||
trace!("set {} to any", input);
|
||||
func[input] = Any;
|
||||
}
|
||||
}
|
||||
let extensions = all_recursive(func, vars, typ, other, other_masks);
|
||||
let extensions = all_recursive(func, vars, typ, other, other_masks, i+1);
|
||||
if extensions.is_empty() {
|
||||
all.push(vec![block.to_vec()]);
|
||||
all.push(vec![i]);
|
||||
}
|
||||
for mut extension in extensions {
|
||||
extension.push(block.to_vec());
|
||||
all.push(extension);
|
||||
if !extension.is_empty() {
|
||||
extension.push(i);
|
||||
all.push(extension);
|
||||
} else {
|
||||
warn!("empty extension?");
|
||||
}
|
||||
}
|
||||
continue 'block;
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("returning");
|
||||
all
|
||||
}
|
||||
|
||||
@ -211,10 +294,10 @@ pub fn print_implicate(vars: &[(Variable, Output)]) -> String {
|
||||
let mut s = String::new();
|
||||
for i in 0..vars.len() {
|
||||
let (var, out) = vars[i];
|
||||
s.push(print_var(var));
|
||||
if out == One {
|
||||
s += "~";
|
||||
s += "̅";
|
||||
}
|
||||
s += print_var(var);
|
||||
if i != vars.len() - 1 {
|
||||
s += " v ";
|
||||
}
|
||||
@ -226,10 +309,10 @@ pub fn print_implicant(vars: &[(Variable, Output)]) -> String {
|
||||
let mut s = String::new();
|
||||
for i in 0..vars.len() {
|
||||
let (var, out) = vars[i];
|
||||
s.push(print_var(var));
|
||||
if out == Zero {
|
||||
s += "~";
|
||||
s += "̅";
|
||||
}
|
||||
s += print_var(var);
|
||||
if i != vars.len() - 1 {
|
||||
s += " ";
|
||||
}
|
||||
@ -237,13 +320,8 @@ pub fn print_implicant(vars: &[(Variable, Output)]) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
fn print_var(var: Variable) -> &'static str {
|
||||
match var {
|
||||
A => "a",
|
||||
B => "b",
|
||||
C => "c",
|
||||
_ => "?"
|
||||
}
|
||||
fn print_var(var: Variable) -> char {
|
||||
('a' as u8 + (0usize.leading_zeros() - var.leading_zeros() - 1) as u8) as char
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -255,7 +333,7 @@ fn test_3var() {
|
||||
vec![(A, One), (C, Zero)],
|
||||
vec![(B, Zero), (C, Zero)],
|
||||
]);
|
||||
let prime = find_prime(&fun, &mut [A, B, C], Zero, &groups).0;
|
||||
let prime = find_core(&fun, &mut [A, B, C], Zero, &groups).0;
|
||||
assert_eq!(prime, vec![
|
||||
vec![(A, Zero), (B, Zero)],
|
||||
vec![(A, One), (C, Zero)],
|
||||
|
@ -8,7 +8,7 @@ fn main() {
|
||||
println!("{}", print_implicant(x));
|
||||
}
|
||||
println!("prime:");
|
||||
let (prime, other) = find_prime(&function, &[A, B, C], One, &groups);
|
||||
let (prime, other) = find_core(&function, &[A, B, C], One, &groups);
|
||||
for x in &prime {
|
||||
println!("{}", print_implicant(x));
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
pub mod svg;
|
||||
|
||||
/// Returns grid coordinates, width, height
|
||||
pub fn grid(var_count: usize) -> (Vec<(usize, usize)>, usize, usize) {
|
||||
let mut grid = vec![(0, 0), (1, 0)];
|
142
src/ui/svg.rs
Normal file
142
src/ui/svg.rs
Normal file
@ -0,0 +1,142 @@
|
||||
use svg_fmt::*;
|
||||
|
||||
use crate::*;
|
||||
|
||||
const SIZE_FONT: f32 = 20.0;
|
||||
|
||||
const COLORS: &'static [Color] = &[
|
||||
rgb(0, 170, 255),
|
||||
rgb(255, 0, 0),
|
||||
rgb(0, 255, 0),
|
||||
rgb(255, 170, 0),
|
||||
rgb(255, 0, 255), // pink
|
||||
rgb(0, 128, 0), // dark green
|
||||
rgb(0, 0, 128), // dark blue
|
||||
rgb(128, 0, 128), // dark red
|
||||
];
|
||||
|
||||
pub fn get_svg(size_factor: usize, function: &FunctionSpec, vars: &[Variable], block_masks: &[(usize, usize)]) -> String {
|
||||
let size4 = size_factor / 4;
|
||||
let size8 = size_factor / 8;
|
||||
let delta = size_factor as f32 / 32.0;
|
||||
let mut svg = String::new();
|
||||
|
||||
let (grid, w, h) = grid(vars.len());
|
||||
|
||||
let mut blocks = vec![vec![vec![]; w]; h];
|
||||
for (i, &(x, y)) in grid.iter().enumerate() {
|
||||
for (idx, &(mask, inv_mask)) in block_masks.iter().enumerate() {
|
||||
if check_mask(i, mask, inv_mask) {
|
||||
blocks[y][x].push(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg += &BeginSvg { w: w * size_factor + 3, h: h * size_factor + 3 }.to_string();
|
||||
for (i, &(x, y)) in grid.iter().enumerate() {
|
||||
svg += &rectangle(1 + x * size_factor, 1 + y * size_factor, size_factor, size_factor)
|
||||
.fill(white())
|
||||
.stroke(Stroke::Color(black(), 2.0)).to_string();
|
||||
let x_start = 1 + x * size_factor;
|
||||
let x_end = 1 + (x+1) * size_factor;
|
||||
let y_start = 1 + y * size_factor;
|
||||
let y_end = 1 + (y+1) * size_factor;
|
||||
let mut rect = rectangle(1 + x * size_factor, 1 + y * size_factor, size_factor, size_factor)
|
||||
.fill(white())
|
||||
.stroke_opacity(0.7)
|
||||
.inflate(-1, -1);
|
||||
for (idx, &(mask, inv_mask)) in block_masks.iter().enumerate() {
|
||||
rect = rect.inflate(-delta, -delta);
|
||||
if check_mask(i, mask, inv_mask) {
|
||||
// check cells around this one
|
||||
let mut up = false;
|
||||
let mut down = false;
|
||||
let mut left = false;
|
||||
let mut right = false;
|
||||
for &var in vars {
|
||||
let j = i ^ var;
|
||||
if check_mask(j, mask, inv_mask) {
|
||||
let (x2, y2) = grid[j];
|
||||
if (x + 1) % w == x2 && y == y2 {
|
||||
right = true;
|
||||
}
|
||||
if x == x2 && (y + 1) % h == y2 {
|
||||
down = true;
|
||||
}
|
||||
if x == x2 && y == (y2 + 1) % h {
|
||||
up = true;
|
||||
}
|
||||
if x == (x2 + 1) % w && y == y2 {
|
||||
left = true;
|
||||
}
|
||||
if !up && !down && !left && !right {
|
||||
// happens when vars >= 5 (obviously)
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut sides = rect.sides();
|
||||
for s in &mut sides {
|
||||
*s = s.opacity(0.9);
|
||||
}
|
||||
let up = up && !(y == 0 && (0..h).all(|y| blocks[y][x].contains(&idx)));
|
||||
let down = down && !(y == h-1 && (0..h).all(|y| blocks[y][x].contains(&idx)));
|
||||
let left = left && !(x == 0 && (0..w).all(|x| blocks[y][x].contains(&idx)));
|
||||
let right = right && !(x == w-1 && (0..w).all(|x| blocks[y][x].contains(&idx)));
|
||||
if up {
|
||||
svg += &sides[0].width(2.0).color(white()).to_string();
|
||||
if !left {
|
||||
svg += &sides[2].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0.0, y_start).to_string();
|
||||
}
|
||||
if !right {
|
||||
svg += &sides[3].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0.0, y_start).to_string();
|
||||
}
|
||||
} else {
|
||||
svg += &sides[0].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||
}
|
||||
if down {
|
||||
svg += &sides[1].width(2.0).color(white()).to_string();
|
||||
if !left {
|
||||
svg += &sides[2].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0, y_end).to_string();
|
||||
}
|
||||
if !right {
|
||||
svg += &sides[3].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0, y_end).to_string();
|
||||
}
|
||||
} else {
|
||||
svg += &sides[1].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||
}
|
||||
if left {
|
||||
svg += &sides[2].width(2.0).color(white()).to_string();
|
||||
if !up {
|
||||
svg += &sides[0].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_start, 0).to_string();
|
||||
}
|
||||
if !down {
|
||||
svg += &sides[1].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_start, 0).to_string();
|
||||
}
|
||||
} else {
|
||||
svg += &sides[2].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||
}
|
||||
if right {
|
||||
svg += &sides[3].width(1.0).color(white()).to_string();
|
||||
if !up {
|
||||
svg += &sides[0].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_end, 0).to_string();
|
||||
}
|
||||
if !down {
|
||||
svg += &sides[1].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_end, 0).to_string();
|
||||
}
|
||||
} else {
|
||||
svg += &sides[3].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
svg += &text((x+1) * size_factor - size8, (y+1) * size_factor - size4 / 3, i.to_string())
|
||||
.align(Align::Right)
|
||||
.size(SIZE_FONT)
|
||||
.color(black()).to_string();
|
||||
svg += &text(x * size_factor + size4, (y+1) * size_factor - size4, function[i].to_string())
|
||||
.size(SIZE_FONT * 2.0)
|
||||
.color(black()).to_string();
|
||||
}
|
||||
svg += &EndSvg.to_string();
|
||||
|
||||
svg
|
||||
}
|
Loading…
Reference in New Issue
Block a user