mirror of
https://github.com/varkor/quiver.git
synced 2024-10-26 07:09:37 +03:00
wip
This commit is contained in:
parent
d6ff7ea022
commit
b61be9b9a9
14
src/arrow.js
14
src/arrow.js
@ -90,6 +90,8 @@ const CONSTANTS = {
|
||||
HOOK_BOTTOM: ["hook-bottom"],
|
||||
/// The corner of a square, used for pullbacks and pushouts.
|
||||
CORNER: ["corner"],
|
||||
/// The corner of a square, used for an alternate style for pullbacks and pushouts.
|
||||
CORNER_INVERSE: ["corner-inverse"],
|
||||
},
|
||||
/// The various label alignment options.
|
||||
LABEL_ALIGNMENT: new Enum(
|
||||
@ -1073,6 +1075,8 @@ class Arrow {
|
||||
switch (heads[i]) {
|
||||
case "epi":
|
||||
case "corner":
|
||||
case "corner-inverse":
|
||||
// FIXME: corner-inverse.
|
||||
[margin_left, margin_right, margin_begin] = [0, head_width, 0];
|
||||
break;
|
||||
case "mono":
|
||||
@ -1158,11 +1162,14 @@ class Arrow {
|
||||
|
||||
// The corner symbol used for pullbacks and pushouts.
|
||||
case "corner":
|
||||
case "corner-inverse":
|
||||
const is_inverse = head_style.endsWith("-inverse");
|
||||
const LENGTH = 12;
|
||||
const base_2 = LENGTH / (2 ** 0.5);
|
||||
const base_point
|
||||
= bezier.point(t_after_length(arclen_to_head + base_2 * start_sign))
|
||||
.add(offset);
|
||||
= bezier.point(t_after_length(
|
||||
arclen_to_head + (is_inverse ? 0 : base_2 * start_sign)
|
||||
)).add(offset);
|
||||
|
||||
// Draw the two halves of the head.
|
||||
for (const side_sign of [-1, 1]) {
|
||||
@ -1173,7 +1180,8 @@ class Arrow {
|
||||
const PI_4 = Math.PI / 4;
|
||||
const direction = this.target.origin.sub(this.source.origin).angle();
|
||||
const corner_angle
|
||||
= Math.PI + PI_4 * Math.round(4 * direction / Math.PI) - direction;
|
||||
= (is_inverse ? 0 : Math.PI)
|
||||
+ PI_4 * Math.round(4 * direction / Math.PI) - direction;
|
||||
|
||||
path.line_by(Point.lendir(
|
||||
LENGTH,
|
||||
|
2
src/icons/var-pullback-checked.svg
Normal file
2
src/icons/var-pullback-checked.svg
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" version="1.1" viewBox="0 0 64 64"><style>.st0{fill:none;stroke:#000;stroke-linecap:round;stroke-miterlimit:10}</style><circle cx="52" cy="12.2" r="3"/><circle cx="12" cy="12.2" r="3"/><path d="M18.8 12.2h26.5m-.1 0c-1.9 0-3.5-1.6-3.5-3.5m.1 7c0-1.9 1.6-3.5 3.5-3.5" class="st0"/><circle cx="12" cy="52.2" r="3"/><path d="M12 18.9v26.5m0 0c0-1.9 1.6-3.5 3.5-3.5m-7 0c1.9 0 3.5 1.6 3.5 3.5" class="st0"/><circle cx="52" cy="52.2" r="3"/><path d="M18.8 52.2h26.5m-.1 0c-1.9 0-3.5-1.6-3.5-3.5m.1 7c0-1.9 1.6-3.5 3.5-3.5M52 18.9v26.5m0 0c0-1.9 1.6-3.5 3.5-3.5m-7 0c1.9 0 3.5 1.6 3.5 3.5M24.5 18.5h-6m0 6v-6" class="st0"/></svg>
|
After Width: | Height: | Size: 675 B |
2
src/icons/var-pullback-unchecked.svg
Normal file
2
src/icons/var-pullback-unchecked.svg
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" version="1.1" viewBox="0 0 64 64"><style>.st0{fill:gray}.st1{fill:none;stroke:gray;stroke-linecap:round;stroke-miterlimit:10}</style><circle cx="52" cy="12.2" r="3" class="st0"/><circle cx="12" cy="12.2" r="3" class="st0"/><path d="M18.8 12.2h26.5m-.1 0c-1.9 0-3.5-1.6-3.5-3.5m.1 7c0-1.9 1.6-3.5 3.5-3.5" class="st1"/><circle cx="12" cy="52.2" r="3" class="st0"/><path d="M12 18.9v26.5m0 0c0-1.9 1.6-3.5 3.5-3.5m-7 0c1.9 0 3.5 1.6 3.5 3.5" class="st1"/><circle cx="52" cy="52.2" r="3" class="st0"/><path d="M18.8 52.2h26.5m-.1 0c-1.9 0-3.5-1.6-3.5-3.5m.1 7c0-1.9 1.6-3.5 3.5-3.5M52 18.9v26.5m0 0c0-1.9 1.6-3.5 3.5-3.5m-7 0c1.9 0 3.5 1.6 3.5 3.5M24.5 18.5h-6m0 6v-6" class="st1"/></svg>
|
After Width: | Height: | Size: 738 B |
@ -587,6 +587,10 @@ input[type="text"].flash {
|
||||
border-color: var(--ui-focus);
|
||||
}
|
||||
|
||||
.panel input.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel .vertical {
|
||||
display: inline-block;
|
||||
width: calc(100% - (20% + 4px) * 2);
|
||||
|
@ -526,6 +526,7 @@ QuiverExport.tikz_cd = new class extends QuiverExport {
|
||||
|
||||
case "adjunction":
|
||||
case "corner":
|
||||
case "corner-inverse":
|
||||
parameters.push("phantom");
|
||||
|
||||
let angle;
|
||||
@ -537,7 +538,9 @@ QuiverExport.tikz_cd = new class extends QuiverExport {
|
||||
angle = -Math.round(edge.angle() * 180 / Math.PI);
|
||||
break;
|
||||
case "corner":
|
||||
label = "\"\\lrcorner\"";
|
||||
case "corner-inverse":
|
||||
label = edge.options.style.name.endsWith("-inverse") ?
|
||||
"\"\\ulcorner\"" : "\"\\lrcorner\"";
|
||||
label_parameters.push("very near start");
|
||||
// Round the angle to the nearest 45º, so that the corner always
|
||||
// appears aligned with horizontal, vertical or diagonal lines.
|
||||
@ -545,7 +548,9 @@ QuiverExport.tikz_cd = new class extends QuiverExport {
|
||||
break;
|
||||
}
|
||||
|
||||
label_parameters.push(`rotate=${angle}`);
|
||||
if (angle !== 0) {
|
||||
label_parameters.push(`rotate=${angle}`);
|
||||
}
|
||||
|
||||
// We allow these sorts of edges to have labels attached,
|
||||
// even though it's a little unusual.
|
||||
|
@ -4,16 +4,18 @@
|
||||
% This package is currently a wrapper around the `tikz-cd` package, importing necessary TikZ
|
||||
% libraries, and defining a new TikZ style for curves of a fixed height.
|
||||
%
|
||||
% Version: 1.0.1
|
||||
% Version: 1.1.0
|
||||
% Authors:
|
||||
% - varkor (https://github.com/varkor)
|
||||
% - AndréC (https://tex.stackexchange.com/users/138900/andr%C3%A9c)
|
||||
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{quiver}[2020/11/27 quiver]
|
||||
\ProvidesPackage{quiver}[2020/12/18 quiver]
|
||||
|
||||
% `tikz-cd` is necessary to draw commutative diagrams.
|
||||
\RequirePackage{tikz-cd}
|
||||
% `amssymb` is necessary for `\lrcorner` and `\ulcorner`.
|
||||
\RequirePackage{amssymb}
|
||||
% `calc` is necessary to draw curved arrows.
|
||||
\usetikzlibrary{calc}
|
||||
% `pathmorphing` is necessary to draw squiggly arrows.
|
||||
|
78
src/ui.js
78
src/ui.js
@ -2819,6 +2819,13 @@ class UI {
|
||||
style.heads = CONSTANTS.ARROW_HEAD_STYLE.NONE;
|
||||
style.tails = CONSTANTS.ARROW_HEAD_STYLE.CORNER;
|
||||
break;
|
||||
|
||||
// Pullback/pushout corner.
|
||||
case "corner-inverse":
|
||||
style.body_style = CONSTANTS.ARROW_BODY_STYLE.NONE;
|
||||
style.heads = CONSTANTS.ARROW_HEAD_STYLE.NONE;
|
||||
style.tails = CONSTANTS.ARROW_HEAD_STYLE.CORNER_INVERSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return style;
|
||||
@ -2847,7 +2854,7 @@ class UI {
|
||||
const value = parseInt(slider.element.value);
|
||||
const step = parseInt(slider.element.step);
|
||||
slider.element.value = value + step * delta;
|
||||
slider.element.dispatchEvent(new Event("input"));
|
||||
slider.dispatch(new Event("input"));
|
||||
}
|
||||
return focused_sliders.length > 0;
|
||||
}
|
||||
@ -3810,6 +3817,7 @@ class Panel {
|
||||
["arrow", "Arrow", Edge.default_options().style, "a"],
|
||||
["adjunction", "Adjunction", { name: "adjunction" }, "j"],
|
||||
["corner", "Pullback / pushout", { name: "corner" }, "p"],
|
||||
["corner-inverse", "Pullback / pushout", { name: "corner-inverse" }, "p"],
|
||||
],
|
||||
"edge-type",
|
||||
["large"],
|
||||
@ -3856,7 +3864,7 @@ class Panel {
|
||||
// we get the expected style, rather than the default style.
|
||||
if (data.name === "arrow") {
|
||||
ui.element.query_selector_all('.arrow-style input[type="radio"]:checked')
|
||||
.forEach((input) => input.element.dispatchEvent(new Event("change")))
|
||||
.forEach((input) => input.dispatch(new Event("change")))
|
||||
} else {
|
||||
this.defocus_inputs();
|
||||
}
|
||||
@ -3868,6 +3876,40 @@ class Panel {
|
||||
}),
|
||||
);
|
||||
|
||||
// TODO: history
|
||||
// TODO: save in localStorage
|
||||
// TODO: fix pressing P for the first time
|
||||
// TODO: address FIXMEs
|
||||
|
||||
const corner_button = this.element
|
||||
.query_selector(`input[name="edge-type"][value="corner"]`);
|
||||
const corner_inverse_button = this.element
|
||||
.query_selector(`input[name="edge-type"][value="corner-inverse"]`);
|
||||
corner_inverse_button.class_list.add("hidden");
|
||||
|
||||
// When the user clicks on the corner button, it alternates between `corner` and
|
||||
// `corner-inverse`.
|
||||
const alternate_buttons = [corner_button, corner_inverse_button];
|
||||
for (let i = 0; i < alternate_buttons.length; ++i) {
|
||||
const button = alternate_buttons[i];
|
||||
const next_button = alternate_buttons[(i + 1) % alternate_buttons.length];
|
||||
button.listen("mouseup", () => {
|
||||
if (button.element.checked) {
|
||||
button.element.disabled = true;
|
||||
next_button.element.disabled = false;
|
||||
next_button.class_list.remove("hidden");
|
||||
button.class_list.add("hidden");
|
||||
next_button.element.checked = true;
|
||||
next_button.dispatch(new Event("change"));
|
||||
UI.delay(() => button.element.disabled = false);
|
||||
}
|
||||
});
|
||||
button.listen("change", () => {
|
||||
next_button.class_list.add("hidden");
|
||||
button.class_list.remove("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
progress_style_selection = () => {
|
||||
const elements = [head_styles, body_styles, tail_styles];
|
||||
while (elements.length > 0) {
|
||||
@ -4179,10 +4221,12 @@ class Panel {
|
||||
let { length, options, draw_label } = properties(data);
|
||||
|
||||
// We use a custom pre-drawn SVG for the pullback/pushout button.
|
||||
if (options.style.name === "corner") {
|
||||
if (options.style.name.startsWith("corner")) {
|
||||
button.set_style({
|
||||
"background-image": ["", "un"].map((prefix) => {
|
||||
return `url("icons/pullback-${prefix}checked.svg")`;
|
||||
return `url("icons/${
|
||||
options.style.name.endsWith("inverse") ? "var-" : ""
|
||||
}pullback-${prefix}checked.svg")`;
|
||||
}).join(", ")
|
||||
});
|
||||
return button;
|
||||
@ -4257,7 +4301,7 @@ class Panel {
|
||||
event.triggered_by_shortcut = true;
|
||||
event.idempotent = option.element.checked;
|
||||
option.element.checked = true;
|
||||
option.element.dispatchEvent(event);
|
||||
option.dispatch(event);
|
||||
Shortcuts.flash(option);
|
||||
// Prevent other elements from being triggered by the same key
|
||||
// press.
|
||||
@ -4382,6 +4426,8 @@ class Panel {
|
||||
}
|
||||
};
|
||||
|
||||
let [corners, inverse_corners] = [0, 0];
|
||||
|
||||
// Collect the consistent and varying input values.
|
||||
for (const cell of ui.selection) {
|
||||
// Options applying to all cells.
|
||||
@ -4426,6 +4472,12 @@ class Panel {
|
||||
}
|
||||
} else {
|
||||
all_edges_are_arrows = false;
|
||||
if (cell.options.style.name === "corner") {
|
||||
++corners;
|
||||
}
|
||||
if (cell.options.style.name === "corner-inverse") {
|
||||
++inverse_corners;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4492,6 +4544,22 @@ class Panel {
|
||||
get_input("head-type", "arrowhead").element.checked = true;
|
||||
}
|
||||
|
||||
// Display the relevant pullback/pushout button.
|
||||
const corner_button = this.element
|
||||
.query_selector(`input[name="edge-type"][value="corner"]`);
|
||||
const corner_inverse_button = this.element
|
||||
.query_selector(`input[name="edge-type"][value="corner-inverse"]`);
|
||||
if (corners > inverse_corners && corners > 0) {
|
||||
corner_button.class_list.remove("hidden");
|
||||
corner_inverse_button.class_list.add("hidden");
|
||||
} else if (inverse_corners > corners && inverse_corners > 0) {
|
||||
corner_inverse_button.class_list.remove("hidden");
|
||||
corner_button.class_list.add("hidden");
|
||||
} else {
|
||||
// Pick the user's preference.
|
||||
// FIXME
|
||||
}
|
||||
|
||||
// Update the actual `value` attribute for the offset, curve, length, and level sliders
|
||||
// so that we can reference it in the CSS.
|
||||
sliders.forEach((slider) => slider.set_attributes({ "value": slider.element.value }));
|
||||
|
Loading…
Reference in New Issue
Block a user