main branch

pull/1/head
km0 1 year ago
parent 1c7b0deea3
commit f9caed169b

@ -1,5 +0,0 @@
TODO: comment the code
TODO: remove new from homepage?
or
TODO: add per-new level in the xquisite branches (this makes more sense!)

@ -36,8 +36,8 @@ def create_app(test_config=None):
os.path.join(app.root_path, "static"), "favicon.ico", mimetype="image/vnd.microsoft.icon",
)
from . import draw
app.register_blueprint(draw.bp)
from . import write
app.register_blueprint(write.bp)
from . import share
app.register_blueprint(share.bp)

@ -8,40 +8,38 @@ from exquisite_branch.db import get_db
bp = Blueprint('display', __name__, url_prefix='/display')
@bp.route('/')
def display():
db = get_db()
branches = db.execute(
"SELECT content, branch, parent, username FROM branches"
).fetchall()
streams = []
for branch in branches[::-1]:
if branch not in flatten(streams):
stream = [branch]
parent = branch['parent']
while parent != 'NEW':
current = next(
(x for x in branches if x['branch'] == parent), None)
parent = current['parent']
stream.append(current)
# branches = db.execute(
# "SELECT content, branch, parent, username FROM branches"
# ).fetchall()
# streams = []
# for branch in branches[::-1]:
# if branch not in flatten(streams):
# stream = [branch]
# parent = branch['parent']
# while parent != 'NEW':
# current = next(
# (x for x in branches if x['branch'] == parent), None)
# parent = current['parent']
# stream.append(current)
streams.append(stream[::-1])
# streams.append(stream[::-1])
return render_template('display_mako.html', branches=branches, streams=streams)
# return render_template('display_mako.html', branches=branches, streams=streams)
def flatten(t):
return [item for sublist in t for item in sublist]
# def flatten(t):
# return [item for sublist in t for item in sublist]
@bp.route('/linked')
def linked():
@bp.route('/')
def display():
db = get_db()
branches = db.execute(
"SELECT content, branch, parent, username FROM branches"
).fetchall()
return render_template('display_linked_mako.html', branches=branches)
return render_template('display.html', branches=branches)

@ -1,26 +1,41 @@
.streams {
overflow-x: auto;
overflow-y: hidden;
position: relative;
height: 500px;
header {
padding: 32px;
max-width: 60ch;
}
.stream {
white-space: nowrap;
position: absolute;
header h1,
header p {
margin: 0;
}
.container{
display: block;
padding: 64px;
}
.svg-container {
.stream,
.streams{
/* white-space: nowrap; */
position: relative;
display: inline-block;
margin: 0;
padding: 0;
width: 40ch;
position: relative;
}
.author {
.text-container {
position: absolute;
left: 50%;
bottom: 50px;
top: 0;
left: 0;
display: inline-block;
}
.author {
position: relative;
font-size: 1rem;
background-color: white;
}
@ -34,6 +49,3 @@
white-space: nowrap;
}
.branch svg {
border-top: 1px solid currentColor;
}

@ -1,52 +0,0 @@
.container{
display: block;
padding: 2000px;
}
.streams {
overflow-x: auto;
overflow-y: hidden;
position: relative;
width: 500px;
height: 500px;
display: inline-block;
}
.stream {
white-space: nowrap;
position: relative;
display: inline-block;
width: 500px;
height: 500px;
position: relative;
}
.svg-container {
position: absolute;
top: 0;
left: 0;
display: inline-block;
}
.author {
position: absolute;
left: 50%;
bottom: 50px;
font-size: 1rem;
background-color: white;
}
.branches {
overflow-x: auto;
overflow-y: hidden;
}
.branch {
white-space: nowrap;
}
.branch svg {
border-top: 1px solid currentColor;
}

@ -1,32 +0,0 @@
svg {
width: 500px;
height: 500px;
background-color: white;
}
#svg-container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
#previous {
position: absolute;
top: 0;
transform: translateX(-100%);
}
#previous svg {
background: none;
border: none;
}
#previous::after {
content: "";
width: 100%;
height: 100%;
left: 0;
position: absolute;
transform: translateX(-25%);
background-color: var(--background);
}

