Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.1
0.5.2
4 changes: 3 additions & 1 deletion src/components/elements/groupobject.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ function GroupedObjectWrapper(props) {
if (element.elementData.componentType === 'pencil') {
newMetadata = element.elementData.metadata.map(
(vert, index) => {
const lwProp = vert.lw !== undefined ? { lw: vert.lw } : {}
if (index === 0) {
return { x: newX, y: newY }
return { x: newX, y: newY, ...lwProp }
} else if (index > 0) {
// here the logic is to get relative vertex coordinates to the original metadata
// so we want to get result of ( relative coordinate + orginal_vert(x) - originalX )
Expand All @@ -141,6 +142,7 @@ function GroupedObjectWrapper(props) {
element.elementData
.metadata[0].y
),
...lwProp,
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions src/components/elements/pencil.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ function Pencil(props) {
})
// Get all instances of every sub child element
const { group, path } = elementFactory.createElement()
const shapeRef = path || group.children[0]
group.elementData = { ...props.itemData, ...props }

if (props.parentGroup) {
/** This element will be rendered and scoped in its parent group */
console.log('properties of pencil', props)
const parentGroup = props.parentGroup
path.translation.x = props.properties.x
path.translation.y = props.properties.y
parentGroup.add(path)
shapeRef.translation.x = props.properties.x
shapeRef.translation.y = props.properties.y
parentGroup.add(shapeRef)
two.update()
} else {
/** This element will render by creating it's own group wrapper */
Expand All @@ -76,7 +77,9 @@ function Pencil(props) {
const { selector } = getEditComponents(two, group, 4)
selectorInstance = selector

group.children.unshift(path)
if (path) {
group.children.unshift(path)
}
two.update()

// document
Expand All @@ -100,7 +103,7 @@ function Pencil(props) {

setInternalState((draft) => {
draft.element = {
[path.id]: path,
[shapeRef.id]: shapeRef,
[group.id]: group,
// [selector.id]: selector,
}
Expand All @@ -109,8 +112,8 @@ function Pencil(props) {
data: group,
}
draft.shape = {
id: path.id,
data: path,
id: shapeRef.id,
data: shapeRef,
}
draft.text = {
data: {},
Expand Down
40 changes: 32 additions & 8 deletions src/factory/pencil.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Main from './main'
import { color_blue } from 'utils/constants'
import Two from 'two.js'
import { mergeSegmentsByLinewidth, averageLinewidth } from 'utils/pencilHelper'

export default class PencilFactory extends Main {
createElement() {
Expand All @@ -10,7 +10,37 @@ export default class PencilFactory extends Main {
const { fill, width, height, radius, stroke, linewidth, metadata } =
this.properties

// let paths = []
const hasLwData = metadata.length > 0 && metadata[0].lw !== undefined

if (hasLwData) {
// New format: multi-path rendering with variable linewidth
const segments = mergeSegmentsByLinewidth(metadata)
const group = two.makeGroup()

segments.forEach((segmentPoints) => {
if (segmentPoints.length < 2) return
const path = two.makePath()
segmentPoints.forEach((point) => {
path.vertices.push(
new Two.Vector(point.x - prevX, point.y - prevY)
)
})
path.noFill()
path.stroke = stroke || '#000'
path.closed = false
path.cap = 'round'
path.join = 'round'
path.linewidth = averageLinewidth(segmentPoints)
group.add(path)
})

group.translation.x = parseInt(prevX)
group.translation.y = parseInt(prevY)
this.group = group
return { group: this.group }
}

// Legacy format: single path with uniform linewidth (backward compatible)
let path = two.makePath()
if (metadata.length > 0) {
metadata.forEach(function (point) {
Expand All @@ -21,21 +51,15 @@ export default class PencilFactory extends Main {
path.noFill()
path.stroke = '#000'
path.closed = false
// two.add(path)
// paths.push(path)
}

// path.fill = fill ? fill : color_blue

path.linewidth = linewidth ? linewidth : 1

this.path = path
// Create group and take children elements as a parameter
const group = two.makeGroup(path)
group.translation.x = parseInt(prevX)
group.translation.y = parseInt(prevY)
this.group = group
console.log('group.id pencil', group.id)
return { group: this.group, path }
}
}
158 changes: 110 additions & 48 deletions src/newCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import Spinner from 'components/common/spinner'
import Loader from 'components/utils/loader'
import { updateX1Y1Vertices, updateX2Y2Vertices } from 'utils/updateVertices'
import { generateUUID } from 'utils/misc'
import {
velocityToLinewidth,
smoothLinewidth,
simplifyWithLinewidth,
} from 'utils/pencilHelper'

function getComponentSchema(obj, boardId) {
let generateId = generateUUID()
Expand Down Expand Up @@ -59,6 +64,7 @@ function getComponentSchema(obj, boardId) {

var isDrawing
var defaultLinewidthValue = 1
var pencilStrokeColorValue = '#000'

function addZUI(
props,
Expand Down Expand Up @@ -86,6 +92,13 @@ function addZUI(
let lastAddedPath
let paths = []

// Velocity-based pencil state
let pencilGroup = null
let pencilRawPoints = []
let lastPencilPoint = null
let lastPencilTime = 0
let lastPencilLinewidth = null

let scenario = null
let SCENARIO_JUST_ADDED_ELEMENT = 'justAddedElement'
let SCENARIO_PENCIL_MODE = 'pencilMode'
Expand Down Expand Up @@ -304,17 +317,25 @@ function addZUI(

document.getElementById('main-two-root').style.cursor = 'auto'
break
case SCENARIO_PENCIL_MODE:
// do here
case SCENARIO_PENCIL_MODE: {
domElement.addEventListener('mousemove', mousemove, false)
domElement.addEventListener('mouseup', mouseup, false)

currentPath = two.makePath()
currentPath.linewidth = defaultLinewidthValue
currentPath.closed = false
two.add(currentPath)
paths.push(currentPath)
// Create a group for live preview segments
pencilGroup = two.makeGroup()
pencilRawPoints = []
lastPencilLinewidth = defaultLinewidthValue

const startCoords = zui.clientToSurface(e.clientX, e.clientY)
lastPencilPoint = { x: startCoords.x, y: startCoords.y }
lastPencilTime = performance.now()
pencilRawPoints.push({
x: startCoords.x,
y: startCoords.y,
lw: lastPencilLinewidth,
})
break
}
default:
shape = null
mouse.x = e.clientX
Expand Down Expand Up @@ -667,19 +688,49 @@ function addZUI(
two.update()
}
break
case SCENARIO_PENCIL_MODE:
let getCoordinate = zui.clientToSurface(e.clientX, e.clientY)

currentPath.vertices.push(
new Two.Vector(getCoordinate.x, getCoordinate.y)
case SCENARIO_PENCIL_MODE: {
const pencilCoords = zui.clientToSurface(e.clientX, e.clientY)

// Distance throttle: skip if too close to last point
const pdx = pencilCoords.x - lastPencilPoint.x
const pdy = pencilCoords.y - lastPencilPoint.y
const pDist = Math.sqrt(pdx * pdx + pdy * pdy)
if (pDist < 3) break

// Compute velocity and map to linewidth
const now = performance.now()
const timeDelta = now - lastPencilTime
const velocity = timeDelta > 0 ? pDist / timeDelta : 0
const targetLw = velocityToLinewidth(velocity, defaultLinewidthValue)
const smoothedLw = smoothLinewidth(lastPencilLinewidth, targetLw, 0.3)

// Create a 2-point path segment for live preview
const segment = two.makePath(
lastPencilPoint.x, lastPencilPoint.y,
pencilCoords.x, pencilCoords.y
)
currentPath.vertices[currentPath.vertices.length - 1].command =
'L'
currentPath.noFill()
currentPath.stroke = '#000'
currentPath.linewidth = defaultLinewidthValue
segment.noFill()
segment.stroke = pencilStrokeColorValue
segment.linewidth = smoothedLw
segment.cap = 'round'
segment.join = 'round'
segment.closed = false
pencilGroup.add(segment)

// Record point with linewidth
pencilRawPoints.push({
x: pencilCoords.x,
y: pencilCoords.y,
lw: smoothedLw,
})

lastPencilPoint = { x: pencilCoords.x, y: pencilCoords.y }
lastPencilTime = now
lastPencilLinewidth = smoothedLw

two.update()
break
}
default:
/**
Currently "resize" event handling is at component level.
Expand Down Expand Up @@ -1056,48 +1107,53 @@ function addZUI(
domElement.removeEventListener('mousemove', mousemove, false)
domElement.removeEventListener('mouseup', mouseup, false)
break
case SCENARIO_PENCIL_MODE:
// isDrawing = false
// console.log(
// 'on mouse up pencil mode',
// paths,
// currentPath.vertices,
// currentPath.translation
// )

let generateId = generateUUID()
let componentData = {
id: generateId,
case SCENARIO_PENCIL_MODE: {
// Remove live preview group — React component will re-render from metadata
if (pencilGroup) {
two.remove(pencilGroup)
}

if (pencilRawPoints.length < 2) {
// Too few points to form a stroke
pencilGroup = null
pencilRawPoints = []
lastPencilPoint = null
lastPencilLinewidth = null
break
}

// Simplify points while preserving lw
const simplifiedPoints = simplifyWithLinewidth(pencilRawPoints, 1.5)

let pencilId = generateUUID()
let pencilComponentData = {
id: pencilId,
boardId: props.boardId,
componentType: 'pencil',
children: {},
metadata: [],
metadata: simplifiedPoints,
x: 0,
y: 0,
linewidth: currentPath.linewidth,
stroke: currentPath.stroke,
linewidth: defaultLinewidthValue,
stroke: pencilStrokeColorValue,
}
componentData.metadata = currentPath.vertices.map(
function (vertex) {
return { x: vertex.x, y: vertex.y }
}
)

componentData.x = Math.floor(componentData.metadata[0]?.x || 0)
componentData.y = Math.floor(componentData.metadata[0]?.y || 0)
pencilComponentData.x = Math.floor(simplifiedPoints[0]?.x || 0)
pencilComponentData.y = Math.floor(simplifiedPoints[0]?.y || 0)

addToLocalComponentStore(
componentData.id,
componentData.componentType,
componentData
pencilComponentData.id,
pencilComponentData.componentType,
pencilComponentData
)

// let currentPathRef = currentPath
// setTimeout(() => {
// two.remove(currentPathRef)
// two.update()
// }, 5000)
// Reset pencil state
pencilGroup = null
pencilRawPoints = []
lastPencilPoint = null
lastPencilLinewidth = null
break
}
default:
// diff to check new x,y and prev x,y
let oldShapeData = {}
Expand Down Expand Up @@ -1496,6 +1552,10 @@ const Canvas = (props) => {
defaultLinewidthValue = props.defaultLinewidth || 1
}, [props.defaultLinewidth])

useEffect(() => {
pencilStrokeColorValue = props.pencilStrokeColor || '#000'
}, [props.pencilStrokeColor])

// on group select use effect hook
useEffect(() => {
// let componentsArr = [...currentComponents]
Expand Down Expand Up @@ -1546,8 +1606,9 @@ const Canvas = (props) => {
let newMetadata = []
if (item.componentType === 'pencil') {
newMetadata = item.metadata.map((vert, index) => {
const lwProp = vert.lw !== undefined ? { lw: vert.lw } : {}
if (index === 0) {
return { x: relativeX, y: relativeY }
return { x: relativeX, y: relativeY, ...lwProp }
} else if (index > 0) {
// here the logic is to get relative vertex coordinates to the original metadata
// so we want to get result of ( relative coordinate + orginal_vert(x) - originalX )
Expand All @@ -1560,6 +1621,7 @@ const Canvas = (props) => {
y:
relativeY +
parseInt(vert.y - item.metadata[0].y),
...lwProp,
}
}
})
Expand Down
Loading