I have created a Google Map custom marker in a Vue component. I have set draggable = "true" but the marker is not draggable. How can I make the marker draggable and get the coordinates once it has been dragged?
/////////////////////////////////////////////////////////////
// basemap
<template>
<div>
<GmapMap :style="'height:500px'" :center="center" :zoom="zoom">
<Location :location="location1"/>
</GmapMap>
</div>
</template>
////////////////////////////////////////////////////////
// Location
<template>
<div>
<GmapCustomMarker
ref="editMarker"
:marker="location.pos"
:draggable="true"
class="g_marker"
alignment="center"
:z-index="999"
>
<div style="background-color: red; width: 50px; height: 50px; border: 2px solid black;"></div>
</GmapCustomMarker>
</div>
</template>
<script>
import GmapCustomMarker from "vue2-gmap-custom-marker";
I guess you are utilizing vue2-gmap-custom-marker package, right? If so, it does not seem to support dragging option. But the following example demonstrates how to implement a draggable marker as a Vue component (which is based on this example):
<template>
<div :draggable="true" :style="{'position': 'absolute'}">
<slot />
</div>
</template>
<script>
import * as VueGoogleMaps from "vue2-google-maps";
/* global google */
export default {
mixins: [VueGoogleMaps.MapElementMixin],
props: {
position: {
type: Object,
default: undefined
}
},
methods: {},
data() {
return {
current: null,
origin: null
};
},
provide() {
const self = this;
return this.$mapPromise.then(map => {
class Overlay extends google.maps.OverlayView {
constructor(map) {
super();
this.setMap(map);
self.current = new google.maps.LatLng(
self.position.lat,
self.position.lng
);
}
draw() {
const container = self.$el;
let pos = this.getProjection().fromLatLngToDivPixel(self.current);
container.style.left = pos.x + "px";
container.style.top = pos.y + "px";
}
onAdd() {
const container = self.$el;
const panes = this.getPanes();
panes.floatPane.appendChild(container);
google.maps.event.addDomListener(
self.$map.getDiv(),
"mouseleave",
() => {
google.maps.event.trigger(container, "mouseup");
}
);
google.maps.event.addDomListener(container, "mousedown", e => {
container.style.cursor = "move";
self.$map.set("draggable", false);
self.origin = e;
const mouseMoveHandler = google.maps.event.addDomListener(
self.$map.getDiv(),
"mousemove",
e => {
let left = self.origin.clientX - e.clientX;
let top = self.origin.clientY - e.clientY;
let pos = self.$overlay
.getProjection()
.fromLatLngToDivPixel(self.current);
let latLng = self.$overlay
.getProjection()
.fromDivPixelToLatLng(
new google.maps.Point(pos.x - left, pos.y - top)
);
self.origin = e;
self.current = latLng;
this.draw();
}
);
google.maps.event.addDomListener(container, "mouseup", () => {
self.$map.set("draggable", true);
container.style.cursor = "default";
google.maps.event.removeListener(mouseMoveHandler);
});
});
}
onRemove() {
self.$el.remove();
}
}
this.$overlay = new Overlay(map);
});
},
destroyed() {
this.$overlay.setMap(null);
this.$overlay = undefined;
}
};
</script>
Here is a demo
Related
How do i use a Jasvscript-based Extension, for example the IconMarkupExtension from https://forge.autodesk.com/blog/placing-custom-markup-dbid in my Angular-based app.
I tried the following:
Import the Javascript file:
import IconMarkupExtension from './IconMarkupExtension';
using the extension by defining in the viewerConfig:
constructor(private router: Router, private auth: AuthService, private api: ApiService, private messageService: MessageService) {
this.viewerOptions3d = {
initializerOptions: {
env: 'AutodeskProduction',
getAccessToken: (async (onGetAccessToken) => {
const authToken: AuthToken = await this.api.get2LToken();
this.auth.currentUserValue.twolegggedToken = authToken.access_token;
onGetAccessToken(this.auth.currentUserValue.twolegggedToken, 30 * 60);
}),
api: 'derivativeV2',
},
viewerConfig: {
extensions: ['IconMarkupExtension'], // [GetParameterExtension.extensionName],
theme: 'dark-theme',
},
onViewerScriptsLoaded: this.scriptsLoaded,
onViewerInitialized: (async (args: ViewerInitializedEvent) => {
if (this.platform.currentProject.encodedmodelurn) {
args.viewerComponent.DocumentId = this.platform.currentProject.encodedmodelurn;
this.loadCustomToolbar();
// this.loadIconMarkupExtension();
}
else {
// Graphische Anpassung
$('#forge-viewer').hide();
// args.viewerComponent.viewer.uninitialize();
this.messageService.clear();
this.messageService.add({ key: 'noModel', sticky: true, severity: 'warn', summary: 'NOT AVAILABLE', detail: 'Do you want to add a Model' });
this.platform.app.openOverlay();
}
}),
// Muss true sein
showFirstViewable: true,
// Ist falsch gesetzt => GuiViewer3D => Buttons asugeblendet in CSS
headlessViewer: false,
};
}
and finally register after onViewerScriptsLoaded
public scriptsLoaded() {
// Extension.registerExtension(GetParameterExtension.extensionName, GetParameterExtension);
Extension.registerExtension('IconMarkupExtension', IconMarkupExtension);
}
The problem i'm facing that i get an error cause of
class IconMarkupExtension extends Autodesk.Viewing.Extension {
Autodesk is not defined
Thank you
The solution was to rewrite the extension, then as usual register and load the extension.
/// <reference types="forge-viewer" />
import { Extension } from '../../viewer/extensions/extension';
declare const THREE: any;
export class IconMarkupExtension extends Extension {
// Extension must have a name
public static extensionName: string = 'IconMarkupExtension';
public _group;
public _button: Autodesk.Viewing.UI.Button;
public _icons;
public _inputs;
public _enabled;
public _frags;
public onClick;
constructor(viewer, options) {
super(viewer, options);
this._group = null;
this._button = null;
this._enabled = false;
this._icons = options.icons || [];
this._inputs = options.inputs || [];
this.onClick = (id) => {
this.viewer.select(id);
this.viewer.fitToView([id], this.viewer.model, false);
};
}
public load() {
const updateIconsCallback = () => {
if (this._enabled) {
this.updateIcons();
}
};
this.viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, updateIconsCallback);
this.viewer.addEventListener(Autodesk.Viewing.ISOLATE_EVENT, updateIconsCallback);
this.viewer.addEventListener(Autodesk.Viewing.HIDE_EVENT, updateIconsCallback);
this.viewer.addEventListener(Autodesk.Viewing.SHOW_EVENT, updateIconsCallback);
return true;
}
unload() {
// Clean our UI elements if we added any
if (this._group) {
this._group.removeControl(this._button);
if (this._group.getNumberOfControls() === 0) {
this.viewer.toolbar.removeControl(this._group);
}
}
return true;
}
onToolbarCreated() {
// Create a new toolbar group if it doesn't exist
this._group = this.viewer.toolbar.getControl('customExtensions');
if (!this._group) {
this._group = new Autodesk.Viewing.UI.ControlGroup('customExtensions');
this.viewer.toolbar.addControl(this._group);
}
// Add a new button to the toolbar group
this._button = new Autodesk.Viewing.UI.Button('IconExtension');
this._button.onClick = (ev) => {
this._enabled = !this._enabled;
this.showIcons(this._enabled);
this._button.setState(this._enabled ? 0 : 1);
};
// this._button.setToolTip(this.options.button.tooltip);
this._button.setToolTip('Showing Panel Information');
// #ts-ignore
this._button.container.children[0].classList.add('fas', 'fa-cogs');
this._group.addControl(this._button);
// Iterate through _inputs
this._inputs.forEach(input => {
var name = '';
if (input.objectPath.indexOf('/')) {
name = input.objectPath.split('/')[input.objectPath.split('/').length - 1];
}
else {
name = input.objectPath;
}
this.viewer.search(name, (idArray) => {
if (idArray.length === 1) {
// console.log(idArray);
this._icons.push({ dbId: idArray[0], label: name, css: 'fas fa-question-circle' });
}
else if (idArray.length !== 1) {
console.log('idArray.length !== 1 getMarkups!!');
}
}, (err) => {
console.log('Something with GETTING MARKUPS went wrong');
}, ['name']);
});
}
showIcons(show) {
console.log(this.viewer);
console.log(this._icons);
// #ts-ignore
const $viewer = $('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer');
// remove previous...
// #ts-ignore
$('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer label.markup').remove();
if (!show) return;
// do we have anything to show?
if (this._icons === undefined || this._icons === null) return;
// do we have access to the instance tree?
const tree = this.viewer.model.getInstanceTree();
if (tree === undefined) { console.log('Loading tree...'); return; }
const onClick = (e) => {
this.onClick($(e.currentTarget).data('id'));
};
this._frags = {};
for (var i = 0; i < this._icons.length; i++) {
// we need to collect all the fragIds for a given dbId
const icon = this._icons[i];
this._frags['dbId' + icon.dbId] = [];
// create the label for the dbId
const $label = $(`
<label class="markup update" data-id="${icon.dbId}">
<span class="${icon.css}"> ${icon.label || ''}</span>
</label>
`);
$label.css('display', this.viewer.isNodeVisible(icon.dbId) ? 'block' : 'none');
$label.on('click', onClick);
$viewer.append($label);
// now collect the fragIds
const _this = this;
tree.enumNodeFragments(icon.dbId, (fragId) => {
_this._frags['dbId' + icon.dbId].push(fragId);
_this.updateIcons(); // re-position of each fragId found
});
}
}
getModifiedWorldBoundingBox(dbId) {
var fragList = this.viewer.model.getFragmentList();
const nodebBox = new THREE.Box3();
// for each fragId on the list, get the bounding box
for (const fragId of this._frags['dbId' + dbId]) {
const fragbBox = new THREE.Box3();
fragList.getWorldBounds(fragId, fragbBox);
nodebBox.union(fragbBox); // create a unifed bounding box
}
return nodebBox;
}
updateIcons() {
// #ts-ignore
// const label = $('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer .update')[0];
// const $label = $(label);
// #ts-ignore
// #ts-ignore
$(() => {
// #ts-ignore
const labels = Array.from($('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer .update'));
for (const label of labels) {
const $label = $(label);
const id = $label.data('id');
// get the center of the dbId(based on its fragIds bounding boxes)
const pos = this.viewer.worldToClient(this.getModifiedWorldBoundingBox(id).center());
// position the label center to it
$label.css('left', Math.floor(pos.x - $label[0].offsetWidth / 2) + 'px');
$label.css('top', Math.floor(pos.y - $label[0].offsetHeight / 2) + 'px');
$label.css('display', this.viewer.isNodeVisible(id) ? 'block' : 'none');
}
});
// for (const label of $('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer .update')) {
// const $label = $(label);
// const id = $label.data('id');
// // #ts-ignore
// // const $label = $('#' + this.viewer.clientContainer.id + ' div.adsk-viewing-viewer .update')[0];
// // const id = $label.data('id');
// // get the center of the dbId (based on its fragIds bounding boxes)
// const pos = this.viewer.worldToClient(this.getModifiedWorldBoundingBox(id).center());
// // console.log(pos);
// // position the label center to it
// $label.css('left', Math.floor(pos.x - $label[0].offsetWidth / 2) + 'px');
// $label.css('top', Math.floor(pos.y - $label[0].offsetHeight / 2) + 'px');
// $label.css('display', this.viewer.isNodeVisible(id) ? 'block' : 'none');
// }
}
}
Use the Viewer's Typescript definitions - see here for install and set them up.
Here's a couple of samples as well if you are after working code:
https://github.com/dukedhx/viewer-ioniccapacitor-angular
https://github.com/dukedhx/viewer-nativescript-angular
I've built a p5 sketch in my app.component file but it seems to always be at the bottom of all other components. Right now I'm at a loss on how to fix this so my sketch is always at the top. Any help on how I can control the positioning of my sketch would be greatly appreciated!
<!-- this is the main app.component.html -->
<div class="nav">
<button [routerLink]="['/']"> Landing </button>
<button [routerLink]="['/resume']"> Resume </button>
<button [routerLink]="['/projects']"> Projects </button>
<button [routerLink]="['/pagenotfound']"> pagenotfound </button>
</div>
<div style="text-align:center">
<h1> {{ title }} </h1>
</div>
<router-outlet></router-outlet>
This is the app.component.ts
import {
Component
} from '#angular/core';
import 'p5';
declare
var p5: any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = '';
test: any
private p5sketch;
constructor() {}
ngOnInit() {
this.createCanvas();
if (window.innerWidth < 600) {
this.title = 'Welcome to my Portfolio'
}
}
private createCanvas() {
this.p5sketch = new p5(this.sketch);
}
private sketch(p: any) {
// not showing for mobile because it looks horrible and takes up processing
if (window.innerWidth < 600) {
return;
}
var canvasVector = {
'width': 1200,
'height': 200
}
var font;
var vehicles = [];
// preload
p.preload = () => {
font = p.loadFont('../assets/AvenirNextLTPro-Demi.otf');
}
// setup
p.setup = () => {
p.createCanvas(window.innerWidth, canvasVector.height);
p.background(51);
let verticies = font.textToPoints("Welcome to my Portfolio", p.width * .1, p.height / 1.5, 100);
verticies.map((dot) => vehicles.push(new Vehicle(dot.x, dot.y)));
};
function Vehicle(x, y) {
this.pos = p.createVector(p.random(p.width), p.random(p.height));
this.target = p.createVector(x, y);
this.vel =
p5.Vector.random2D();
// p.createVector();
this.acc = p.createVector();
this.maxspeed = 10;
this.maximumforce = 1;
}
Vehicle.prototype.update = function() {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.acc.mult(0);
}
Vehicle.prototype.show = function() {
p.stroke(255);
p.strokeWeight(5);
p.point(this.pos.x, this.pos.y);
}
Vehicle.prototype.behaviors = function() {
var arrive = this.arrive(this.target);
var mouse = p.createVector(p.mouseX, p.mouseY);
var flee = this.flee(mouse).mult(5);
this.applyForce(arrive);
this.applyForce(flee);
}
Vehicle.prototype.applyForce = function(f) {
this.acc.add(f);
}
Vehicle.prototype.flee = function(target) {
var desired = p5.Vector.sub(target, this.pos);
var d = desired.mag();
if (d < 50) {
desired.setMag(this.maxspeed);
desired.mult(-1);
var steer = p5.Vector.sub(desired, this.vel);
steer.limit(this.maximumforce)
return steer;
} else {
return p.createVector(0, 0);
}
}
Vehicle.prototype.arrive = function(target) {
var desired = p5.Vector.sub(target, this.pos);
var d = desired.mag();
var speed = (d < 100) ?
speed = p.map(d, 0, 100, 0, this.maxspeed) :
this.maxspeed;
desired.setMag(speed);
var steer = p5.Vector.sub(desired, this.vel);
steer.limit(this.maximumforce)
return steer;
}
// draw
p.draw = () => {
p.background(51);
for (let i = 0; i < vehicles.length; i++) {
var v = vehicles[i];
v.behaviors();
v.update();
v.show();
}
}
// end draw
}
}
If you're using the default createCanvas() function, then it creates a new canvas and adds it to the bottom of the page.
You can move that canvas using the P5.dom library, specifically the parent() function. From the reference:
// in the html file:
// <div id="myContainer"></div>
// in the js file:
var cnv = createCanvas(100, 100);
cnv.parent('myContainer');
This will move the canvas into the myContainer div element.
I am adding svg to canvas and want to set custom Element Parameters. I shows custom parameter when we console log getActiveObject() but when we use canvas.toJSON() Element Parameter node values does not change.
var canvas = new fabric.Canvas('designcontainer'),
/* Save additional attributes in Serialization */
var ElementParameters = {
ElementType:'',
imageType:'',
top:'',
left:'',
colors:'',
isBaseTier:'',
atLevel:''
};
fabric.Object.prototype.toObject = (function (toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this), {
ElementParameters:{
ElementType:'',
imageType:'',
top:'',
left:'',
colors:'',
isBaseTier:'',
atLevel:''
},
});
};
})(fabric.Object.prototype.toObject);
/* End : Save additional attributes in Serialization */
var Designer = {
addElement: function(e,p){ /* e = element, image src | p = parameters set for image */
if(p.imageType == "svg"){
if(p.ElementType == "caketier"){
var group = [];
console.log('Before ');
console.log(ElementParameters);
$.extend(ElementParameters,p);
console.log('After ');
console.log(ElementParameters);
fabric.loadSVGFromURL(e,function(objects,options){
var shape = fabric.util.groupSVGElements(objects,options);
var bound = shape.getBoundingRect();
shape.set({
left: p.left,
top: p.top,
width:bound.width+2,
height:bound.height,
angle:0,
centeredScaling:true,
ElementParameters:ElementParameters
});
if(shape.paths && baseColor.length > 0){
for(var i = 0;i<shape.paths.length;i++) shape.paths[i].setFill(baseColor[i]);
}
canvas.add(shape);
shape.setControlsVisibility(HideControls);
canvas.renderAll();
},function(item, object) {
object.set('id',item.getAttribute('id'));
group.push(object);
});
}
}
}
}
$(".tierbox").on('click',function(){
var i = $(this).find('img'),
src = i.attr('src'),
param = i.data('parameters');
Designer.addElement(src,param);
});
Now when I call JSON.stringify(json), Element Parameter node does not get overwrite with values set in shape.set() method.
Replace fabric.Object.prototype.toObject = (function (toObject) { ... } to
fabric.Object.prototype.toObject = (function (toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this), {
ElementParameters:this.ElementParameters
});
};
})(fabric.Object.prototype.toObject);
I have a problem with changes of material of some elements
when geometry is loaded:
_this.viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, () => {
changeModelMaterial()
});
...
const changeModelMaterial = () => {
const grey = new THREE.Color(0.5, 0.5, 0.5);
let dbIds = getDbIds()
changeAllElementsMaterial(grey)
setMaterialOfDbIds(dbIds)
}
code that i`m using to change material:
const changeAllElementsMaterial = (color) => {
const fragmentList = _this.viewer.model.getFragmentList();
for (let materialId of fragmentList.materialids) {
if (fragmentList.materialmap[materialId]) {
fragmentList.materialmap[materialId].map = null
fragmentList.materialmap[materialId].color = color
fragmentList.materialmap[materialId].needsUpdate = true;
}
}
_this.viewer.impl.invalidate(true);
}
const setMaterialOfDbIds = (dbIds) => {
var color_diffuse = 0xAB00EE;
var color_specular = 0xEEABEE;
var colorM = new THREE.MeshPhongMaterial({
color: color_diffuse,
specular: color_specular
});
_this.viewer.impl.matman().addMaterial(
'ADN-Material-' +
"common color material", // or a GUID
colorM,
true);
for (let dbId of dbIds) {
_this.viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
_this.viewer.model.getFragmentList().setMaterial(fragId, colorM);
});
}
_this.viewer.impl.invalidate(true);
}
It works, because I see that materials of model are changed, but the problem is that materials back to default after ~1-2 sec.
After this I cannot change material even with run this code manually.
Question is why Viewer is locking material change after this 2 sec, how to prevent it
And maybe you will be able to tell me what i can do better with material changes, eg. maybe something better that running my code after GEOMETRY_LOAD. The best would be change material before first render of model
........
hint:
when change event from GEOMETRY_LOADED_EVENT to OBJECT_TREE_CREATED_EVENT "sometimes" but only sometimes it works well (materials stay to the end of working with model), but mostly when i run my method after OBJECT_TREE_CREATED it not working (even not working by run it manually, materials are in some way locked). So I suspect that problem is between time of GEOMETRY_LOAD and OBJECT_TREE_CREATED
I will be grateful for any help
==============================full code==============================
index.html
<div id="main">
<div id="MyViewerDiv"></div>
<button id="open-nav-button" onClick="showDocInfo()">test</button>
</div>
<script src="https://developer.api.autodesk.com/derivativeservice/v2/viewers/three.min.js"></script>
<script src="https://developer.api.autodesk.com/derivativeservice/v2/viewers/viewer3D.min.js"></script>
<script type="text/javascript" src="lib/jquery.min.js"></script>
<script src="js/autodesk-viewer.js"></script>
<script src="js/extension/test-extension.js"></script>
<script>
const autodeskViewer = new AutodeskViewer()
const showDocInfo = () => {
autodeskViewer.showDocInfo()
}
</script>
autodesk-viewer.js
var AutodeskViewer = (function () {
function AutodeskViewer() {
var _this = this;
this.urn = 'urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2UtamF2YS1zYW1wbGUtYXBwLTFzcGduazdqcWpxdjhmYXV0YmNzd2R0cGdvN3VtNWY1L1BPQy1Gb3JnZS1JVCUyMDIwMTclMjBSdWNoXzEwMDUxNy5ud2Q';
this.initializeViewer = function (containerId, documentId) {
_this.viewerApp = new Autodesk.Viewing.ViewingApplication(containerId);
var config = {
extensions: ['TestExtension']
};
_this.viewerApp.registerViewer(_this.viewerApp.k3D, Autodesk.Viewing.Private.GuiViewer3D, config);
_this.viewerApp.loadDocument(documentId, _this.onDocumentLoadSuccess, _this.onDocumentLoadFailure);
}
this.onDocumentLoadSuccess = function (doc) {
const viewables = _this.viewerApp.bubble.search(av.BubbleNode.MODEL_NODE);
if (viewables.length === 0) {
return;
}
_this.viewerApp.selectItem(viewables[0].data, _this.onItemLoadSuccess, _this.onItemLoadFail);
_this.viewer3d = _this.viewerApp.getCurrentViewer();
}
this.onDocumentLoadFailure = (viewerErrorCode) => {}
this.onItemLoadSuccess = (viewer) => {
_this.viewer = viewer
}
this.onItemLoadFail = (errorCode) => {}
this.initialize = () => {
var options = {
env: 'AutodeskProduction',
getAccessToken: _this.getToken,
refreshToken: _this.getToken
};
Autodesk.Viewing.Initializer(options, _this.initCallback);
};
this.initCallback = function () {
_this.initializeViewer('MyViewerDiv', _this.urn, '3d');
};
this.getToken = function (onGetAccessToken) {
$.get("forge/oauth/token")
.done(function (data) {
token = data
onGetAccessToken(token, 60 * 60);
})
.fail(function (error) {
console.log('ERROR', error);
});
};
this.showDocInfo = function () {};
this.initialize();
}
return AutodeskViewer;
}());
test-extension.js
var _self;
var _viewer;
var _tempValue = 0;
function TestExtension(viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
_self = this;
_viewer = viewer;
}
const changeModelMaterial = () => {
// _tempValue++;
// if (_tempValue == 2) {
const elements = [4340, 4342, 4344, 4346, 4348, 4367, 4371, 4375, 4380, 4452, 4468, 4488, 4503, 4517, 4520, 4522, 4524, 4526, 4528, 4530]
changeAllElementsMaterial(new THREE.Color(0.5, 0.5, 0.5))
setMaterialOfDbIds(elements)
_tempValue = 0
// }
}
const changeAllElementsMaterial = (color) => {
var fragmentList = _viewer.model.getFragmentList();
for (let materialId of fragmentList.materialids) {
if (fragmentList.materialmap[materialId]) {
fragmentList.materialmap[materialId].map = null
fragmentList.materialmap[materialId].color = color
fragmentList.materialmap[materialId].needsUpdate = true;
}
}
_viewer.impl.invalidate(true);
}
const setMaterialOfDbIds = (dbIds) => {
var colorM = new THREE.MeshPhongMaterial({
color: new THREE.Color(0xAB00EE)
});
for (let dbId of dbIds) {
_viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
_viewer.model.getFragmentList().setMaterial(fragId, colorM);
});
}
_viewer.impl.invalidate(true);
}
TestExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
TestExtension.prototype.constructor = TestExtension;
TestExtension.prototype.load = function () {
_viewer.addEventListener(Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, changeModelMaterial)
// _viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, changeModelMaterial)
return true
};
TestExtension.prototype.unload = function () {
return true
};
Autodesk.Viewing.theExtensionManager.registerExtension('TestExtension', TestExtension);
I found the solution, quite accidentally... from other thing i tried to do
before:
const setMaterialOfDbIds = (dbIds) => {
var color_diffuse = 0xAB00EE;
var color_specular = 0xEEABEE;
var colorM = new THREE.MeshPhongMaterial({
color: color_diffuse,
specular: color_specular
});
_this.viewer.impl.matman().addMaterial("common color material", colorM, true);
for (let dbId of dbIds) {
_this.viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
_this.viewer.model.getFragmentList().setMaterial(fragId, colorM);
});
}
_this.viewer.impl.invalidate(true);
}
after
const setMaterialOfDbIds = (dbIds) => {
var color_diffuse = 0xAB00EE;
var color_specular = 0xEEABEE;
var colorM = new THREE.MeshPhongMaterial({
color: color_diffuse,
specular: color_specular
});
_this.viewer.impl.matman().addMaterial("common color material", colorM, true);
for (let dbId of dbIds) {
_this.viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
_this.viewer.model.getFragmentList().setMaterial(fragId, colorM);
var fragProxy = _this.viewer.impl.getFragmentProxy(_this.viewer.model, fragId)
fragProxy.updateAnimTransform()
});
}
_this.viewer.impl.invalidate(true);
}
Really I don`t know why adding
var fragProxy = _this.viewer.impl.getFragmentProxy(_this.viewer.model, fragId)
fragProxy.updateAnimTransform()
made the difference, i didn`t saw anything like that in any example of updating material.
What is interesting this code is running for only few elements in model, but it works for even those elements that materials changed before (in changeAllElementsMaterial method).
#Philippe Leefsma if you understand it pls tell something more why it works
So far I cannot reproduce the issue on my side, I am using the following code (ES7) extracted from that extension: Viewing.Extension.Material
createColorMaterial (color) {
const material = new THREE.MeshPhongMaterial({
specular: new THREE.Color(color),
side: THREE.DoubleSide,
reflectivity: 0.0,
color
})
const materials = this.viewer.impl.getMaterials()
materials.addMaterial(
this.guid(),
material,
true)
return material
}
async onModelCompletedLoad() {
const material = this.createColorMaterial(0xFF0000)
const model = this.viewer.model
const fragIds = await Toolkit.getFragIds(model)
fragIds.forEach((fragId) => {
model.getFragmentList().setMaterial(
fragId, material)
})
this.viewer.impl.sceneUpdated(true)
}
The onModelCompletedLoad is a custom event fired when both GEOMETRY_LOADED_EVENT and OBJECT_TREE_CREATED_EVENT have been fired.
Take a look at this article for more details: Asynchronous viewer events notification
I doubt you can easily change the materials before the model is first rendered, however you could use a custom overlay that hides the model until your custom logic has performed all required steps, this is the approach I am using in my demos at: https://forge-rcdb.autodesk.io/configurator
After loading a model, all custom materials are being persisted fine:
The material extension can be tested live from there.
Hope that helps
I have the following React component that grabs a video frame and loads the frame image into another image component upon specific events(pause, seek).
I wish to be able to set the current video frame position on rendering of the video element then add a seek event afterwards .
The problem I am experiencing is setting the frame position in componentDidMount with: video.currentTime = this.props.currentVideoFramePosition actually calls the seek event even though I added the event afterwards.
How can I modify the below code to set the current time of the video without triggering the seek event.
I am trying to avoid this on page load because the draw function is returning a value to another component which I do not want…….
import React from 'react';
export default class MyComponent extends React.Component {
constructor() {
super();
const self = this;
this.frameCaptureListener = function(e){
self.draw();
};
}
componentDidMount() {
var video = document.getElementById('video');
document.getElementById('video-canvas').style.display='none';
video.currentTime = this.props.currentVideoFramePosition;
video.addEventListener('pause', this.frameCaptureListener, false);
video.addEventListener('seeked', this.frameCaptureListener, false);
}
componentWillUnmount(){
var video = document.getElementById('video');
video.removeEventListener('pause', this.frameCaptureListener, false );
video.removeEventListener('seeked', this.frameCaptureListener, false);
}
draw(){
var video = document.getElementById('video');
var canvas = document.getElementById('video-canvas');
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = 1280;
canvas.height = 720;
context.drawImage( video, 0, 0, canvas.width, canvas.height);
var dataURL = canvas.toDataURL();
video.setAttribute('crossOrigin', 'anonymous');
var frameData = {imgSrc:dataURL, lastFramePosition:video.currentTime};
return this.props.onPause(frameData);
}
onArrowNavClick(navNum){
const self = this;
var video = document.getElementById('video');
video.currentTime = video.currentTime + navNum;
video.pause();
}
render(){
const videoUrl = this.props.videoUrl;
return(
<div>
<div className="framegrab-videocontainer">
<img src="/images/arrow_left_grab.svg" className="arrow-left" onClick={() => this.onArrowNavClick(-1)}></img>
<video id="video" src={videoUrl} className="framegrab-video" controls crossOrigin="anonymous" />
<img src="/images/arrow_right_grab.svg" className="arrow-right" onClick={() => this.onArrowNavClick(1)}></img>
</div>
<canvas id="video-canvas" ></canvas>
</div>
);
}
}
Here is how I am doing it with audio. Your problem with triggering seeking might be fixed by using a if (video.captureTime == props.captureTime) return; in frameCaptureListener.
import React from 'react';
import kshuffle, {knuthShuffle} from 'knuth-shuffle';
import JProgressBar from '../common/jProgressBar';
import PlayerActions from './player.actions';
let PlayerCtrlSty = {
backgroundColor: '#072202',
color: '#eaab5f',
margin: 'auto',
padding: '3px',
width: '320px'
}
let timerLeftSty = {minWidth: '40px'}
let timerRightSty = {textAlign: 'right', minWidth: '40px'}
let getTime = (time) => {
var minutes = time < 60 ? 0 : Math.floor(time / 60);
var seconds = Math.floor(time - (minutes * 60)) * .01;
return (minutes + seconds).toFixed(2);
}
class PlayerCtrlRender extends React.Component {
render() {
let index = this.state.playIndex;
let currentTrack = this.state.randomOn ? this.state.shuffledQueue[index] : this.props.queue[index];
let source = 'http://192.168.0.101:3950/play/' + currentTrack.location;
let title = currentTrack.title;
let progressData = {count: this.state.progressCount * 100, index: this.state.progressIndex * 100};
return (
<div id='PlayerCtrlSty' style={PlayerCtrlSty}>
<div className='FlexBox'>
<div style={timerLeftSty}>{this.state.progressIndex}</div>
<PlayerActions playing={this.state.isPlaying} clickHandler={this.clickHandler}/>
<div style={timerRightSty}>{this.state.progressCount}</div>
</div>
<JProgressBar data={progressData} position='none' />
<div id="title" style={{textAlign: 'center'}}>{title}</div>
<audio
ref="audioDiv"
src={source}
onDurationChange={this.onDurationChange}
onTimeUpdate={this.onTimeUpdate}
onEnded={this.nextSong}
/>
</div>
);
}
}
export default class PlayerCtrl extends PlayerCtrlRender {
constructor() {
super();
this.state = {
playIndex: 0,
queueLength: 1,
isPlaying: false,
progressCount: 0,
progressIndex: 0,
shuffledQueue: [{title: '', location: ''}],
randomOn: false
};
}
componentDidMount = () => {
let queue = knuthShuffle(this.props.queue.slice(0));
this.setState({queueLength: queue.length, shuffledQueue: queue});
this.refs.audioDiv.volume = .1;
};
clickHandler = (buttonid) => {
this.refs.audioDiv.autoplay = false;
switch (buttonid) {
case 'play': this.refs.audioDiv.play(); this.setState({isPlaying: true}); break;
case 'pause': this.refs.audioDiv.pause(); this.setState({isPlaying: false}); break;
case 'back': this.refs.audioDiv.currentTime = 0; break;
case 'next': this.nextSong(); break;
case 'random': this.refs.audioDiv.autoplay = this.state.isPlaying;
this.setState({randomOn: !this.state.randomOn}); break;
}
};
nextSong = () => {
this.refs.audioDiv.autoplay = this.state.isPlaying;
this.refs.audioDiv.currentTime = 0;
let newIndex = this.state.playIndex + 1;
if (newIndex == this.state.queueLength) newIndex = 0;
this.setState({playIndex: newIndex});
};
onDurationChange = () => {
let duration = this.refs.audioDiv.duration;
duration = getTime(Math.floor(duration));
this.setState({progressCount: duration})
this.setState({progressIndex: 0})
};
onTimeUpdate = () => {
let currentTime = this.refs.audioDiv.currentTime;
currentTime = getTime(Math.floor(currentTime));
this.setState({progressIndex: currentTime})
};
}