@ -13,10 +13,11 @@ body {
a {
color: currentColor;
text-decoration: none;
user-select: all;
}
a:hover {
color: tomato;
color: white;
}
nav {

@ -4,7 +4,7 @@ header {
}
header object {
width: 100vmin;
width: 500px;
height: auto;
margin: 0 auto;
}

@ -1,4 +1,4 @@
:root {
--background: #edd;
--background: #2a9d8f;
--color: black;
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

@ -1,114 +0,0 @@
// Great resource from https://stackoverflow.com/a/40700068
// Thank you ConnorFan
var strokeWidth = 2;
var bufferSize;
var svgElement = document.getElementById("svgElement");
var rect = svgElement.getBoundingClientRect();
var path = null;
var strPath;
var buffer = []; // Contains the last positions of the mouse cursor
svgElement.addEventListener("mousedown", function (e) {
bufferSize = document.getElementById("cmbBufferSize").value;
path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("fill", "none");
path.setAttribute("stroke", "currentColor");
path.setAttribute("stroke-width", strokeWidth);
buffer = [];
var pt = getMousePosition(e);
appendToBuffer(pt);
strPath = "M" + pt.x + " " + pt.y;
path.setAttribute("d", strPath);
svgElement.appendChild(path);
});
svgElement.addEventListener("mousemove", function (e) {
if (path) {
appendToBuffer(getMousePosition(e));
updateSvgPath();
}
});
svgElement.addEventListener("mouseup", function () {
if (path) {
path = null;
}
});
svgElement.addEventListener("mouseleave", function () {
if (path) {
path = null;
}
});
var getMousePosition = function (e) {
return {
x: e.pageX - rect.left,
y: e.pageY - rect.top,
};
};
var appendToBuffer = function (pt) {
buffer.push(pt);
while (buffer.length > bufferSize) {
buffer.shift();
}
};
// Calculate the average point, starting at offset in the buffer
var getAveragePoint = function (offset) {
var len = buffer.length;
if (len % 2 === 1 || len >= bufferSize) {
var totalX = 0;
var totalY = 0;
var pt, i;
var count = 0;
for (i = offset; i < len; i++) {
count++;
pt = buffer[i];
totalX += pt.x;
totalY += pt.y;
}
return {
x: totalX / count,
y: totalY / count,
};
}
return null;
};
var updateSvgPath = function () {
var pt = getAveragePoint(0);
if (pt) {
// Get the smoothed part of the path that will not change
strPath += " L" + pt.x + " " + pt.y;
// Get the last part of the path (close to the current mouse position)
// This part will change if the mouse moves again
var tmpPath = "";
for (var offset = 2; offset < buffer.length; offset += 2) {
pt = getAveragePoint(offset);
tmpPath += " L" + pt.x + " " + pt.y;
}
// Set the complete current path coordinates
path.setAttribute("d", strPath + tmpPath);
}
};
//
//
//
// SAVE THE BRANCH
const form = document.querySelector("form");
form.addEventListener("submit", () => {
let wrapper = document.createElement("div");
wrapper.appendChild(svgElement);
form["content"].value = wrapper.innerHTML;
return true;
});

@ -13,8 +13,8 @@
<body>
<nav>
<%block name='nav' >
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('display.display')}}">Results</a>
<a href="${url_for('home.home')}">Home</a>
<a href="${url_for('display.display')}">Results</a>
</%block>
</nav>
${self.body()}

@ -1,49 +1,44 @@
{%extends 'base.html' %} {%block head %}
<link rel="stylesheet" href="{{url_for('static', filename='css/display.css')}}" />
<title>Display</title>
{%endblock%} {%block nav%}
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('draw.last')}}">Draw</a>
{%endblock%} {%block contents%}
<h1>Exquisite Branch</h1>
<div class="streams">
{% for stream in streams %}
<div
class="stream"
style="color: rgb({{ range(0, 255) | random }},{{ range(0, 255) | random }},{{ range(0, 255) | random }}); transform: translate({{ range(-5, 5) | random }}px, {{ range(-5, 5) | random }}px)"
>
{% for branch in stream %}
<div class="svg-container">
{{branch['content'] | safe}}
<span
class="author"
style="transform: translate({{ range(-50, 50) | random }}px, {{ range(-10, 10) | random }}px"
>
{{ branch['username']}}
</span>
</div>
{%endfor%}
</div>
{%endfor%}
</div>
{{colors}}
<h2>Branches</h2>
<div class="branches">
{% for stream in streams %}
<div class="branch">
{% for branch in stream %}
<div class="svg-container">
{{branch['content'] | safe}}
<span class="author"> {{ branch['username']}} </span>
<%inherit file="base_mako.html" />
<%block name="head">
<link rel="stylesheet" href="${url_for('static', filename='css/display.css')}">
</%block>
<header>
<h1>Exquisite Excerpts</h1>
<p>Here texts branch and follow their own path. Click on an excerpt to create a new branch and continue toward new meanings. </p>
</header>
<main class="container">
<% from random import random %>
<% from collections import defaultdict %>
<% transform = {'NEW': ''} %>
<% visited = defaultdict(int) %>
<div class="stream">
% for branch in branches:
<% visited[branch['parent']] += 1 %>
% if visited[branch['parent']] > 1:
<% steer = (random() - 0.5) * 0.25 %>
% else:
<% steer = 0 %>
% endif
<% transform[branch['branch']] = f'{transform[branch["parent"]]} rotate({random() * 0.04 + steer}turn) translateX(100%)' %>
<div class="text-container" style="transform: ${transform[branch['parent']]}">
<a href="${url_for('write.write', parent=branch['branch'], _external=True, _scheme='http')}" target="__blank">
${branch['content']}
</a>
<span class="author">${branch['username']}</span>
</div>
{%endfor%}
% endfor
</div>
{%endfor%}
</div>
{%endblock%}
</main>

