Merge remote-tracking branch 'origin' into kurt-bring-back-multi-profile

This commit is contained in:
Kurt Hutten Irev-Dev
2024-12-18 09:53:34 +11:00
8 changed files with 160 additions and 7 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 144 KiB

View File

@ -1,6 +1,17 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, tearDown, TEST_COLORS } from './test-utils'
import {
test as testFixture,
expect as expectFixture,
} from './fixtures/fixtureSetup'
import { join } from 'path'
import {
getUtils,
setup,
tearDown,
TEST_COLORS,
executorInputPath,
} from './test-utils'
import * as fsp from 'fs/promises'
import { XOR } from 'lib/utils'
test.beforeEach(async ({ context, page }, testInfo) => {
@ -1028,3 +1039,58 @@ part002 = startSketchOn('XZ')
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
})
})
testFixture.describe('Electron constraint tests', () => {
testFixture(
'Able to double click label to set constraint',
{ tag: '@electron' },
async ({ tronApp, homePage, scene, editor, toolbar }) => {
await tronApp.initialise({
fixtures: { homePage, scene, editor, toolbar },
folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'test-sample')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('angled_line.kcl'),
join(bracketDir, 'main.kcl')
)
},
})
const [clickHandler] = scene.makeMouseHelpers(600, 300)
await test.step('setup test', async () => {
await homePage.expectState({
projectCards: [
{
title: 'test-sample',
fileCount: 1,
},
],
sortBy: 'last-modified-desc',
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
})
await test.step('Double click to constrain', async () => {
await clickHandler()
await tronApp.page.getByRole('button', { name: 'Edit Sketch' }).click()
const child = tronApp.page
.locator('.segment-length-label-text')
.first()
.locator('xpath=..')
await child.dblclick()
const cmdBarSubmitButton = tronApp.page.getByRole('button', {
name: 'arrow right Continue',
})
await cmdBarSubmitButton.click()
await expectFixture(tronApp.page.locator('.cm-content')).toContainText(
'length001 = 15.3'
)
await expectFixture(tronApp.page.locator('.cm-content')).toContainText(
'|> angledLine([9, length001], %)'
)
await tronApp.page.getByRole('button', { name: 'Exit Sketch' }).click()
})
}
)
})

View File

