123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /*
- * ExtrudeTool.js
- *
- * @author realor
- */
- import { Tool } from './Tool.js'
- import { Solid } from '../core/Solid.js'
- import { Profile } from '../core/Profile.js'
- import { ObjectBuilder } from '../builders/ObjectBuilder.js'
- import { Extruder } from '../builders/Extruder.js'
- import { Controls } from '../ui/Controls.js'
- import { I18N } from '../i18n/I18N.js'
- import * as THREE from '../lib/three.module.js'
- class ExtrudeTool extends Tool {
- constructor(application, options) {
- super(application)
- this.name = 'extrude'
- this.label = 'tool.extrude.label'
- this.help = 'tool.extrude.help'
- this.className = 'extrude'
- this.depthStep = 0.1
- this.setOptions(options)
- this.stage = 0
- this.solid = null
- this.depth = 0
- this.extrudeLine = new THREE.Line3(new THREE.Vector3(0, 0, -100), new THREE.Vector3(0, 0, 100))
- this.extrudeLineWorld = new THREE.Line3(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 0))
- this.matrixWorldInverse = new THREE.Matrix4()
- this.line = null
- this._onPointerDown = this.onPointerDown.bind(this)
- this._onPointerUp = this.onPointerUp.bind(this)
- this._onPointerMove = this.onPointerMove.bind(this)
- this._onSelection = this.onSelection.bind(this)
- this._onContextMenu = this.onContextMenu.bind(this)
- this.extrudeMaterial = new THREE.LineBasicMaterial({
- color: new THREE.Color(1, 0, 0),
- depthTest: false,
- linewidth: 1.5
- })
- this.createPanel()
- }
- activate() {
- this.panel.visible = true
- const application = this.application
- const container = application.container
- container.addEventListener('contextmenu', this._onContextMenu, false)
- container.addEventListener('pointerdown', this._onPointerDown, false)
- container.addEventListener('pointerup', this._onPointerUp, false)
- container.addEventListener('pointermove', this._onPointerMove, false)
- application.addEventListener('selection', this._onSelection, false)
- this.prepareExtrusion()
- }
- deactivate() {
- this.panel.visible = false
- const application = this.application
- const container = application.container
- container.removeEventListener('contextmenu', this._onContextMenu, false)
- container.removeEventListener('pointerdown', this._onPointerDown, false)
- container.removeEventListener('pointerup', this._onPointerUp, false)
- container.removeEventListener('pointermove', this._onPointerMove, false)
- application.removeEventListener('selection', this._onSelection, false)
- application.pointSelector.deactivate()
- this.removeExtrusionAxis()
- }
- createPanel() {
- this.panel = this.application.createPanel(this.label, 'left', 'panel_extrude')
- this.panel.preferredHeight = 140
- this.helpElem = document.createElement('div')
- this.panel.bodyElem.appendChild(this.helpElem)
- this.depthInputElem = Controls.addNumberField(this.panel.bodyElem, 'extrude_depth', 'label.depth', 0)
- this.depthInputElem.step = this.depthStep
- this.depthInputElem.addEventListener(
- 'change',
- event => {
- this.depth = parseFloat(this.depthInputElem.value)
- this.updateExtrusion()
- },
- false
- )
- this.depthLabelElem = this.depthInputElem.parentElement.firstChild
- this.buttonsElem = document.createElement('div')
- this.panel.bodyElem.appendChild(this.buttonsElem)
- this.applyButton = Controls.addButton(this.buttonsElem, 'extrude_apply', 'button.apply', event => {
- this.depth = parseFloat(this.depthInputElem.value)
- this.updateExtrusion()
- })
- this.finishButton = Controls.addButton(this.buttonsElem, 'revolve_finish', 'button.finish', event => {
- this.application.selection.clear()
- })
- }
- onPointerDown(event) {
- const application = this.application
- const pointSelector = application.pointSelector
- if (!pointSelector.isPointSelectionEvent(event)) return
- if (this.stage === 0) {
- this.setStage(1)
- }
- }
- onPointerMove(event) {
- const pointSelector = this.application.pointSelector
- if (!pointSelector.isPointSelectionEvent(event)) return
- if (this.stage === 1) {
- const snap = pointSelector.snap
- if (snap) {
- let vector = new THREE.Vector3()
- vector.copy(snap.positionWorld).applyMatrix4(this.matrixWorldInverse)
- let height = vector.z
- vector.copy(this.solid.builder.direction).normalize()
- this.depth = height / vector.z
- this.updateDepthInPanel()
- this.updateExtrusion()
- }
- }
- }
- onPointerUp(event) {
- const pointSelector = this.application.pointSelector
- if (!pointSelector.isPointSelectionEvent(event)) return
- if (this.stage === 1) {
- this.setStage(0)
- }
- }
- onSelection(event) {
- this.prepareExtrusion()
- }
- onContextMenu(event) {
- const pointSelector = this.application.pointSelector
- if (!pointSelector.isPointSelectionEvent(event)) return
- event.preventDefault()
- }
- setStage(stage) {
- this.stage = stage
- const application = this.application
- switch (stage) {
- case 0: // set depth value
- application.pointSelector.auxiliaryLines = []
- application.pointSelector.deactivate()
- this.depthLabelElem.style.display = ''
- this.depthInputElem.disabled = false
- this.depthInputElem.style.display = ''
- this.applyButton.style.display = ''
- this.finishButton.style.display = ''
- I18N.set(this.helpElem, 'innerHTML', 'tool.extrude.drag_pointer')
- application.i18n.update(this.helpElem)
- this.removeExtrusionAxis()
- break
- case 1: // dynamic extrude
- application.pointSelector.clearAxisGuides()
- application.pointSelector.excludeSelection = true
- application.pointSelector.auxiliaryPoints = []
- application.pointSelector.auxiliaryLines = [this.extrudeLineWorld]
- application.pointSelector.activate()
- this.depthLabelElem.style.display = ''
- this.depthInputElem.disabled = true
- this.depthInputElem.style.display = ''
- this.applyButton.style.display = 'none'
- this.finishButton.style.display = ''
- I18N.set(this.helpElem, 'innerHTML', 'tool.extrude.drag_pointer')
- application.i18n.update(this.helpElem)
- this.addExtrusionAxis()
- break
- case 2: // no object selected
- application.pointSelector.auxiliaryLines = []
- application.pointSelector.deactivate()
- this.depthLabelElem.style.display = 'none'
- this.depthInputElem.style.display = 'none'
- this.applyButton.style.display = 'none'
- this.finishButton.style.display = 'none'
- I18N.set(this.helpElem, 'innerHTML', 'tool.extrude.select_object')
- application.i18n.update(this.helpElem)
- this.removeExtrusionAxis()
- break
- }
- }
- prepareExtrusion() {
- const application = this.application
- const object = application.selection.object
- let solid
- if (object instanceof Profile && !(object.parent instanceof Solid)) {
- solid = this.extrudeProfile(object)
- } else if (object instanceof Profile && object.parent instanceof Solid && object.parent.builder instanceof Extruder && object.parent.children.length === 3) {
- solid = object.parent
- } else if (object instanceof Solid && object.builder instanceof Extruder && object.children.length === 3 && object.children[2] instanceof Profile) {
- solid = object
- } else {
- solid = null
- }
- this.solid = solid
- if (solid) {
- this.depth = solid.builder.depth
- this.updateDepthInPanel()
- if (!application.selection.contains(solid)) {
- application.selection.set(solid)
- }
- this.setStage(0)
- } else {
- this.depth = 0
- this.setStage(2)
- }
- }
- extrudeProfile(profile) {
- const application = this.application
- const parent = profile.parent
- application.removeObject(profile)
- const solid = new Solid()
- solid.name = 'Extrude'
- solid.builder = new Extruder(0)
- profile.matrix.decompose(solid.position, solid.rotation, solid.scale)
- solid.matrix.copy(profile.matrix)
- profile.visible = false
- profile.matrix.identity()
- profile.matrix.decompose(profile.position, profile.rotation, profile.scale)
- solid.add(profile)
- ObjectBuilder.build(solid)
- application.addObject(solid, parent, false, true)
- return solid
- }
- updateExtrusion() {
- const solid = this.solid
- if (solid) {
- let extruder = solid.builder
- if (this.depth !== extruder.depth) {
- extruder.depth = this.depth
- solid.needsRebuild = true
- ObjectBuilder.build(solid)
- this.application.notifyObjectsChanged(solid)
- }
- }
- }
- updateDepthInPanel() {
- const k = 10000000
- this.depthInputElem.value = Math.round(this.depth * k) / k
- }
- addExtrusionAxis() {
- const application = this.application
- const solid = this.solid
- let vector = new THREE.Vector3()
- vector
- .copy(solid.builder.direction)
- .normalize()
- .multiplyScalar(1000)
- this.extrudeLine.start.copy(vector)
- vector.negate()
- this.extrudeLine.end.copy(vector)
- let matrixWorld = solid.matrixWorld
- this.extrudeLineWorld.copy(this.extrudeLine).applyMatrix4(matrixWorld)
- this.matrixWorldInverse.copy(matrixWorld).invert()
- if (this.line) {
- application.removeObject(this.line)
- }
- let geometryPoints = []
- geometryPoints.push(this.extrudeLineWorld.start)
- geometryPoints.push(this.extrudeLineWorld.end)
- let geometry = new THREE.BufferGeometry()
- geometry.setFromPoints(geometryPoints)
- this.line = new THREE.Line(geometry, this.extrudeMaterial)
- this.line.name = 'extrudeLine'
- this.line.raycast = function() {}
- this.line.renderOrder = 10
- application.overlays.add(this.line)
- application.repaint()
- }
- removeExtrusionAxis() {
- if (this.line) {
- this.application.removeObject(this.line)
- this.line = null
- }
- }
- }
- export { ExtrudeTool }
|