@ -1,46 +0,0 @@
<%inherit file="base_mako.html" />
<%block name="head">
<link rel="stylesheet" href="${url_for('static', filename='css/display_mako.css')}">
</%block>
<main class="container">
<% from random import random %>
<% from collections import defaultdict %>
<% transform = {'NEW': ''} %>
<% visited = defaultdict(int) %>
<div class="stream">
% for branch in branches:
<% visited[branch['parent']] += 1 %>
% if visited[branch['parent']] > 1:
<% steer = (random() - 0.5) * 0.25 %>
% else:
<% steer = 0 %>
% endif
<% transform[branch['branch']] = f'{transform[branch["parent"]]} rotate({random() * 0.04 + steer}turn) translateX(100%)' %>
<div class="svg-container" style="transform: ${transform[branch['parent']]}">
<a href="${url_for('draw.draw', parent=branch['branch'], _external=True, _scheme='https')}" target="__blank">
${branch['content']}
</a>
</div>
% endfor
</div>
</main>

@ -1,21 +0,0 @@
<%inherit file="base_mako.html" />
<%block name="head">
<link rel="stylesheet" href="${url_for('static', filename='css/display_mako.css')}">
</%block>
<% from random import random %>
<% offset = 1 / (len(streams) - 1)%>
% for stream in streams:
<div class="stream">
<% transform = f'rotate({offset * loop.index}turn) translateX(100%) ' %>
% for branch in stream:
<% transform = transform + ' rotate(' + str((random() * 2 - 1) * 0.02) + 'turn) translateX(100%)'%>
<div class="svg-container" style="transform: ${transform}">${branch['content']}</div>
% endfor
</div>
% endfor

@ -1,51 +0,0 @@
{%extends 'base.html' %} {%block head %}
<title>Draw</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/draw.css')}}" />
<script src="{{url_for('static', filename='js/draw.js')}}" defer></script>
{%endblock%} {%block nav%}
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('display.display')}}">Results</a>
{%endblock%} {%block contents%}
<h1>Draw</h1>
<div id="divSmoothingFactor">
<label for="cmbBufferSize">Smoothing</label>
<select id="cmbBufferSize">
<option value="1">1 - No smoothing</option>
<option value="4">4 - Sharp curves</option>
<option value="8" selected="selected">8 - Smooth curves</option>
<option value="12">12 - Very smooth curves</option>
<option value="16">16 - Super smooth curves</option>
<option value="20">20 - Hyper smooth curves</option>
</select>
</div>
<div id="svg-container">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
id="svgElement"
x="0px"
y="0px"
width="500px"
height="500px"
viewBox="0 0 500 500"
enable-background="new 0 0 500 500"
xml:space="preserve"
data-parent="{{parent or None}}"
data-branch="{{branch}}"
></svg>
<div id="previous">{% if content %} {{content|safe}} {% endif %}</div>
</div>
<form method="POST">
<input type="hidden" name="branch" value="{{branch}}" />
<input type="hidden" name="content" />
<input type="text" name="username" placeholder="Name" />
<input type="submit" />
</form>
{%endblock%}

@ -12,15 +12,14 @@
<body>
<header class="title">
<object
data="{{url_for('static', filename='img/logo.svg')}}"
data="{{url_for('static', filename='img/title.svg')}}"
type="image/svg+xml"
></object>
<h1>really exquisite indeed</h1>
</header>
<main>
<a href="{{url_for('draw.new')}}">Start new</a> <br />
<a href="{{url_for('draw.last')}}">Continue from last</a> <br />
<a href="{{url_for('write.new')}}">Start new</a> <br />
<a href="{{url_for('write.last')}}">Continue from last</a> <br />
<a href="{{url_for('display.display')}}">Display results</a>
</main>
</body>

@ -11,9 +11,9 @@
<div class="share">
Send this link to your friends:
<a
href="{{url_for('draw.draw', parent=branch)}}"
data-copy="{{ url_for('draw.draw', parent=branch, _external=True, _scheme='https')}}"
>{{ url_for('draw.draw', parent=branch, _external=True, _scheme='https')}}</a
href="{{url_for('write.write', parent=branch)}}"
data-copy="{{ url_for('write.write', parent=branch, _external=True, _scheme='https')}}"
>{{ url_for('write.write', parent=branch, _external=True, _scheme='https')}}</a
>
</div>

