diff --git a/README.md b/README.md deleted file mode 100644 index a9e9d4fc..00000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# excalibur -Created with CodeSandbox diff --git a/package.json b/package.json new file mode 100644 index 00000000..d60ce44c --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "react", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "src/index.js", + "dependencies": { + "react": "16.12.0", + "react-dom": "16.12.0", + "react-scripts": "3.0.1", + "roughjs": "3.1.0" + }, + "devDependencies": { + "typescript": "3.3.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} \ No newline at end of file diff --git a/public/FG_Virgil.ttf b/public/FG_Virgil.ttf new file mode 100644 index 00000000..80001666 Binary files /dev/null and b/public/FG_Virgil.ttf differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 00000000..42ae2d2d --- /dev/null +++ b/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + React App + + + + +
+ + + + \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..1444e77a --- /dev/null +++ b/src/index.js @@ -0,0 +1,187 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import rough from "roughjs/dist/rough.umd.js"; + +import "./styles.css"; + +var elements = []; + +function newElement(type, x, y) { + const element = { + type: type, + x: x, + y: y, + width: 0, + height: 0 + }; + generateShape(element); + return element; +} + +function rotate(x1, y1, x2, y2, angle) { + // π‘Žβ€²π‘₯=(π‘Žπ‘₯βˆ’π‘π‘₯)cosπœƒβˆ’(π‘Žπ‘¦βˆ’π‘π‘¦)sinπœƒ+𝑐π‘₯ + // π‘Žβ€²π‘¦=(π‘Žπ‘₯βˆ’π‘π‘₯)sinπœƒ+(π‘Žπ‘¦βˆ’π‘π‘¦)cosπœƒ+𝑐𝑦. + // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line + return [ + (x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, + (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2 + ]; +} + +var generator = rough.generator(); + +function generateShape(element) { + if (element.type === "selection") { + element.draw = (rc, context) => { + context.fillStyle = "rgba(0, 0, 255, 0.10)"; + context.fillRect(element.x, element.y, element.width, element.height); + }; + } else if (element.type === "rectangle") { + const shape = generator.rectangle( + element.x, + element.y, + element.width, + element.height + ); + element.draw = (rc, context) => { + rc.draw(shape); + }; + } else if (element.type === "ellipse") { + const shape = generator.ellipse( + element.x + element.width / 2, + element.y + element.height / 2, + element.width, + element.height + ); + element.draw = (rc, context) => { + rc.draw(shape); + }; + } else if (element.type === "arrow") { + const x1 = element.x; + const y1 = element.y; + const x2 = element.x + element.width; + const y2 = element.y + element.height; + + const size = 30; // pixels + const distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); + // Scale down the arrow until we hit a certain size so that it doesn't look weird + const minSize = Math.min(size, distance / 2); + const xs = x2 - ((x2 - x1) / distance) * minSize; + const ys = y2 - ((y2 - y1) / distance) * minSize; + + const angle = 20; // degrees + const [x3, y3] = rotate(xs, ys, x2, y2, (-angle * Math.PI) / 180); + const [x4, y4] = rotate(xs, ys, x2, y2, (angle * Math.PI) / 180); + + const shapes = [ + generator.line(x1, y1, x2, y2), + generator.line(x3, y3, x2, y2), + generator.line(x4, y4, x2, y2) + ]; + + element.draw = (rc, context) => { + shapes.forEach(shape => rc.draw(shape)); + }; + return; + } else if (element.type === "text") { + if (element.text === undefined) { + element.text = prompt("What text do you want?"); + } + element.draw = (rc, context) => { + context.font = "20px Virgil"; + const measure = context.measureText(element.text); + const height = + measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent; + context.fillText( + element.text, + element.x - measure.width / 2, + element.y + measure.actualBoundingBoxAscent - height / 2 + ); + }; + } else { + throw new Error("Unimplemented type " + element.type); + } +} + +function App() { + const [draggingElement, setDraggingElement] = React.useState(null); + const [elementType, setElementType] = React.useState("selection"); + const [selectedElements, setSelectedElements] = React.useState([]); + function ElementOption({ type, children }) { + return ( + + ); + } + return ( +
+ Rectangle + Ellipse + Arrow + Text + Selection + { + const element = newElement( + elementType, + e.clientX - e.target.offsetLeft, + e.clientY - e.target.offsetTop + ); + elements.push(element); + setDraggingElement(element); + drawScene(); + }} + onMouseUp={e => { + setDraggingElement(null); + drawScene(); + }} + onMouseMove={e => { + if (!draggingElement) return; + let width = e.clientX - e.target.offsetLeft - draggingElement.x; + let height = e.clientY - e.target.offsetTop - draggingElement.y; + draggingElement.width = width; + // Make a perfect square or circle when shift is enabled + draggingElement.height = e.shiftKey ? width : height; + generateShape(draggingElement); + drawScene(); + }} + /> +
+ ); +} +const rootElement = document.getElementById("root"); + +function drawScene() { + ReactDOM.render(, rootElement); + + const canvas = document.getElementById("canvas"); + const rc = rough.canvas(canvas); + const context = canvas.getContext("2d"); + context.clearRect(0, 0, canvas.width, canvas.height); + + elements.forEach(element => { + element.draw(rc, context); + if (true || element.isSelected) { + const margin = 4; + context.setLineDash([8, 4]); + context.strokeRect( + element.x - margin, + element.y - margin, + element.width + margin * 2, + element.height + margin * 2 + ); + context.setLineDash([]); + } + }); +} + +drawScene(); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 00000000..f96dc647 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,5 @@ +/* http://www.eaglefonts.com/fg-virgil-ttf-131249.htm */ +@font-face { + font-family: "Virgil"; + src: url("https://uploads.codesandbox.io/uploads/user/ed077012-e728-4a42-8395-cbd299149d62/AflB-FG_Virgil.ttf"); +}