Browse Source

Implement rectangular selection

better-rectangles
Tissevert 8 months ago
parent
commit
1a65637f53
8 changed files with 151 additions and 19 deletions
  1. +24
    -5
      gui.css
  2. +2
    -0
      index.html
  3. +5
    -1
      src/GUI.js
  4. +27
    -0
      src/Geometry/Rectangle.js
  5. +22
    -0
      src/Geometry/Segment.js
  6. +7
    -6
      src/Scoria.js
  7. +59
    -2
      src/Toolbar/EditMode.js
  8. +5
    -5
      src/XML/ALTO.js

+ 24
- 5
gui.css View File

@ -20,7 +20,7 @@ html, body {
cursor: pointer;
}
#workzone {
#page {
padding: 1em;
background: #fffdf5;
border-left: 1px solid #e2e2e2;
@ -30,19 +30,38 @@ html, body {
transform: translateX(-50%);
}
#workzone.editMode .String {
#selector {
display: none;
position: fixed;
border: 1px dashed #aaa;
}
#selector.on {
display: block;
}
#page.editMode .String {
cursor: crosshair;
}
#workzone.editMode .String.deleted {
#page.editMode .String.deleted {
cursor: not-allowed;
}
#workzone * {
#selector, #page.freeze {
cursor: crosshair;
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
}
#page * {
position: absolute;
}
#workzone p {
#page p {
font-size: 0.9em;
margin: 0;
white-space: nowrap;


+ 2
- 0
index.html View File

@ -26,6 +26,8 @@
</span>
</div>
<div id="workzone">
<div id="page"></div>
<div id="selector"></div>
</div>
</body>
</html>

+ 5
- 1
src/GUI.js View File

@ -1,7 +1,9 @@
import printSpace from XML.ALTO.Element;
var workzone = document.getElementById('workzone');
workzone.style.width = printSpace.pxWidth + 'px';
var page = document.getElementById('page');
page.style.width = printSpace.pxWidth + 'px';
var selector = document.getElementById('selector');
var inputFiles = document.getElementById('inputFiles');
var fileNumber = document.getElementById('fileNumber');
@ -15,7 +17,9 @@ return {
fileName: fileName,
fileNumber: fileNumber,
inputFiles: inputFiles,
page: page,
saveScoria: saveScoria,
selector: selector,
wcThreshold: wcThreshold,
workzone: workzone
};

+ 27
- 0
src/Geometry/Rectangle.js View File

@ -0,0 +1,27 @@
import * as Segment from Geometry.Segment;
return {
contains: contains,
intersects: intersects,
make: make
}
function make(o) {
if(o.x != undefined && o.y != undefined) {
return {x: Segment.make(o.x), y: Segment.make(o.y)};
} else if(o instanceof HTMLElement) {
var bounds = o.getBoundingClientRect();
return {
x: Segment.make({from: bounds.x, to: bounds.right}),
y: Segment.make({from: bounds.y, to: bounds.bottom})
}
}
}
function contains(a, b) {
return Segment.contains(a.x, b.x) && Segment.contains(a.y, b.y);
}
function intersects(a, b) {
return Segment.intersects(a.x, b.x) && Segment.intersects(a.y, b.y);
}

+ 22
- 0
src/Geometry/Segment.js View File

@ -0,0 +1,22 @@
return {
contains: contains,
intersects: intersects,
make: make
}
function contains(segmentA, segmentB) {
return segmentA.min <= segmentB.min && segmentB.max <= segmentA.max;
}
function intersects(segmentA, segmentB) {
return segmentA.min <= segmentB.min && segmentB.min <= segmentA.max
|| segmentB.min <= segmentA.min && segmentA.min <= segmentB.max;
}
function make(o) {
if(o.origin != undefined && o.norm != undefined) {
return {min: o.origin, max: o.origin + o.norm};
} else if(o.from != undefined && o.to != undefined) {
return {min: Math.min(o.from, o.to), max: Math.max(o.from, o.to)};
}
}

+ 7
- 6
src/Scoria.js View File