@ -0,0 +1,27 @@
{%extends 'base.html' %} {%block head %}
<title>Draw</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/write.css')}}" />
<script src="{{url_for('static', filename='js/draw.js')}}" defer></script>
{%endblock%} {%block nav%}
<a href="{{url_for('home.home')}}">Home</a>
<a href="{{url_for('display.display')}}">Results</a>
{%endblock%} {%block contents%}
<!-- mmm i dont remember what i was using these two things for! -->
<!-- data-parent="{{parent or None}}" -->
<!-- data-branch="{{branch}}" -->
<!-- Maybe to create links in the display page? -->
<h1>Write</h1>
<div id="previous">{% if content %} {{content|safe}} {% endif %}</div>
<form method="POST">
<input type="hidden" name="branch" value="{{branch}}" />
<textarea name="content" ></textarea>
<input type="text" name="username" placeholder="Name" />
<input type="submit" />
</form>
{%endblock%}

@ -6,11 +6,11 @@ from werkzeug.exceptions import abort
from shortuuid import uuid
bp = Blueprint('draw', __name__, url_prefix='/draw')
bp = Blueprint('write', __name__, url_prefix='/write')
@bp.route('/<parent>', methods=('GET', 'POST'))
def draw(parent=None):
def write(parent=None):
db = get_db()
if request.method == 'POST':
@ -42,7 +42,7 @@ def draw(parent=None):
if previous is None:
abort(404, f"Previous with id {parent} doesn't exist")
return render_template('draw.html', parent=parent, content=previous['content'], branch=branch)
return render_template('write.html', parent=parent, content=previous['content'], branch=branch)
@bp.route('/last', methods=('GET', 'POST'))
@ -68,7 +68,7 @@ def last():
db.commit()
return redirect(url_for('share.share', branch=branch))
return render_template('draw.html', parent=parent, content=previous['content'], branch=branch)
return render_template('write.html', parent=parent, content=previous['content'], branch=branch)
@bp.route('/', methods=('GET', 'POST'))
@ -90,4 +90,4 @@ def new():
db.commit()
return redirect(url_for('share.share', branch=branch))
return render_template('draw.html', parent=parent, branch=branch)
return render_template('write.html', parent=parent, branch=branch)

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 180 KiB

@ -0,0 +1,61 @@
# Exquisite Excerpts
A branching version of the [exquisite corpse](https://en.wikipedia.org/wiki/Exquisite_corpse) game, forked from the [exquisite branch](https://git.xpub.nl/kamo/exquisite-branch) drawing app developed for [SI17](https://issue.xpub.nl/17/).
Write something, upload it and send the link to someone else: they will continue from your excerpt. With a catch: if you send to just one person the chain will continue linearly, but send it to more people and things will start branching in different directions.
Could be a writing machine to work on the collective pubblication for the graduation, inspired by what [Kim wrote here](https://pad.xpub.nl/p/gradcollectivexpub).
## Install
Clone the repository.
```
git clone https://git.xpub.nl/kamo/exex.git
```
Create a virtual environment.
```
python3 -m venv venv
```
Install the requirements with `pip` and the `requirements.txt` file.
```
pip install -r requirements.txt
```
Create an `.env` file in the root folder of the project with the following variables:
```
DEBUG=True
FLASK_ENV=development
FLASK_APP=exquisite_branch
```
Before running the app for the first time, be sure to initialize the database. __Watch out:__ this will delete the previous instance of the database along with its contents!
```
flask init-db
```
After initializing the database you can run the flask application
```
flask run
```
## Overview
Exex saves contents in a database, and join them together in a branching version of the exquisite corpse.
The original exquisite corpse data structure is something similar to a linked list, where every drawing is connected to the previous one. Contents in _exex_ are saved in a database with the same principle.
![Diagram with the situationship of the entries](linked.svg)
Each entry in the database has the following properties:
- `id`: a unique identifier for every entry
- `branch`: a random name for the excerpt
- `parent`: the random name of the previous excerpt
- `content`: the actual content of the writings
- `username`: the author of the excerpt
When generating the display page, every entry look up its `parent` property to position itself after that.
Mhh should rewrite this better bc is super convoluted ahah.

@ -1,7 +1,6 @@
autopep8==1.6.0
click==8.0.3
colorama==0.4.4
-e git+https://git.xpub.nl/kamo/exquisite-branch.git@ae185baf0c048ac5f9171314ea5e842362b99dcd#egg=exquisite_branch
Flask==2.0.2
Flask-Mako==0.4
itsdangerous==2.0.1

@ -1,7 +1,7 @@
from setuptools import find_packages, setup
setup(
name='exquisite_branch',
name='exquisite_excerpts',
version='1.0.0',
packages=find_packages(),
include_package_data=True,

Loading…
Cancel
Save