import React = require("react"); import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from 'react-color'; import { Doc, Field, Opt } from "../../fields/Doc"; import { Document } from "../../fields/documentSchemas"; import { InkField } from "../../fields/InkField"; import { BoolCast, Cast, NumCast } from "../../fields/Types"; import { DocumentType } from "../documents/DocumentTypes"; import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; import { AntimodeMenu, AntimodeMenuProps } from "./AntimodeMenu"; import "./FormatShapePane.scss"; @observer export class FormatShapePane extends AntimodeMenu { static Instance: FormatShapePane; private _lastFill = "#D0021B"; private _lastLine = "#D0021B"; private _lastDash = "2"; private _mode = ["fill-drip", "ruler-combined"]; @observable private _subOpen = [false, false]; @observable private _currMode = "fill-drip"; @observable _lock = false; @observable private _fillBtn = false; @observable private _lineBtn = false; @observable _controlBtn = false; @observable private _controlPoints: { X: number, Y: number }[] = []; @observable _currPoint = -1; getField(key: string) { return this.selectedInk?.reduce((p, i) => (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt); } @computed get selectedInk() { const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK); return inks.length ? inks : undefined; } @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; } @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; } @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; } @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; } @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } @computed get widthStk() { return this.getField("strokeWidth") || "1"; } @computed get markHead() { return this.getField("strokeStartMarker") || ""; } @computed get markTail() { return this.getField("strokeEndMarker") || ""; } @computed get shapeHgt() { return this.getField("_height"); } @computed get shapeWid() { return this.getField("_width"); } @computed get shapeXps() { return this.getField("x"); } @computed get shapeYps() { return this.getField("y"); } @computed get shapeRot() { return this.getField("rotation"); } set unFilled(value) { this.colorFil = value ? "" : this._lastFill; } set solidFil(value) { this.unFilled = !value; } set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); } set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); } set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); } set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); } set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } set dashdStk(value) { value && (this._lastDash = value) && (this.unStrokd = false); this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined); } set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); } set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); } set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); } set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); } set shapeWid(value) { this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { const oldWidth = NumCast(i.rootDoc._width); i.rootDoc._width = Number(value); this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth); }); } set shapeHgt(value) { this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { const oldHeight = NumCast(i.rootDoc._height); i.rootDoc._height = Number(value); this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight); }); } constructor(props: Readonly<{}>) { super(props); FormatShapePane.Instance = this; this._canFade = false; runInAction(() => this.Pinned = BoolCast(Doc.UserDoc()["menuFormatShape-pinned"])); } @action closePane = () => { this.fadeOut(false); this.Pinned = false; } @action upDownButtons = (dirs: string, field: string) => { switch (field) { case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break; // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break; case "Xps": this.selectedInk?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break; case "Yps": this.selectedInk?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break; case "stk": this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break; case "wid": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { //redraw points const oldWidth = NumCast(i.rootDoc._width); const oldHeight = NumCast(i.rootDoc._height); const oldX = NumCast(i.rootDoc.x); const oldY = NumCast(i.rootDoc.y); i.rootDoc._width = oldWidth + (dirs === "up" ? 10 : - 10); this._lock && (i.rootDoc._height = (i.rootDoc._width / oldWidth * NumCast(i.rootDoc._height))); const doc = Document(i.rootDoc); if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { const newPoints: { X: number, Y: number }[] = []; ink.forEach(i => { // (new x — oldx) + (oldxpoint * newWidt)/oldWidth const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; newPoints.push({ X: newX, Y: newY }); }); Doc.GetProto(doc).data = new InkField(newPoints); } } }); break; case "hgt": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { const oldWidth = NumCast(i.rootDoc._width); const oldHeight = NumCast(i.rootDoc._height); const oldX = NumCast(i.rootDoc.x); const oldY = NumCast(i.rootDoc.y); i.rootDoc._height = oldHeight + (dirs === "up" ? 10 : - 10); this._lock && (i.rootDoc._width = (i.rootDoc._height / oldHeight * NumCast(i.rootDoc._width))); const doc = Document(i.rootDoc); if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { const newPoints: { X: number, Y: number }[] = []; ink.forEach(i => { // (new x — oldx) + (oldxpoint * newWidt)/oldWidth const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; newPoints.push({ X: newX, Y: newY }); }); Doc.GetProto(doc).data = new InkField(newPoints); } } }); break; } } @undoBatch @action addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => { this.selectedInk?.forEach(action(inkView => { if (this.selectedInk?.length === 1) { const doc = Document(inkView.rootDoc); if (doc.type === DocumentType.INK) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { const newPoints: { X: number, Y: number }[] = []; var counter = 0; for (var k = 0; k < index; k++) { control.forEach(pt => (pts[k].X === pt.X && pts[k].Y === pt.Y) && counter++); } //decide where to put the new coordinate const spNum = Math.floor(counter / 2) * 4 + 2; for (var i = 0; i < spNum; i++) { ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } for (var j = 0; j < 4; j++) { newPoints.push({ X: x, Y: y }); } for (var i = spNum; i < ink.length; i++) { newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } this._currPoint = -1; Doc.GetProto(doc).data = new InkField(newPoints); } } } })); } @undoBatch @action deletePoints = () => { this.selectedInk?.forEach(action(inkView => { if (this.selectedInk?.length === 1 && this._currPoint !== -1) { const doc = Document(inkView.rootDoc); if (doc.type === DocumentType.INK) { const ink = Cast(doc.data, InkField)?.inkData; if (ink && ink.length > 4) { const newPoints: { X: number, Y: number }[] = []; const toRemove = Math.floor(((this._currPoint + 2) / 4)); for (var i = 0; i < ink.length; i++) { if (Math.floor((i + 2) / 4) !== toRemove) { newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } } this._currPoint = -1; Doc.GetProto(doc).data = new InkField(newPoints); if (newPoints.length === 4) { const newerPoints: { X: number, Y: number }[] = []; newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); Doc.GetProto(doc).data = new InkField(newerPoints); } } } } })); } @undoBatch @action rotate = (angle: number) => { const _centerPoints: { X: number, Y: number }[] = []; SelectionManager.SelectedDocuments().forEach(action(inkView => { const doc = Document(inkView.rootDoc); if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { const xs = ink.map(p => p.X); const ys = ink.map(p => p.Y); const left = Math.min(...xs); const top = Math.min(...ys); const right = Math.max(...xs); const bottom = Math.max(...ys); _centerPoints.push({ X: left, Y: top }); } } })); var index = 0; SelectionManager.SelectedDocuments().forEach(action(inkView => { const doc = Document(inkView.rootDoc); if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { doc.rotation = Number(doc.rotation) + Number(angle); const ink = Cast(doc.data, InkField)?.inkData; if (ink) { const newPoints: { X: number, Y: number }[] = []; ink.forEach(i => { const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X; const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y; newPoints.push({ X: newX, Y: newY }); }); Doc.GetProto(doc).data = new InkField(newPoints); const xs = newPoints.map(p => p.X); const ys = newPoints.map(p => p.Y); const left = Math.min(...xs); const top = Math.min(...ys); const right = Math.max(...xs); const bottom = Math.max(...ys); doc._height = (bottom - top); doc._width = (right - left); } index++; } })); } @undoBatch @action control = (xDiff: number, yDiff: number, controlNum: number) => { this.selectedInk?.forEach(action(inkView => { if (this.selectedInk?.length === 1) { const doc = Document(inkView.rootDoc); if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { const newPoints: { X: number, Y: number }[] = []; const order = controlNum % 4; for (var i = 0; i < ink.length; i++) { if (controlNum === i || (order === 0 && i === controlNum + 1) || (order === 0 && controlNum !== 0 && i === controlNum - 2) || (order === 0 && controlNum !== 0 && i === controlNum - 1) || (order === 3 && i === controlNum - 1) || (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) || (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2) || ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1)) ) { newPoints.push({ X: ink[i].X - (xDiff * inkView.props.ScreenToLocalTransform().Scale), Y: ink[i].Y - (yDiff * inkView.props.ScreenToLocalTransform().Scale) }); } else { newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } } const oldx = doc.x; const oldy = doc.y; const xs = ink.map(p => p.X); const ys = ink.map(p => p.Y); const left = Math.min(...xs); const top = Math.min(...ys); Doc.GetProto(doc).data = new InkField(newPoints); const xs2 = newPoints.map(p => p.X); const ys2 = newPoints.map(p => p.Y); const left2 = Math.min(...xs2); const top2 = Math.min(...ys2); const right2 = Math.max(...xs2); const bottom2 = Math.max(...ys2); doc._height = (bottom2 - top2); doc._width = (right2 - left2); //if points move out of bounds doc.x = oldx - (left - left2); doc.y = oldy - (top - top2); } } } })); } @undoBatch @action switchStk = (color: ColorState) => { const val = String(color.hex); this.colorStk = val; return true; } @undoBatch @action switchFil = (color: ColorState) => { const val = String(color.hex); this.colorFil = val; return true; } colorPicker(setter: (color: string) => {}, type: string) { return
; } inputBox = (key: string, value: any, setter: (val: string) => {}) => { return <> setter(e.target.value)))} autoFocus />
; } inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => { return <> {title1}

{title2}

setter(e.target.value)} autoFocus /> {title2 === "" ? "" : <> setter2(e.target.value)} autoFocus />
} ; } colorButton(value: string, setter: () => {}) { return <> ; } controlPointsButton() { return <>

; } lockRatioButton() { return <>

; } rotate90Button() { return <>

; } @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); } @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); } @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); } @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); } @computed get stkInput() { return this.inputBox("stk", this.widthStk, (val: string) => this.widthStk = val); } @computed get dashInput() { return this.inputBox("dsh", this.widthStk, (val: string) => this.widthStk = val); } @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); } @computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); } @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } @computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); } @computed get controlPoints() { return this.controlPointsButton(); } @computed get lockRatio() { return this.lockRatioButton(); } @computed get rotate90() { return this.rotate90Button(); } @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } @computed get propertyGroupItems() { const fillCheck =
= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}> Fill: {this.fillButton}
Stroke: {this.lineButton}
{this._fillBtn ? this.fillPicker : ""} {this._lineBtn ? this.linePicker : ""} {this._fillBtn || this._lineBtn ? "" :
} {(this.solidStk || this.dashdStk) ? "Width" : ""} {(this.solidStk || this.dashdStk) ? this.stkInput : ""} {(this.solidStk || this.dashdStk) ? this.widthStk = e.target.value))} /> : (null)}
{(this.solidStk || this.dashdStk) ? <>

Arrow Head

this.markHead = this.markHead ? "" : "arrow"))} style={{ position: "absolute", right: 110, width: 20 }} />

Arrow End

this.markTail = this.markTail ? "" : "arrow"))} style={{ position: "absolute", right: 0, width: 20 }} />
: ""} Dash: this.dashdStk = this.dashdStk === "2" ? "0" : "2"))} style={{ position: "absolute", right: 110, width: 20 }} />
; const sizeCheck =
= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}> {this.controlPoints} {this.hgtInput} {this.XpsInput} {this.rotInput}
; const subMenus = this._currMode === "fill-drip" ? [`Appearance`, 'Transform'] : []; const menuItems = this._currMode === "fill-drip" ? [fillCheck, sizeCheck] : []; const indexOffset = 0; return
{subMenus.map((subMenu, i) =>
{menuItems[i]}
)}
; } @computed get closeBtn() { return ; } @computed get propertyGroupBtn() { return
{this._mode.map(mode => )}
; } render() { return this.getElementVert([this.closeBtn, this.propertyGroupItems]); } }