@ -174,8 +174,13 @@ export const ClientSideScene = ({
const Overlays = () => {
const { context } = useModelingContext()
if (context.mouseState.type === 'isDragging') return null
// Set a large zIndex, the overlay for hover dropdown menu on line segments needs to render
// over the length labels on the line segments
return (
<div className="absolute inset-0 pointer-events-none">
<div
className="absolute inset-0 pointer-events-none"
style={{ zIndex: '99999999' }}
>
{Object.entries(context.segmentOverlays)
.filter((a) => a[1].visible)
.map(([pathToNodeString, overlay], index) => {

View File

@ -49,6 +49,7 @@ import {
defaultSourceRange,
sourceRangeFromRust,
resultIsOk,
SourceRange,
} from 'lang/wasm'
import {
engineCommandManager,
@ -110,6 +111,7 @@ import { SegmentInputs } from 'lang/std/stdTypes'
import { Node } from 'wasm-lib/kcl/bindings/Node'
import { radToDeg } from 'three/src/math/MathUtils'
import toast from 'react-hot-toast'
import { getArtifactFromRange, codeRefFromRange } from 'lang/std/artifactGraph'
type DraftSegment = 'line' | 'tangentialArcTo'
@ -648,7 +650,7 @@ export class SceneEntities {
)
let seg: Group
const _node1 = getNodeFromPath<CallExpression>(
const _node1 = getNodeFromPath<Node<CallExpression>>(
maybeModdedAst,
segPathToNode,
'CallExpression'
@ -675,6 +677,13 @@ export class SceneEntities {
from: segment.from,
to: segment.to,
}
const startRange = _node1.node.start
const endRange = _node1.node.end
const sourceRange: SourceRange = [startRange, endRange, true]
const selection: Selections = computeSelectionFromSourceRangeAndAST(
sourceRange,
maybeModdedAst
)
const result = initSegment({
prevSegment: sketch.paths[index - 1],
callExpName,
@ -687,6 +696,7 @@ export class SceneEntities {
theme: sceneInfra._theme,
isSelected,
sceneInfra,
selection,
})
if (err(result)) return
const { group: _group, updateOverlaysCallback } = result
@ -2541,3 +2551,26 @@ function getSketchesInfo({
}
return sketchesInfo
}
/**
* Given a SourceRange [x,y,boolean] create a Selections object which contains
* graphSelections with the artifact and codeRef.
* This can be passed to 'Set selection' to internally set the selection of the
* modelingMachine from code.
*/
function computeSelectionFromSourceRangeAndAST(
sourceRange: SourceRange,
ast: Node<Program>
): Selections {
const artifactGraph = engineCommandManager.artifactGraph
const artifact = getArtifactFromRange(sourceRange, artifactGraph) || undefined
const selection: Selections = {
graphSelections: [
{
artifact,
codeRef: codeRefFromRange(sourceRange, ast),
},
],
otherSelections: [],
}
return selection
}

View File

@ -229,7 +229,6 @@ export class SceneInfra {
const vector = new Vector3(0, 0, 0)
// Get the position of the object3D in world space
// console.log('arrowGroup', arrowGroup)
arrowGroup.getWorldPosition(vector)
// Project that position to screen space
@ -347,7 +346,6 @@ export class SceneInfra {
requestAnimationFrame(this.animate)
TWEEN.update() // This will update all tweens during the animation loop
if (!this.isFovAnimationInProgress) {
// console.log('animation frame', this.cameraControls.camera)
this.camControls.update()
this.renderer.render(this.scene, this.camControls.camera)
this.labelRenderer.render(this.scene, this.camControls.camera)
@ -434,7 +432,6 @@ export class SceneInfra {
if (!this.selected.hasBeenDragged && hasBeenDragged) {
this.selected.hasBeenDragged = true
// this is where we could fire a onDragStart event
// console.log('onDragStart', this.selected)
}
if (
hasBeenDragged &&

View File

@ -56,6 +56,8 @@ import { normaliseAngle, roundOff } from 'lib/utils'
import { SegmentOverlayPayload } from 'machines/modelingMachine'
import { SegmentInputs } from 'lang/std/stdTypes'
import { err } from 'lib/trap'
import { editorManager, sceneInfra } from 'lib/singletons'
import { Selections } from 'lib/selections'
interface CreateSegmentArgs {
input: SegmentInputs
@ -69,6 +71,7 @@ interface CreateSegmentArgs {
theme: Themes
isSelected?: boolean
sceneInfra: SceneInfra
selection?: Selections
}
interface UpdateSegmentArgs {
@ -118,6 +121,7 @@ class StraightSegment implements SegmentUtils {
isSelected = false,
sceneInfra,
prevSegment,
selection,
}) => {
if (input.type !== 'straight-segment')
return new Error('Invalid segment type')
@ -156,6 +160,7 @@ class StraightSegment implements SegmentUtils {
isSelected,
callExpName,
baseColor,
selection,
}
// All segment types get an extra segment handle,
@ -825,8 +830,37 @@ function createLengthIndicator({
lengthIndicatorText.innerText = roundOff(length).toString()
const lengthIndicatorWrapper = document.createElement('div')
// Double click workflow
lengthIndicatorWrapper.ondblclick = () => {
const selection = lengthIndicatorGroup.parent?.userData.selection
if (!selection) {
console.error('Unable to dimension segment when clicking the label.')
return
}
sceneInfra.modelingSend({
type: 'Set selection',
data: {
selectionType: 'singleCodeCursor',
selection: selection.graphSelections[0],
},
})
// Command Bar
editorManager.commandBarSend({
type: 'Find and select command',
data: {
name: 'Constrain length',
groupId: 'modeling',
argDefaultValues: {
selection,
},
},
})
}
// Style the elements
lengthIndicatorWrapper.style.position = 'absolute'
lengthIndicatorWrapper.style.pointerEvents = 'auto'
lengthIndicatorWrapper.appendChild(lengthIndicatorText)
const cssObject = new CSS2DObject(lengthIndicatorWrapper)
cssObject.name = SEGMENT_LENGTH_LABEL_TEXT

View File

@ -109,6 +109,7 @@ function DisplayObj({
setHasCursor(false)
}
}, [node.start, node.end, node.type])
return (
<pre
ref={ref}

View File

@ -1209,3 +1209,20 @@ function getWallOrCapPlaneCodeRef(
range: planeCodeRef[0].range,
}
}
/**
* Get an artifact from a code source range
*/
export function getArtifactFromRange(
range: SourceRange,
artifactGraph: ArtifactGraph
): Artifact | null {
for (const artifact of artifactGraph.values()) {
if ('codeRef' in artifact && artifact.codeRef) {
const match =
artifact?.codeRef.range[0] === range[0] &&
artifact?.codeRef.range[1] === range[1]
if (match) return artifact
}
}
return null
}