@ -13,15 +13,16 @@ function isScoria(wordId) {
return scoriae[wordId] || false;
}
function toggleWord(e) {
function toggleWord(domElem, forceState) {
if(editMode.checked) {
var wordId = e.target.id;
if(scoriae[wordId]) {
delete scoriae[wordId];
var state = forceState != undefined ? forceState : !scoriae[domElem.id];
if(state) {
scoriae[domElem.id] = true;
domElem.classList.add('deleted');
} else {
scoriae[wordId] = true;
delete scoriae[domElem.id];
domElem.classList.remove('deleted');
}
e.target.classList.toggle('deleted');
}
}


+ 59
- 2
src/Toolbar/EditMode.js View File

@ -1,11 +1,68 @@
import {editMode, workzone} from GUI;
import {editMode, page, selector, workzone} from GUI;
import * as Rectangle from Geometry.Rectangle;
import toggleWord from Scoria;
var selection = {
x: {from: null, to: null},
y: {from: null, to: null}
};
editMode.addEventListener('change', sync);
workzone.addEventListener('mousedown', startRectangle);
workzone.addEventListener('mousemove', moveRectangle);
workzone.addEventListener('mouseup', endRectangle);
return {
sync: sync
};
function sync() {
workzone.className = editMode.checked ? "editMode" : "";
page.classList[editMode.checked ? "add" : "remove"]("editMode");
}
function startRectangle(e) {
if(editMode.checked) {
selection.x.from = e.clientX;
selection.y.from = e.clientY;
selector.className = 'on';
page.classList.add('freeze');
}
}
function moveRectangle(e) {
if(selection.x.from != undefined) {
selection.x.to = e.clientX;
selection.y.to = e.clientY;
selector.style.left = Math.min(selection.x.from, selection.x.to) + 'px';
selector.style.width = Math.abs(selection.x.to - selection.x.from) + 'px';
selector.style.top = Math.min(selection.y.from, selection.y.to) + 'px';
selector.style.height = Math.abs(selection.y.to - selection.y.from) + 'px';
flagAllIn(Rectangle.make(selection), page);
}
}
function endRectangle(e) {
if(editMode.checked) {
selection.x.from = selection.y.from = selection.x.to = selection.y.to = null;
selector.style.left = selector.style.top = selector.style.width =
selector.style.height = null;
selector.className = '';
page.classList.remove('freeze');
}
}
function flagAllIn(rectangle, domElem) {
if(domElem.classList.contains('String')) {
if(Rectangle.contains(rectangle, Rectangle.make(domElem))) {
toggleWord(domElem, true);
} else if(Rectangle.intersects(rectangle, Rectangle.make(domElem))) {
toggleWord(domElem, false);
}
} else {
for(var i = 0; i < domElem.children.length; i++) {
if(Rectangle.intersects(rectangle, Rectangle.make(domElem.children[i]))) {
flagAllIn(rectangle, domElem.children[i]);
}
}
}
}

+ 5
- 5
src/XML/ALTO.js View File

@ -1,7 +1,7 @@
import * as Element from XML.ALTO.Element;
import {isScoria, toggleWord} from Scoria;
import Quality;
import workzone from GUI;
import page from GUI;
import * as Dom from UnitJS.Dom;
import {appendTo, onNodes} from XML;
@ -16,10 +16,10 @@ function ifPush(test, elem) {
function display(ALTODoc) {
var printSpace = Element.make(ALTODoc.querySelector('Page PrintSpace'));
Element.setPrintSpace(printSpace);
workzone.style.height = Element.printSpace.pxHeight + 'px';
Dom.clear(workzone);
page.style.height = Element.printSpace.pxHeight + 'px';
Dom.clear(page);
onNodes(printSpace.get('TextBlock'), makeTextBlock(printSpace))
.forEach(appendTo(workzone));
.forEach(appendTo(page));
function makeTextBlock(parentElement) {
return function(textBlock) {
@ -53,7 +53,7 @@ function display(ALTODoc) {
class: ifPush(Quality.isLow(wc), "lowQuality")(
ifPush(isScoria(id), "deleted")(["String"])
),
onClick: toggleWord,
onClick: function(e) {toggleWord(e.target)},
id: id,
textContent: string.getAttribute('CONTENT')
});


Loading…
Cancel
Save