bx-1480-constructor #75

Merged
ksenia_mikhailova merged 4 commits from bx-1480-constructor into dev 2024-09-25 08:36:56 +03:00
14 changed files with 205 additions and 142 deletions

View File

@ -348,11 +348,6 @@ button {
&_color {
@apply w-full;
.color_picker,
.list_picker {
@apply ml-4;
}
}
input[type=range] {
@ -361,35 +356,15 @@ button {
}
}
.color_picker {
.picker {
@apply leading-none;
&-selected {
@apply size-10 rounded border-gray-300 shadow inline-block leading-none;
&__active {
@apply outline outline-2 outline-offset-2 outline-primary;
}
}
&-changer {
@apply absolute w-80 z-20 p-4 border rounded bg-white flex gap-0 right-0;
}
}
.list_picker {
@apply leading-none;
&-selected {
@apply size-10 rounded border-gray-300 shadow inline-block leading-none;
&__active {
@apply outline outline-2 outline-offset-2 outline-primary;
}
}
&-changer {
@apply absolute w-80 z-20 p-4 border rounded bg-white flex gap-0 right-0;
@apply flex gap-0;
}
&-list {

View File

@ -4,7 +4,6 @@ import { Stats, OrbitControls } from '@tresjs/cientos'
import { degToRad } from 'three/src/math/MathUtils.js';
const section_count = use_section_count()
const extra_section = use_extra_section()
const fence_section = use_fence_section()
const defDistance = 3
@ -26,7 +25,7 @@ const cameraStat = reactive({
const camera = ref("camera")
watch(fence_section, ()=>{
watch(fence_section, () => {
let v = fence_section.value * 2;
if (v <= defDistance) v = defDistance
controlsState.minDistance = v;
@ -47,12 +46,14 @@ watch(fence_section, ()=>{
<TresCanvas>
<TresPerspectiveCamera v-bind="cameraStat" ref="camera" />
<OrbitControls v-bind="controlsState" make-default />
<Suspense>
<ModelCamera />
</Suspense>
<TresGroup :position-y="-0.5">
<Suspense>
<ModelParametric />
</Suspense>
</TresGroup>
</TresCanvas>
</ClientOnly>
</div>

View File

@ -3,7 +3,8 @@ import { getColorHexFromRal, getColorNameFromRal } from '@/components/ral'
import type { ralTypes } from '@/components/ral'
import { predefLamelleColors, predefPillarColors } from '~/composables/useCalc';
import type { patternTypes } from '../pattern';
import { getFilename, type patternTypes } from '../pattern';
import { Vector3 } from 'three';
const lamelle_height = use_lamelle_height()
const lamelles_count = use_lamelles_count()
@ -16,6 +17,8 @@ const section_count = use_section_count()
const extra_section = use_extra_section()
const total_length = use_total_length()
const min_length = use_min_length()
const goto_cam = use_goto_camera()
const open_calc = use_open_calc()
if (!pillar_color.value) {
const r = Math.floor(Math.random() * predefPillarColors.length)
@ -144,7 +147,7 @@ const setPillarColor = (color: ralTypes) => {
pillar_color.value = color
pillar_text.value = contrastColor(color) ?? '#000'
}
const setPillarPattern = (pattern:patternTypes) => {
const setPillarPattern = (pattern: patternTypes) => {
pillar_pattern.value = pattern
}
watch(() => form_state, changeParametres, { deep: true })
@ -180,6 +183,12 @@ const calc_table = computed(() => {
},
]
})
watch(open_calc, () => {
if (Object.keys(open_calc.value).length == 0) {
goto_cam.value = new Vector3(1, 1, 1)
}
})
</script>
<template>
<div class="container relative py-4">
@ -221,30 +230,40 @@ const calc_table = computed(() => {
<input id="remove_pillar" type="checkbox" v-model="form_state.remove_pillar" />
<label for="remove_pillar">Без столбов</label>
</div>
</div>
</div>
<div class="col-span-3 col-start-10">
<div class="form-row">
<div class="form-item form-item_color">
<label for="lamelle_color">Цвет ламелей</label>
<input id="lamelle_color" type="text" :value="getColorNameFromRal(lamelle_color)" :style="{
backgroundColor: getColorHexFromRal(lamelle_color) ?? 'transparent',
color: lamelle_text
}" disabled />
<DropdownColorPicker :cb="setLamelleColor" />
<DropdownPicker type="color" :cb="setLamelleColor" name="lamelle_color"
:goto_cam="new Vector3(1, 1, 1)">
<input id="lamelle_color" type="text" :value="getColorNameFromRal(lamelle_color)" :style="{
backgroundColor: getColorHexFromRal(lamelle_color) ?? 'transparent',
color: lamelle_text
}" />
</DropdownPicker>
</div>
<div class="form-item form-item_color">
<label for="pillar_color">Цвет столба</label>
<input id="pillar_color" type="text" :value="getColorNameFromRal(pillar_color)" :style="{
backgroundColor: getColorHexFromRal(pillar_color) ?? 'transparent',
color: pillar_text
}" disabled />
<DropdownColorPicker :cb="setPillarColor" />
<DropdownPicker type="color" :cb="setPillarColor" name="pillar_color"
:goto_cam="new Vector3(-1, -1, -1)">
<input id="pillar_color" type="text" :value="getColorNameFromRal(pillar_color)" :style="{
backgroundColor: getColorHexFromRal(pillar_color) ?? 'transparent',
color: pillar_text
}" />
</DropdownPicker>
</div>
<div class="form-item form-item_color">
<label for="pattern">Узор</label>
<input id="pattern" type="text" :value="pillar_pattern" />
<DropdownList :cb="setPillarPattern" />
<label for="pillar_pattern">Узор столба</label>
<DropdownPicker type="pattern" :cb="setPillarPattern" name="pillar_pattern"
:goto_cam="new Vector3(-1, 1, -1)">
<input id="pillar_pattern" type="text" :value="pillar_pattern" :style="{
backgroundImage: `url(${getFilename(pillar_pattern)})`
}" />
</DropdownPicker>
</div>
</div>
</div>
@ -252,8 +271,9 @@ const calc_table = computed(() => {
<div class="col-span-12 xl:col-span-6 xl:col-start-4 grid calc_table">
<div class="grid grid-cols-4 relative">
<template v-for="item in calc_table">
<div class="col-span-3 calc_table-maincell">{{
item.name }}</div>
<div class="col-span-3 calc_table-maincell">
{{ item.name }}
</div>
<div class="col-span-1 calc_table-maincell">{{ item.value }}</div>
</template>
</div>
@ -261,4 +281,14 @@ const calc_table = computed(() => {
</div>
</template>
</div>
</template>
</template>
<style lang="scss" scoped>
#pillar_pattern {
background-clip: content-box;
background-size: contain;
&[style*='url("/'] {
color: transparent;
}
}
</style>

View File

@ -1,48 +1,13 @@
<script setup lang="ts">
import { ralClassicPallette, getColorHexFromRal } from '@/components/ral'
import { ralClassicPallette } from '@/components/ral'
const props = defineProps(['color', 'cb', 'open', 'active'])
const open = props.open ?? true
const props = defineProps(['color', 'cb'])
const onClick = (color: string) => {
if (props.cb) {
props.cb(color)
}
toggleOpen()
}
const isOpenPicker = ref<boolean>(false)
const toggleOpen = (value: boolean = !isOpenPicker) => {
isOpenPicker.value = value !== undefined ? value : !isOpenPicker.value
}
const picker = ref()
const clickOutside = (e: Event) => {
if (!picker.value.contains(e.target)) {
toggleOpen(false)
}
}
onMounted(() => {
document.addEventListener('click', clickOutside)
})
onUnmounted(() => {
document.removeEventListener('click', clickOutside)
})
</script>
<template>
<div class="color_picker" ref="picker">
<div class="color_picker-selected" @click="open ? toggleOpen(!isOpenPicker) : onClick(props.color)"
:style="[props.color && { backgroundColor: getColorHexFromRal(props.color) ?? '' }]"
:class="[{ 'color_picker-selected__active': active }]"></div>
<div class="color_picker-changer flex flex-wrap" v-if="isOpenPicker">
<template v-for="col in ralClassicPallette">
<div class="color size-5" :class="[{ 'outline outline-primary': props.color == col.hex }]"
:style="[{ backgroundColor: col.hex }]" @click="onClick(col.code)">
</div>
</template>
<template v-for="col in ralClassicPallette">
<div class="color size-5" :class="[{ 'outline outline-primary': props.color == col.hex }]"
:style="[{ backgroundColor: col.hex }]" @click="props.cb(col.code)">
</div>
</div>
</template>
<style scoped>
.color_picker-selected:not([style*=color]) {
background-image: conic-gradient(from 50deg, orange, yellow, green, cyan, blue, violet)
}
</style>
</template>
</template>

View File

@ -1,49 +1,12 @@
<script setup lang="ts">
import { getFilename, patterns } from '../pattern';
const props = defineProps(['color', 'cb', 'open', 'active'])
const open = props.open ?? true
const is_open = ref<boolean>(false)
const picker = ref()
const onClick = (color: string) => {
if (props.cb) {
props.cb(color)
}
toggleOpen()
}
const toggleOpen = (value: boolean = !is_open) => {
is_open.value = value !== undefined ? value : !is_open.value
}
const clickOutside = (e: Event) => {
if (!picker.value.contains(e.target)) {
toggleOpen(false)
}
}
onMounted(() => {
document.addEventListener('click', clickOutside)
})
onUnmounted(() => {
document.removeEventListener('click', clickOutside)
})
const props = defineProps(['color', 'cb'])
</script>
<template>
<div class="list_picker" ref="picker">
<div class="list_picker-selected" @click="open ? toggleOpen(!is_open) : onClick(props.color)"
:class="[{ 'list_picker-selected__active': active }]">
<NuxtImg :src="getFilename(patterns[1].name)" />
</div>
<div class="list_picker-changer" v-if="is_open">
<div class="list_picker-list">
<template v-for="item in patterns">
<NuxtImg :src="getFilename(item.name)" class="list_picker-item" @click="onClick(item.name)"
v-if="item.filename" />
<span class=" list_picker-item list_picker-item--empty" @click="onClick(item.name)" v-else />
</template>
</div>
</div>
</div>
<template v-for="item in patterns">
<NuxtImg :src="getFilename(item.name)" class="picker-item" @click="props.cb(item.name)"
v-if="item.filename" />
<span class="picker-item list_picker-item--empty" @click="props.cb(item.name)" v-else />
</template>
</template>

View File

@ -0,0 +1,74 @@
<script setup lang="ts">
import { getColorHexFromRal } from '@/components/ral'
import { Vector3 } from 'three';
const props = defineProps(['color', 'cb', 'name', 'type', 'goto_cam'])
const goto_cam = use_goto_camera()
const open_calc = use_open_calc()
const picker = ref()
const is_open = ref<boolean>(open_calc.value.includes(props.name))
const toggleOpen = (value: boolean = !is_open) => {
is_open.value = value
if (value == true) open_calc.value = [props.name]
else if (value == false && open_calc.value.includes(props.name)) open_calc.value = []
if (value == true && props.goto_cam) goto_cam.value = props.goto_cam
}
const onClick = (color: string) => {
if (props.cb) {
props.cb(color)
}
}
const clickOutside = (e: Event) => {
if (!picker.value.contains(e.target)) {
toggleOpen(false)
}
}
onMounted(() => {
document.addEventListener('click', clickOutside)
})
onUnmounted(() => {
document.removeEventListener('click', clickOutside)
})
watch(open_calc, () => {
if (open_calc.value.includes(props.name) && is_open.value !== true) {
is_open.value = true
} else if (!open_calc.value.includes(props.name) && is_open.value !== false) {
is_open.value = false
}
})
</script>
<template>
<div class="picker" ref="picker">
<template v-if="$slots.default">
<div @click="toggleOpen(!is_open)">
<slot></slot>
</div>
</template>
<template v-else>
<div class="picker-selected" @click="toggleOpen(!is_open)"
:style="[props.color && { backgroundColor: getColorHexFromRal(props.color) ?? '' }]"></div>
</template>
<div class="picker-changer flex flex-wrap" v-if="is_open">
<template v-if="props.type == 'color'">
<DropdownColorPicker :color="props.color" :cb="onClick" />
</template>
<template v-else-if="props.type == 'pattern'">
<DropdownList :color="props.color" :cb="onClick" />
</template>
</div>
</div>
</template>
<style scoped>
.color_picker-selected:not([style*=color]) {
background-image: conic-gradient(from 50deg, orange, yellow, green, cyan, blue, violet)
}
</style>

View File

@ -0,0 +1,50 @@
<script setup lang="ts">
import { useLoop } from '@tresjs/core';
import { Vector3 } from 'three';
const { controls, camera } = useTresContext();
const goto_camera = use_goto_camera();
const COUNT = 30
type smooth = {
value: Vector3 | undefined,
count: number
}
const smooth_target = reactive({}) as smooth
const smooth_move = reactive({}) as smooth
const set_moveto = (obj: smooth, value: smooth["value"]) => {
obj.value = value
obj.count = COUNT
}
watch(goto_camera, () => {
smooth_target.value = goto_camera.value
smooth_target.count = COUNT
})
const koef = (1 / COUNT) * 3
const { onBeforeLoop } = useRenderLoop()
onBeforeLoop(() => {
if (smooth_target.value || smooth_move.value) {
if (smooth_target.value) {
(controls.value as any).target.lerp(smooth_target.value as Vector3, koef);
(controls.value as any).update();
smooth_target.count -= 1;
if (smooth_target.count == 1) {
set_moveto(smooth_target, undefined);
}
} else if (smooth_move.value) {
camera.value?.position.lerp(smooth_move.value as Vector3, koef);
camera.value?.updateMatrixWorld();
(controls.value as any).update();
smooth_move.count -= 1;
if (smooth_move.count == 1) {
set_moveto(smooth_move, undefined);
}
}
}
})
</script>
<template></template>

View File

@ -4,16 +4,12 @@ import {
CineonToneMapping,
Mesh,
Object3D,
Vector3,
EquirectangularReflectionMapping,
Euler,
PMREMGenerator,
} from 'three';
import { GainMapLoader, } from '@monogrid/gainmap-js'
//@ts-ignore
import { useGLTF, } from '@tresjs/cientos'
import { getColorHexFromRal, type ralTypes } from '../ral';
import { getFilename } from '../pattern';
const pillar_color = use_pillar_color()
const pillar_pattern = use_pattern()
@ -65,7 +61,7 @@ set_material(lamelle.value, getColorHexFromRal(lamelle_color.value));
[top, fastening, fence, fence_bottom, fence_top].map(
(el: Ref) => { set_material(el.value, getColorHexFromRal(pillar_color.value)) });
[fence_inner].map(
(el: Ref) => { set_material(el.value, getColorHexFromRal(pillar_color.value), lamelle_count.value) });
(el: Ref) => { set_material(el.value, getColorHexFromRal(pillar_color.value)) });
const { seek, seekAll } = useSeek()
watch(lamelle_color, () => {
@ -136,8 +132,12 @@ watch(() => [section_count.value, extra_section.value], () => {
watch(lamelle_count, () => {
[fence_inner].map(
(el: Ref) => { set_material(el.value, getColorHexFromRal(pillar_color.value), lamelle_count.value) });
camera.value?.lookAt(10, 10, 10)
(el: Ref) => { set_material(el.value, getColorHexFromRal(pillar_color.value),
{
pattern: pillar_pattern.value,
count: lamelle_count.value
}
) });
})
const pmremGenerator = new PMREMGenerator(renderer.value);
pmremGenerator.compileEquirectangularShader();

View File

@ -1,7 +1,7 @@
export const patterns = [
{ name: 'Без узора', },
{ name: 'Узор 1', filename: 'tile1.png' },
{ name: 'Еще раз узор 1', filename: 'tile1.png' },
{ name: 'А это узор 2', filename: 'tile2.jpg' },
]
export const getFilename = (name: patternTypes) => {
const el = patterns.find(el => el.name == name)

View File

@ -1,4 +1,5 @@
import type { ralTypes } from '@/components/ral'
import { Vector3 } from 'three'
import { type patternTypes } from '~/components/pattern'
export const predefPillarColors = ['3004', '7043', '6028', '5013', '8016', '1020', '3005', '4009']
@ -19,4 +20,7 @@ export const use_extra_section = () => useState('extra_section', () => 0)
export const use_total_length = () => useState('total_length', () => (((min + 104) * n) + 104) * 0.001)
export const use_min_length = () => useState('min_length', () => 700)
export const use_max_size = () => useState<number>('max_size', () => 20)
export const use_explosion_state = () => useState<boolean>('explosion_state', () => false)
export const use_explosion_state = () => useState<boolean>('explosion_state', () => false)
export const use_goto_camera = () => useState<Vector3 | undefined>('gotocam', () => undefined)
export const use_open_calc = () => useState<string[]>('open_calc', () => [])

View File

@ -1,6 +1,7 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: false },
hooks: {},
app: {
pageTransition: { name: 'page', mode: 'out-in' },

Binary file not shown.

Binary file not shown.

BIN
public/pattern/tile2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB