Creating a 2D Snapshot using three JS library inside Node JS - node.js

I'm using the below code to create 3D model using the Three JS library. Code was running successfully in node JS, but when I'm trying to save an image, I'm getting output as a blank image.
I do have a ThreeUtil JS file which is holding the constants of my shader materials and a couple of functions. There is no problem with this utility file
Observation: I see my buffer data with all element values as zero itself. Can someone let me know where exactly I'm going wrong with my implementation?
Please find the code in below sandbox URL
https://codesandbox.io/s/boring-lewin-7msxm
Attached sample input file as sampleinput.json . from an endpoint need to class main typescript file which will perform the creation and capturing screenshot
import Jimp from 'jimp';
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
const THREE = require('three');
const THREESTLLoader = require('three-stl-loader');
const THREEObjLoader = require('three-obj-loader');
const jsdom = require('jsdom');
const gl = require('gl');
import { WebGLRenderTarget } from 'three';
const HOMUNCULUS_OBJ_URL = './assets/3d-homunculus.obj';
const Canvas = require('canvas');
export class ThreeDimensionalModel {
private prostateMeshData: ProstateMeshData;
private width: number = 660;
private height: number = 660;
private dimensions: ThreeCanvasDimensions;
private scenes: Scenes;
private camera: THREE.PerspectiveCamera;
private matrix: THREE.Matrix4;
private rotationMatrix: THREE.Matrix4;
private prostateMeshBoundingBox: THREE.Box3;
private renderer: THREE.WebGLRenderer;
constructor() {
const { JSDOM } = jsdom;
const { window } = new JSDOM();
const { document } = (new JSDOM('')).window;
// #ts-ignore
global.window = window;
// #ts-ignore
global.document = document;
// #ts-ignore
global.window.decodeURIComponent = global.decodeURIComponent;
// Adding XML HTTP Requests library
// #ts-ignore
global.XMLHttpRequest = XMLHttpRequest;
this.dimensions = this.calculateDimensions();
this.scenes = this.initializeScenes();
this.camera = this.initializeCamera();
this.decorateScenes();
this.loadModels(this.prostateMeshData);
this.renderer = this.createRenderer();
this.animate();
}
private calculateDimensions = (): ThreeCanvasDimensions => ({
width: this.width,
height: this.height,
aspect: this.width / this.height,
})
private initializeScenes = (): Scenes => ({
main: new THREE.Scene(),
homunculus: new THREE.Scene(),
})
private initializeCamera(): THREE.PerspectiveCamera {
const camera = new THREE.PerspectiveCamera(
70,
this.dimensions.aspect,
1,
10000);
camera.position.z = 100;
return camera;
}
private decorateScenes(): void {
const ambientLight = new THREE.AmbientLight(0x505050);
const spotLight = this.getSpotLight();
const plane = this.getPlane();
ThreeUtil.addObjectToAllScenes(this.scenes, spotLight);
ThreeUtil.addObjectToAllScenes(this.scenes, ambientLight);
ThreeUtil.addObjectToAllScenes(this.scenes, plane);
}
private loadModels(prostateMeshData: ProstateMeshData): void {
const loader = new THREESTLLoader(THREE);
const dataUri = `data:text/plain;base64,${prostateMeshData.prostateMeshUrl}`;
loader.prototype.load(dataUri, (geometry: THREE.Geometry) => {
this.setModelMatricesAndProstateMeshBoundings(geometry);
this.setupHomunculusMesh();
const prostateMesh = this.createProstateMesh(geometry);
let biopsyMeshes;
if (prostateMeshData.biopsies) {
biopsyMeshes = this.createBiopsyMeshes(prostateMeshData.biopsies);
}
if (prostateMeshData.rois) {
this.loadRoiMeshesOnScene(prostateMeshData.rois);
}
this.addToMainScene(prostateMesh);
if (prostateMeshData.biopsies) {
biopsyMeshes.forEach(biopsyMesh => this.addToMainScene(biopsyMesh));
}
this.scaleInView();
});
}
public saveAsImage() {
// after init of renderer
const renderTarget = new WebGLRenderTarget(this.width, this.height);
const target = this.renderer.getRenderTarget() || renderTarget;
const size = this.renderer.getSize();
// #ts-ignore
target.setSize(size.width, size.height);
this.renderer.setRenderTarget(renderTarget);
this.renderer.render( this.scenes.main, this.camera, target );
// retrieve buffer from render target
const buffer = new Uint8Array(this.width * this.height * 4);
this.renderer.readRenderTargetPixels(renderTarget, 0, 0, this.width, this.height, buffer);
// create jimp image
const image = new Jimp({
data: buffer,
width: this.width,
height: this.height
}, (err: any, images: any) => {
images.write('output.jpeg');
});
}
private createRenderer(): THREE.WebGLRenderer {
const canvasWidth = 1024;
const canvasHeight = 1024;
const canvas = new Canvas.Canvas(canvasWidth, canvasHeight);
canvas.addEventListener = () => {
// noop
}
canvas.style = {};
const renderer = new THREE.WebGLRenderer({ context: gl(this.width, this.height), canvas , preserveDrawingBuffer: true , alpha: true});
renderer.autoClear = false;
renderer.setClearColor(0xFFFFFF);
renderer.setPixelRatio(2);
renderer.setSize(this.dimensions.width, this.dimensions.height);
renderer.sortObjects = false;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
return renderer;
}
private getPlane = (): THREE.Mesh => new THREE.Mesh(
new THREE.PlaneBufferGeometry(2000, 2000, 8, 8),
new THREE.MeshBasicMaterial({ visible: false })
)
private animate = (): void => {
this.renderScenes();
}
private getSpotLight(): THREE.SpotLight {
const spotLight = new THREE.SpotLight(0xffffff, 1.5);
spotLight.position.set(0, 500, 2000);
spotLight.castShadow = true;
spotLight.shadow.camera.near = 200;
spotLight.shadow.camera.far = this.camera.far;
spotLight.shadow.camera.fov = 50;
spotLight.shadow.bias = -0.00022;
spotLight.shadow.mapSize.width = 2048;
spotLight.shadow.mapSize.height = 2048;
return spotLight;
}
private setModelMatricesAndProstateMeshBoundings(geometry: THREE.Geometry): void {
geometry.computeFaceNormals();
geometry.computeBoundingBox();
// A transformation matrix (matrix) is applied to prostate segmentation, target and biopsy,
// which contains a translation and a rotation
// The translation shifts the center of segmentation mesh to view center
this.prostateMeshBoundingBox = geometry.boundingBox.clone();
const center = this.prostateMeshBoundingBox.getCenter(new THREE.Vector3());
const translationMatrix = new THREE.Matrix4().makeTranslation(-center.x, -center.y, -center.z);
// The default view (DICOM coordinate) is patient head pointing towards user.
// We like patient foot pointing towards user.
// Therefore a 180 degree rotation along x (left-right) axis is applied.
const rotAxis = new THREE.Vector3(1, 0, 0);
this.rotationMatrix = new THREE.Matrix4().makeRotationAxis(rotAxis, Math.PI);
// matrix is the multiplication of rotation and translation
this.matrix = new THREE.Matrix4().multiplyMatrices(this.rotationMatrix, translationMatrix);
}
private createProstateMesh(geometry: THREE.Geometry): THREE.Mesh {
const shaderMaterial = ThreeUtil.createShaderMaterial(new THREE.Vector4(0.8, 0.6, 0.2, 0));
geometry.computeFaceNormals();
geometry.computeBoundingBox();
// A transformation matrix (matrix) is applied to prostate segmentation, target and biopsy,
// which contains a translation and a rotation
// The translation shifts the center of segmentation mesh to view center
this.prostateMeshBoundingBox = geometry.boundingBox.clone();
const mesh = new THREE.Mesh(geometry, shaderMaterial);
mesh.applyMatrix(this.matrix);
mesh.name = 'prostate';
return mesh;
}
private renderScenes(): void {
this.renderer.clear();
this.renderer.setViewport(-150, 0, 210, 75);
this.renderer.render(this.scenes.homunculus, this.camera);
this.renderer.clearDepth();
this.renderer.setViewport(0, 0, this.dimensions.width, this.dimensions.height);
this.renderer.render(this.scenes.main, this.camera);
}
private toCameraCoordinates(position: THREE.Vector3): THREE.Vector3 {
return position.applyMatrix4(this.camera.matrixWorldInverse);
}
private scaleInView(): void {
const point1 = this.toCameraCoordinates(this.prostateMeshBoundingBox.min);
const point2 = this.toCameraCoordinates(this.prostateMeshBoundingBox.max);
this.camera.fov = ThreeUtil.angleInDegree(point1, point2, this.camera) * 2;
this.camera.updateProjectionMatrix();
}
private createBiopsyMeshes(biopsiesMeshData: BiopsyMeshData[]): THREE.Mesh[] {
return biopsiesMeshData.map((biopsyMeshData: BiopsyMeshData, index: number) => {
const startPoint = new THREE.Vector3(
biopsyMeshData.proximalX,
biopsyMeshData.proximalY,
biopsyMeshData.proximalZ
);
const endPoint = new THREE.Vector3(biopsyMeshData.distalX, biopsyMeshData.distalY, biopsyMeshData.distalZ);
const color = biopsyMeshData.adenocarcinoma ?
0x0000ff: 0x002233;
const opacity = biopsyMeshData.adenocarcinoma ? 0.65 : 0.3;
const material = new THREE.MeshLambertMaterial({
transparent: true,
depthWrite: false,
depthTest: false,
emissive: new THREE.Color(color),
opacity,
});
const cylinder = ThreeUtil.getCylinderBetweenPoints(startPoint, endPoint, material);
cylinder.applyMatrix(this.matrix);
cylinder.name = `biopsy_${index}`;
return cylinder;
});
}
private loadRoiMeshesOnScene(roisMeshData: RoiMeshData[]): void {
roisMeshData.forEach((roiMeshData: RoiMeshData, index: number) => this.setupRoiMesh(roiMeshData, index));
}
private setupRoiMesh(roiMeshData: RoiMeshData, index: number): void {
const loader = new THREESTLLoader(THREE);
const dataUri = `data:text/plain;base64,${roiMeshData.ROIMeshUrl}`;
if (roiMeshData.tooltipConfiguration) {
if (typeof(roiMeshData.tooltipConfiguration) === 'string' ) {
roiMeshData.tooltipConfiguration = JSON.parse(roiMeshData.tooltipConfiguration);
}
}
loader.prototype.load(dataUri, (roiGeometry: THREE.Geometry) => {
const color = new THREE.Color(0xA396CB);
const colorVector = new THREE.Vector4(color.r, color.g, color.b, 0);
const shaderMaterial = ThreeUtil.createShaderMaterial(colorVector, roiMeshData.adenocarcinoma);
const mesh = new THREE.Mesh(roiGeometry, shaderMaterial);
mesh.name = `roi_${roiMeshData.ROIId}`;
roiGeometry.applyMatrix(this.matrix);
this.addToMainScene(mesh);
this.saveAsImage();
});
}
private setupHomunculusMesh(): void {
new THREEObjLoader(THREE);
new THREE.OBJLoader().load(HOMUNCULUS_OBJ_URL, (group: THREE.Group) => {
group.traverse((child) => {
if (child instanceof THREE.Mesh) {
// apply custom material
// #ts-ignore
child.material = new THREE.MeshPhongMaterial({
color: 0xb09b70,
specular: 0x050505,
shininess: 100,
flatShading: false
});
const homunculusMatrix = new THREE.Matrix4();
homunculusMatrix.makeScale(0.020, 0.020, 0.020);
homunculusMatrix.multiply(this.rotationMatrix);
child.applyMatrix(homunculusMatrix);
}
});
this.scenes.homunculus.add(group);
});
}
private addToMainScene(object3D: THREE.Object3D): void {
this.scenes.main.add(object3D);
// Adding Ambient Light for all scenes inside main scenes except Humanoid
const ambientLight = new THREE.AmbientLight(0x000000);
this.scenes.main.add(ambientLight);
}
}

Related

How to convert an array of bytes into an image that can be drawn onto a canvas?

I'm using node-canvas to do some image manipulation (in this example to convert the image to a grayscale version, just to keep the example simple). I have no issues getting the raw data, but I'm having trouble working out how to convert the derived array of data back into something that the canvas context can draw.
import { writeFile } from "fs/promises";
import { loadImage } from "canvas"
import { OUTPUT, WIDTH, HEIGHT } from "../config";
const fadeToGray = ctx => {
const src = ctx.getImageData(0, 0, WIDTH, HEIGHT).data;
const dst = new Uint32Array(src.length >> 2);
for (let i = 0; i < src.length; i += 2) {
dst[i >> 2] = (src[i] * 4899 + src[++i] * 9617 + src[++i] * 1868 + 8192) >> 14;
}
// I'm stuck here!
const image = CONVERT_BYTES_TO_IMAGE(dst);
ctx.drawImage(image, 0, 0, WIDTH, HEIGHT);
}
const start = async () => {
const image = await loadImage('input/test.png');
const canvas = createCanvas(WIDTH, HEIGHT);
const ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, WIDTH, HEIGHT);
fadeToGray(ctx);
writeFile(`${OUTPUT}/test.png`, canvas.toBuffer("image/png"));
}
start().then(() => {
console.log('done')
}).catch(err => {
console.error('dammit', err)
});
I feel like I'm missing something very obvious. How do I convert my array of bytes back into a drawable image?

Infinitely looping background as character moves in Phaser?

I have a background image and a separate ground image that I want to loop infinitely as long as the character is moving forward. When the character stops, the background and ground should not be moving. For similar games it is often suggested to add this.game.background.tilePosition.x -= 1
to the update function. This is not what I am looking for as it makes the background constantly move regardless of whether the character is moving. At the moment my background and ground images are repeating, but they are restricted to this.game.world.setBounds(0, 0, 1280, 800);. Any suggestions would be greatly appreciated. My code is below:
function Hero(game, x, y) {
Phaser.Sprite.call(this, game, x, y, 'player');
//rest of code for Hero constructor....
}
Hero.prototype = Object.create(Phaser.Sprite.prototype);
Hero.prototype.constructor = Hero;
//code for Hero.prototype....
PlayState = {};
PlayState.init = function () {
//code for keyboard...
};
PlayState.preload = function () {
this.game.load.json('level:1', 'data/level01.json');
this.game.load.image('ground', 'images/ground.png'); // I need this to
//repeat infinitely
this.game.load.image('background', 'images/background.png'); // I need
//this to repeat infinitely
this.game.load.spritesheet('player', 'images/player.png', 64, 64);
};
PlayState.create = function () {
this.game.world.setBounds(0, 0, 1280, 800);
this.game.background = this.game.add.tileSprite(0, 0,
this.game.world.width, 800, 'background');
this.game.ground = this.game.add.tileSprite(0, 680,
this.game.world.width, 166, 'ground');
this.game.physics.arcade.enable(this.game.ground);
this.game.ground.body.immovable = true;
this.game.ground.body.allowGravity = false;
this.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
this._loadLevel(this.game.cache.getJSON('level:1'));
};
PlayState.update = function () {
this.physics.arcade.collide(this.player, this.game.ground);
};
PlayState._loadLevel = function (data) {
this._spawnPlayer({player: data.player});
const GRAVITY = 1200;
this.game.physics.arcade.gravity.y = GRAVITY;
};
PlayState._spawnPlayer = function (data) {
this.player = new Hero(this.game, data.player.x, data.player.y);
this.game.add.existing(this.player);
this.game.camera.follow(this.player,
Phaser.Camera.FOLLOW_PLATFORMER);
};
window.onload = function () {
let game = new Phaser.Game(866, 520, Phaser.CANVAS, 'game');
game.state.add('play', PlayState);
game.state.start('play');
};
I have tried the following solution (see below) where I create a constructor function for the background. This idea is from an existing tutorial done in TypeScript that does exactly what I am looking for. However, I am not familiar with TypeScript so I have just tried to interpret the code from the tutorial as best I can and put it into Javascript but at the moment I am getting the error TypeError: "a is undefined"in the console. I am still learning Javascript and I can't see where I am going wrong. (I have included the keyboard logic and the character movement this time for clarity.)
function MyBackground(game, x, y) {
Phaser.Sprite.call(this, game, x, y, 'background');
}
MyBackground.prototype = Object.create(Phaser.Sprite.prototype);
MyBackground.prototype.constructor = MyScene;
MyBackground.prototype.repeatScene = function () {
this.nextFrame = new Phaser.Sprite(this.game, this.width, 0, "background", 0);
this.game.add.existing(this.nextFrame);
};
function Hero(game, x, y) {
Phaser.Sprite.call(this, game, x, y, 'player');
this.anchor.set(0.5, 0.5);
this.game.physics.enable(this);
this.body.collideWorldBounds = false;
this.animations.add('stop', [0]);
this.animations.add('run', [1, 2, 3, 4, 5], 14, true); // 14fps looped
this.animations.add('jump', [6]);
this.animations.add('fall', [7]);
this.animations.add('die', [8, 9, 8, 9, 8, 9, 8, 9], 12); // 12fps no loop
}
Hero.prototype = Object.create(Phaser.Sprite.prototype);
Hero.prototype.constructor = Hero;
Hero.prototype.move = function (direction) {
const SPEED = 200;
this.body.velocity.x = direction * SPEED;
// update image flipping & animations
if (this.body.velocity.x < 0) {
this.scale.x = -1;
}
else if (this.body.velocity.x > 0) {
this.scale.x = 1;
}
};
Hero.prototype.jump = function () {
const JUMP_SPEED = 600;
let canJump = this.body.touching.down;
if (canJump) {
this.body.velocity.y = -JUMP_SPEED;
}
return canJump;
};
Hero.prototype.bounce = function () {
const BOUNCE_SPEED = 200;
this.body.velocity.y = -BOUNCE_SPEED;
};
Hero.prototype.update = function () {
// update sprite animation, if it needs changing
let animationName = this._getAnimationName();
if (this.animations.name !== animationName) {
this.animations.play(animationName);
}
};
Hero.prototype.die = function () {
this.alive = false;
this.body.enable = false;
this.animations.play('die').onComplete.addOnce(function () {
this.kill();
}, this);
};
Hero.prototype._getAnimationName = function () {
let name = 'stop'; // default animation
if (!this.alive) {
name = 'die';
}
else if (this.body.velocity.y > 0 && !this.body.touching.down) {
name = 'fall';
}
else if (this.body.velocity.y < 0) {
name = 'jump';
}
else if (this.body.velocity.x !== 0 && this.body.touching.down ) {
name = 'run';
}
return name;
PlayState = {};
PlayState.init = function () {
this.game.renderer.renderSession.roundPixels = true;
this.keys = this.game.input.keyboard.addKeys({
left: Phaser.KeyCode.LEFT,
right: Phaser.KeyCode.RIGHT,
up: Phaser.KeyCode.UP
};
PlayState.preload = function () {
this.game.load.json('level:1', 'data/level01.json');
this.game.load.image('ground', 'images/ground.png'); // I need this to repeat infinitely
this.game.load.image('background', 'images/background.png'); // I need this to repeat infinitely
this.game.load.spritesheet('player', 'images/player.png', 64, 64);
};
PlayState.create = function () {
this.background = new MyBackground(this.game, 0, 0);
this.game.add.existing(this.MyBackground);
this.game.physics.arcade.enable(this.game.ground);
this.game.ground.body.immovable = true;
this.game.ground.body.allowGravity = false;
this.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
this._loadLevel(this.game.cache.getJSON('level:1'));
this.game.world.setBounds(0, 0, this.MyBackground.width * 2, 800);
};
PlayState.update = function () {
var backgroundWidth = this.game.stage.getChildAt(0).getBounds().width; //getChildAt(0) because the background is created first in create
if (this.x > backgroundWidth * .75) {
this.x = backgroundWidth * .25;
repeatScene();
};
this._handleInput();
this.physics.arcade.collide(this.player, this.game.ground);
};
PlayState._handleInput = function () {
if (this.keys.up.isDown) {
this.player.jump();
} else if (this.keys.right.isDown) { // move hero right
this.player.move(1);
} else if (this.keys.left.isDown) { // move hero left
this.player.move(-1);
} else { // stop
this.player.move(0);
}
};
PlayState._loadLevel = function (data) {
this._spawnPlayer({player: data.player});
const GRAVITY = 1200;
this.game.physics.arcade.gravity.y = GRAVITY;
};
PlayState._spawnPlayer = function (data) {
this.player = new Hero(this.game, data.player.x, data.player.y);
this.game.add.existing(this.player);
this.game.camera.follow(this.player, Phaser.Camera.FOLLOW_PLATFORMER);
};
window.onload = function () {
let game = new Phaser.Game(866, 520, Phaser.CANVAS, 'game');
game.state.add('play', PlayState);
game.state.start('play');
};

Can't I use await animation on Device.StartTimer()?

I'm making app with Xamarin.forms pcl.
If I use UIRefreshControl with Scrollview on iOS and animation same time.
It crash!
Is this bug of UIRefreshControl?
I provide reproduce sample code here.
https://github.com/myallb/test_pulltorefresh.git
Sample code is very simple.
Is there any mistake??
Thanks.
public partial class testPage : ContentPage
{
public PullToRefreshLayout RefreshView = null;
AbsoluteLayout layout;
public testPage()
{
InitializeComponent();
layout = new AbsoluteLayout()
{
BackgroundColor = Color.Purple,
};
ScrollView scrollview = new ScrollView()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = layout
};
RefreshView = new PullToRefreshLayout
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Content = scrollview,
RefreshColor = Color.Red,
RefreshCommand = new Command(RefreshStart)
};
RefreshView.IsPullToRefreshEnabled = true;
Content = RefreshView;
Device.StartTimer(new TimeSpan(0, 0, 1), ani);
}
bool ani()
{
Label z = new Label()
{
Text = "Z",
TextColor = Color.White,
FontAttributes = FontAttributes.Bold,
FontSize = new Random().Next(22, 35)
};
AbsoluteLayout.SetLayoutBounds(z, new Rectangle(0.67 + new Random().Next(0, 10) / 100.0, 0.13 + new Random().Next(0, 10) / 100.0, 40, 40));
AbsoluteLayout.SetLayoutFlags(z, AbsoluteLayoutFlags.PositionProportional);
layout.Children.Add(z);
Device.BeginInvokeOnMainThread(async () =>
{
Task t1 = z.FadeTo(0, 3500);
Task t2 = z.TranslateTo(0, -70, 3500, Easing.SinInOut);
await Task.WhenAll(t1, t2);
layout.Children.Remove(z);
});
return true;
}
void RefreshStart()
{
Debug.WriteLine("RefreshStart");
if (RefreshView != null)
RefreshView.IsRefreshing = true;
Device.BeginInvokeOnMainThread(async () =>
{
await Task.Delay(20);
Debug.WriteLine("RefreshEnd");
RefreshView.IsRefreshing = false;
});
}
}

MvvmCross: Cannot add more than one UIPickerView

I'm building an iOS view using Xamarin and MvvmCross and I have come across an interesting little issue. I can't seem to add more than one UIPickerView to a UIView.
Add one view and all works well. Add a second and the simulator just hangs when I try and open the page.
This seems to be related to a UITextField with an InputView as I also have an issue if I try to add a UIDatePicker as well.
Nothing strange in the debug output.
Here is the code:
[Register("EditJobViewJobView")]
public class EditJobView : MvxViewController
{
public new EditJobViewModel ViewModel
{
get { return (EditJobViewModel)base.ViewModel; }
}
private const float _leftMargin = 6;
private const float _labelHeight = 20;
private const float _pickerHeight = 28;
private readonly UIFont _labelFont = UIFont.BoldSystemFontOfSize(18f);
private readonly UIFont _controlFont = UIFont.SystemFontOfSize(18f);
private readonly UIView _paddingInsert = new UIView(new RectangleF(0, 0, 4, 0));
private int _currentTop = 0;
public override void ViewDidLoad()
{
base.ViewDidLoad();
View = new UIView() { BackgroundColor = UIColor.White };
NavigationItem.SetRightBarButtonItem(new UIBarButtonItem("Save", UIBarButtonItemStyle.Bordered,
(sender, args) => ViewModel.OkCommand.Execute(null)), true);
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
Title = "Edit Job";
AddLabel("Job Status");
MvxPickerViewModel jobStatusPickerViewModel;
var jobStatusTextView = AddPickerView(out jobStatusPickerViewModel);
AddLabel("Job Priority");
MvxPickerViewModel jobPriorityPickerViewModel;
var jobPriorityTextView = AddPickerView(out jobPriorityPickerViewModel);
var set = this.CreateBindingSet<EditJobView, EditJobViewModel>();
set.Bind(jobStatusPickerViewModel).For(p => p.SelectedItem).To(vm => vm.SelectedJobStatusType);
set.Bind(jobStatusPickerViewModel).For(p => p.ItemsSource).To(vm => vm.JobStatusTypes);
set.Bind(jobStatusTextView).To(vm => vm.SelectedJobStatusType);
set.Bind(jobPriorityPickerViewModel).For(p => p.SelectedItem).To(vm => vm.SelectedJobPriority);
set.Bind(jobPriorityPickerViewModel).For(p => p.ItemsSource).To(vm => vm.JobPriorities);
set.Bind(jobPriorityTextView).To(vm => vm.SelectedJobPriority);
set.Apply();
}
private void AddLabel(string caption)
{
_currentTop += 10;
var frame = new RectangleF(_leftMargin, _currentTop, 300, _labelHeight);
var label = new UILabel(frame);
label.Font = _labelFont;
label.Text = caption;
AddView(label);
_currentTop += 2;
}
private UITextField AddPickerView(out MvxPickerViewModel pickerViewModel)
{
var textField = AddTextField();
var pickerView = new UIPickerView();
pickerViewModel = new MvxPickerViewModel(pickerView);
pickerView.Model = pickerViewModel;
pickerView.ShowSelectionIndicator = true;
textField.InputView = pickerView;
return textField;
}
private UITextField AddTextField()
{
var frame = new RectangleF(_leftMargin, _currentTop, 300, _pickerHeight);
var textField = new UITextField(frame);
textField.Layer.BorderColor = UIColor.Black.CGColor;
textField.Layer.BorderWidth = 1f;
textField.Font = _controlFont;
textField.LeftView = _paddingInsert;
textField.LeftViewMode = UITextFieldViewMode.Always;
AddView(textField);
return textField;
}
private void AddView(UIView view)
{
View.AddSubview(view);
_currentTop += (int)view.Frame.Height;
}
}
Any ideas?
This was caused by creating a shared padding insert across a number of UITextFields.
To fix this I changed
textField.LeftView = _paddingInsert;
to
textField.LeftView = new UIView(new RectangleF(0, 0, 4, 0));

Sending a object to server with jQuery

I have a jsfiddle set up because there is a decent amount of code but I will also cp it here.
But first the problem. I would like to send the Shape object to the server for processing on mouseup. The problem I am having is when I add the jQuery ajax or load functions to test the AJAX I get some sort of run away code and everything freezes.
first the code I add so you can try it in the jsfiddle
$('body').load('index.php/main/add_shape', shape, function () { shape.points["x"]});
Now for the code on the js fiddle for the sake of being able to look at it here and Indexing.
HTML
<body>
<canvas id="drawn" height="200" width="200">
<p>Your browser doesn't support HTML5 and the canvas element. You should probably upgrade to one of the following<br>
Google Chrome<br>
Firefox<br>
Safari<br>
Opera
</p>
</canvas>
</body>
CSS
canvas {
border: 1px solid #000;
}
JavaScript
var eventMap = {
mousemove: "move",
touchmove: "move",
mousedown: "down",
touchstart: "down",
mouseup: "up",
touchend: "up"
};
function Shape (type, color, height, width, radius, x, y) {
this.type = type;
this.color = color;
this.h = height;
this.w = width;
this.r = radius;
this.points = ["x","y"];
this.points["x"] = [x];
this.points["y"] = [y];
};
var tools = {}
$(window).ready(function () {
function init () {
hex = 000000;
canvas = $('#drawn').get(0)
c = canvas.getContext('2d');
c.lineJoin = "round";
c.lineCap = "round";
c.strokeStyle = "#"+hex;
c.lineWidth = 1;
tool = new tools['pencil']();
$('canvas').bind('mousedown mousemove mouseup', mouse_Draw);
}
function mouse_Draw (e) {
var pos = findPos(this);
e._x = e.pageX - pos.x;
e._y = e.pageY - pos.y;
if (eventMap[e.type] == "down") {
shapes = new Shape (1, 2, null, null, null, e._x, e._y);
} else if (eventMap[e.type] == "move") {
shapes.points["x"].push(e._x);
shapes.points["y"].push(e._y);
} else if (eventMap[e.type] == 'up') {
shapes.points["x"].push(e._x);
shapes.points["y"].push(e._y);
alert(shapes.points["x"].toString());
}
var func = tool[eventMap[e.type]];
if (func) {
func(e);
}
}
init();
});
function findPos(obj) {
var curleft = curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
}
/*****************TOOLS*************/
tools.pencil = function () {
var tool = this;
tool.started = false;
tool.down = function (e) {
c.beginPath();
c.moveTo(e._x, e._y);
tool.started = true;
shape = new Shape (pencil, c.strokeStyle, null, null, null, e._x, e._y);
};
tool.move = function (e) {
if (tool.started) {
c.lineTo(e._x, e._y);
c.stroke();
shape.points["x"].push(e._x);
shape.points["y"].push(e._y);
}
};
tool.up = function (e) {
if (tool.started) {
tool.started = false;
}
};
}
As you can see when you add the jQuery load line to the tool.up it freezes.
if you have a nice new browser ...
tool.up = function (e) {
if (tool.started) {
tool.started = false;
alert(JSON.stringify(shape));
}
};
(or you can steal json code from json.org) then you just have to parse the json serverside
The answer was the object was an infinite loop because of this line
shape = new Shape (pencil, c.strokeStyle, null, null, null, e._x, e._y);
needed to be
shape = new Shape ('pencil', c.strokeStyle, null, null, null, e._x, e._y);

Resources