Archive for the ‘Programmation’ Category


Design pattern en AS3 [Abstract Factory] 4/7

Dans le dernier article portant sur les design patterns en ActionScript3 , nous avons abordée la factory. Aujourd’hui voyons l’Abstract factory (fabrique abstraite). Elle encapsule un groupe de factory ayant une thématique commune. Le code crée une implémentation concrète de la fabrique abstraite, puis utilise les interfaces génériques pour créer des objets concrets de la thématique. Le client ne se préoccupe pas de savoir laquelle de ces fabriques a donné un objet concret, car il n’utilise que les interfaces génériques des objets produits. Ce patron de conception sépare les détails d’implémentation d’un ensemble d’objets de leur usage générique.

Exemple de mise en œuvre :

private var _contenu0:IContenu;
private var _contenu1:IContenu;
private var _contenu2:IContenu;

public function creator(content:Object)
{

/*
ABSTRACT FACTORY
factory utilise d'autres factory , cache les classes fabricant l'objet
*/

_contenu1 = FactoryContent.prebuild(content);
_contenu2 = FactoryContent.prebuild(_content);

}

Contenu de la classe FactoryContent :

public class FactoryContent
{
public function FactoryContent()
{
}

public static function prebuild(content:Object):IContenu
{
var resultatContenu:IContenu;

resultatContenu = BContenu.prebuildContent(content);

return resultatContenu;
}

}

on peut toujours construire des contenus “from scratch”  sans factory :

_contenu0 = BContenu.prebuildContent(_content);
_contenu1 = BContenu.prebuildContent(_content);
_contenu2 = BContenu.prebuildContent(_content]);

public static function prebuildContent(content:Object):IContenu
{
var resultatContenu:IContenu;
resultatContenu = new BContenu(content);

return resultatContenu;
}





Design Patterns en AS3 [Singleton] 1/7

Cet article sera le premier d’une série de 7 articles traitant de l’implémentation des principaux design pattern en ActionScript 3, avec une présentation théorique et un exemple pratique.

Aujourd’hui le Singleton

Le singleton est le design pattern le plus simple à mettre en place. Le but est de restreindre l’instanciation d’une classe à un seul objet (ou bien à quelques objets seulement). Il est utilisé lorsque l’on a besoin d’exactement un objet pour coordonner des opérations dans un système.Il sera surtout utilisé pour un contrôleur présent dans un MVC (Modèle Vue Contrôleur) qui sera compose d’autres objets de contrôleurs. Le modèle est parfois utilisé pour son efficacité, lorsque le système est plus rapide ou occupe moins de mémoire avec peu d’objets qu’avec beaucoup d’objets similaires.

On implémente le singleton en écrivant une classe qui possèdera une méthode créant une instance uniquement s’il n’en existe pas encore. Sinon elle renvoie une référence vers l’objet qui existe déjà.

Le singleton doit être implémenté avec précaution dans les applications multi-thread. Si deux threads exécutent en même temps la méthode de création alors que l’objet unique n’existe pas encore, il faut absolument s’assurer qu’un seul créera l’objet, et que l’autre obtiendra une référence vers ce nouvel objet.
La solution classique à ce problème consiste à utiliser l’exclusion mutuelle pour indiquer que l’objet est en cours d’instanciation.

Voici un exemple d’implémentation :


package
{
public class example
{
// on définit une variable statique Booléenne permettant de savoir si l'objet est créé
private static var _fromGetInstance:Boolean = false;

// c'est l'objet en lui même , il est statique et private pour que seule la classe puisse y accéder
private static var _uniqueInstance:example;

// méthode d'accès à l'objet du singleton , il peut être remplacé par un getter qui sera lui aussi en public static.
// la méthode retourne l'objet et vérifie sa disponibilité
public static function getInstance():LoadingModal
{
if(! _uniqueInstance)
{
_fromGetInstance = true;
_uniqueInstance = new LoadingModal();
}
return _uniqueInstance;

}

// constructeur de la classe
// on ne peut pas créer de nouveaux objets par le constructeur sinon cela renvoie une erreur
public function example()
{
if(!_fromGetInstance)
{
throw new Error("interdit");
}
_fromGetInstance = false;

}

}
}





The Qt Web Runtime journey begins…

l y a peu, dès Qt 4.4 en réalité, Qt dispose de son wrapper autour de WebKit : Qt WebKit. Depuis, ce module est devenu l’un des plus utilisés de Qt ; en effet, utiliser du contenu Web est extrêmement demandé par le marché actuel. Dans les télévisions, les netbooks, les téléphones mobiles et bien d’autres périphériques domestiques, l’utilisation de l’Internet est absolument irremplaçable, l’application étant hébergée sur le Web. Il était donc temps de fournir une couche supplémentaire pour faciliter et sécuriser le développement d’applications basées sur le Web. D’où un nouveau projet pour les équipes de développement de Qt : le Qt Web Runtime, basé sur Qt et sur Qt WebKit, grâce auquel les applications Web deviendront plus facilement plus fonctionnelles.

L’un des objectifs de ce framework est de fournir un runtime Web basé sur les standards du W3C. Ainsi, vous pourrez facilement développer et déployer des applications Web sur des smartphones ou d’autres plateformes de la même manière qu’une application plus traditionnelle.

La fonctionnalité-clé ? Vous pouvez accéder au matériel (appareil photo ou accéléromètre, par exemple) et aux autres ressources de l’appareil (liste de contacts, messages…) via des API JavaScript.

En code, voici ce que donne l’accès à l’accéléromètre du périphérique :

var wrtSensors = nokia.device.load("sensors");
wrtSensors.startChannel(callback, "AccelerometerAxis", errorCallback);

function callback(data) {
       console.log("x-axis: " + data.axisX + " y-axis: " + data.axisY + " z-axis: " + data.axisZ);
}      

function errorCallback(err) {
       console.log("Ouch, " + err.message + "error code:" + err.code);
}

Il suffit de s’enregistrer aux notifications concernant l’accélération (soit le canal AccelerometerAxis) grâce à la méthode startChannel. Ensuite, vos fonctions de rappel sont utilisées chaque fois que le capteur reçoit un signal d’accélération.

Qt fonctionnant sur toute une série de plateformes, QWR fera de même et supportera toute une variété de plateformes. En tant que partie intégrante de Qt, cette technologie sera disponible ainsi que ses sources, selon le nouveau modèle de gouvernance (qui a notamment ouvert les repository de Qt sur Gitorious).

Ce framework est actuellement assez jeune et ne dispose pas encore de toutes les fonctionnalités qu’il devrait posséder à terme ni du support de nombreuses plateformes. Cependant, selon le modèle de contribution, chaque utilisateur pourra construire ce framework, grâce notamment à des retours dessus – vous aurez une place active dans son développement.

source FR : http://www.developpez.net/forums/d958542/c-cpp/bibliotheques/qt/voyage-qt-web-runtime-debute/

source EN :  http://labs.trolltech.com/blogs/2010/07/19/the-qt-web-runtime-journey-begins/




Réalitée augmentée en utilisant une webcam et Flash

Avant de coder dans un projet Flash CS4, nous avons besoin de préparer les librairies et les assets .

Pour démarrer :

1/ créer un nouveau projet dans Flash CS4, pour cela aller dans Fichiers -> nouveau…-> fichier flash (AS3.0) et sauver le fichier en
tant que  AugmentedReality.fla dans le répertoire du projet

2/ créer un document en choisissant Fichiers -> nouveau … -> Fichier ActionScript et le sauver en tant que AugmentedReality.as
le sauver dans le répertoire du projet

3/ définir ce fichier comme le fichier par défaut , fenêtre -> propriétés -> classe

4/ Créer un répertoire de librairie dans le projet et rajouter rajouter les librairies suivantes (Flex SDK code Library, Flash augmented Reality Code Library, Papervision3D code Library)  en allant dans Fichiers > Paramètres de publication > Flash > ActionScript 3.0 > répertoire librairie

5/ Créer un répertoire asset et rajouter le modèle 3D Collada et les deux fichiers de données AR.

Les différents rôles des librairies :

Flex SDK Code library : Permet d’importer les données de fichiers FLAR.
Spark Project’s Flash Augmented Reality code library (FLARToolkit): Détection graphique.
Papervision3D code library (PV3D): Permet l’import , le positionnement et le rendu du modèle 3D.

La Bibliothèque de Réalité augmentée Flash Toolkit (FLARToolkit) est importante pour cette application. Il analyse l’image fournie par la webcam et définit l’espace occupée pour cartographier le modèle 3D. Le fichier FLARCameraParams.dat Il corrige la distorsion de la webcam et détecte le pattern. L’image du pattern est définie dans le fichier FLARPattern.pat. Pour utiliser un autre graphique, créer une image JPEG , et utilisez le FLAR Marker Generator (AIR, 322K) pour générer un nouveau fichier de signatures.

package {

//--------------------------------------
// Imports
//--------------------------------------
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Camera;
import flash.media.Video;
import flash.utils.ByteArray;

import org.libspark.flartoolkit.core.FLARCode;
import org.libspark.flartoolkit.core.param.FLARParam;
import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData;
import org.libspark.flartoolkit.core.transmat.FLARTransMatResult;
import org.libspark.flartoolkit.detector.FLARSingleMarkerDetector;
import org.libspark.flartoolkit.pv3d.FLARBaseNode;
import org.libspark.flartoolkit.pv3d.FLARCamera3D;

import org.papervision3d.lights.PointLight3D;
import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.parsers.DAE;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;

//--------------------------------------
// Class Definition
//--------------------------------------
public class AugmentedReality extends Sprite
{

//--------------------------------------
// Class Properties
//--------------------------------------

//  1. WebCam
private var video : Video;
private var webcam : Camera;

//  2. FLAR Marker Detection
private var flarBaseNode : FLARBaseNode;
private var flarParam : FLARParam;
private var flarCode : FLARCode;
private var flarRgbRaster_BitmapData : FLARRgbRaster_BitmapData;
private var flarSingleMarkerDetector : FLARSingleMarkerDetector;
private var flarCamera3D : FLARCamera3D;
private var flarTransMatResult : FLARTransMatResult;
private var bitmapData : BitmapData;
private var FLAR_CODE_SIZE : uint         = 16;
private var MARKER_WIDTH : uint         = 80;

[Embed(source="./assets/FLAR/FLARPattern.pat", mimeType="application/octet-stream")]
private var Pattern : Class;

[Embed(source="./assets/FLAR/FLARCameraParameters.dat", mimeType="application/octet-stream")]
private var Params : Class;

//  3. PaperVision3D
private var basicRenderEngine : BasicRenderEngine;
private var viewport3D : Viewport3D;
private var scene3D : Scene3D;
private var collada3DModel : DAE;

//   Fun, Editable Properties
private var VIDEO_WIDTH : Number = 640;  //Set 100 to 1000 to set width of screen
private var VIDEO_HEIGHT : Number = 480;  //Set 100 to 1000 to set height of screen
private var WEB_CAMERA_WIDTH : Number = VIDEO_WIDTH/2;  //Smaller than video runs faster
private var WEB_CAMERA_HEIGHT : Number = VIDEO_HEIGHT/2;  //Smaller than video runs faster
private var VIDEO_FRAME_RATE : Number = 30;  //Set 5 to 30. Higher values = smoother video
private var DETECTION_THRESHOLD : uint  = 80;  //Set 50 to 100. Set to detect marker more accurately.
private var DETECTION_CONFIDENCE : Number = 0.5;  //Set 0.1 to 1. Set to detect marker more accurately.
private var MODEL_SCALE : Number = 0.8;  //Set 0.1 to 5. Set higher to enlarge model

// Fun, Editable Properties: Load a Different Model
private var COLLADA_3D_MODEL : String = "./assets/models/licensed/hummer/models/hummer.dae";

//--------------------------------------
// Constructor
//--------------------------------------

/**
* The constructor is the ideal place
* for project setup since it only runs once.
* Prepare A,B, & C before repeatedly running D.
**/
public function AugmentedReality()
{
//Prepare
prepareWebCam();  //Step A
prepareMarkerDetection();  //Step B
preparePaperVision3D();  //Step C

//  Repeatedly call the loop method
//  to detect and adjust the 3D model.
addEventListener(Event.ENTER_FRAME, loopToDetectMarkerAndUpdate3D); //Step D
}

//--------------------------------------
// Methods
//--------------------------------------

/**
* A. Access the user's webcam, wire it
*    to a video object, and display the
*    video onscreen.
**/
private function prepareWebCam() : void
{
video  = new Video(VIDEO_WIDTH, VIDEO_HEIGHT);
webcam = Camera.getCamera();
webcam.setMode(WEB_CAMERA_WIDTH, WEB_CAMERA_HEIGHT, VIDEO_FRAME_RATE);
video.attachCamera(webcam);
addChild(video);
}

/**
* B. Prepare the FLAR tools to detect with
*    parameters, the marker pattern, and
*    a BitmapData object to hold the information
*    of the most recent webcam still-frame.
**/
private function prepareMarkerDetection() : void
{
//   The parameters file corrects imperfections
//   In the webcam's image. The pattern file
//   defines the marker graphic for detection
//   by the FLAR tools.
flarParam = new FLARParam();
flarParam.loadARParam(new Params() as ByteArray);
flarCode = new FLARCode(FLAR_CODE_SIZE, FLAR_CODE_SIZE);
flarCode.loadARPatt(new Pattern());

//   A BitmapData is Flash's version of a JPG image in memory.
//   FLAR studies this image every frame with its
//   marker-detection code.
bitmapData = new BitmapData(VIDEO_WIDTH, VIDEO_HEIGHT);
bitmapData.draw(video);
flarRgbRaster_BitmapData = new FLARRgbRaster_BitmapData(bitmapData);
flarSingleMarkerDetector = new FLARSingleMarkerDetector(flarParam, flarCode, MARKER_WIDTH);
}

/**
* C. Create PaperVision3D's 3D tools including
*    a scene, a base node container to hold the
*    3D Model, and the loaded 3D model itself.
**/
private function preparePaperVision3D() : void
{
//  Basics of the empty 3D scene fit for
//  FLAR detection inside a 3D render engine.
basicRenderEngine     = new BasicRenderEngine();
flarTransMatResult     = new FLARTransMatResult();
viewport3D         = new Viewport3D();
flarCamera3D        = new FLARCamera3D(flarParam);
flarBaseNode        = new FLARBaseNode();
scene3D           = new Scene3D();
scene3D.addChild(flarBaseNode);

//  Load, scale, and position the model
//  The position and rotation will be
//  adjusted later in method D below.
collada3DModel = new DAE();
collada3DModel.load(COLLADA_3D_MODEL);
collada3DModel.scaleX = collada3DModel.scaleY = collada3DModel.scaleZ = MODEL_SCALE;
collada3DModel.z = 5;       //Moves Model 'Up' a Line Perpendicular to Marker
collada3DModel.rotationX = -90; //Rotates Model Around 2D X-Axis of Marker
collada3DModel.rotationY = 180; //Rotates Model Around 2D Y-Axis of Marker
collada3DModel.rotationZ = 90;  //Rotates Model Around a Line Perpendicular to Marker

//  Add the 3D model into the
//  FLAR container and add the
//  3D cameras view to the screen
//  so the user can view the result
flarBaseNode.addChild(collada3DModel);
addChild(viewport3D);
}

/**
* D. Detect the marker in the webcamera.
*    If found: move, scale, and rotate the
*    3D model to composite it over the marker
*    in the user's physical space.
**/
private function loopToDetectMarkerAndUpdate3D(aEvent : Event) : void
{

//  Copy the latest still-frame of the webcam video
//  into the BitmapData object for detection
bitmapData.draw(video);

try {

//  Detect *IF* the marker is found in the latest still-frame
if(flarSingleMarkerDetector.detectMarkerLite(flarRgbRaster_BitmapData, DETECTION_THRESHOLD) &&
flarSingleMarkerDetector.getConfidence() > DETECTION_CONFIDENCE) {

//  Repeatedly Loop and Adjust 3D Model to Match Marker
flarSingleMarkerDetector.getTransformMatrix(flarTransMatResult);
flarBaseNode.setTransformMatrix(flarTransMatResult);
basicRenderEngine.renderScene(scene3D, flarCamera3D, viewport3D);
}
} catch(error : Error) {}
}
}
}

constructeur de la classe

À partir de la ligne 93, les méthodes de préparation sont appelés pour chacun des éléments suivants: la webcam, la détection du pattern et Papervision3D. En plus des méthodes de configuration, la fonction loopToDetectMarkerAndUpdate3D est configuré pour exécuter encore et encore. En écoutant l’événement ENTER_FRAME construit en Flash, cette boucle sera appelé à plusieurs reprises tant que la demande est en cours d’exécution.

Initialiser la webcam

Comme l’exécution de l’application et d’une caméra est détectée, Flash Player invitera automatiquement l’utilisateur a activer sa webcam (ligne 96) .

Initialiser FLAR

La partie la plus importante du projet est la détection du pattern (marqueur graphique). Comme le marqueur graphique est détectée via la webcam, sa position, la rotation et l’échelle dans l’espace sont calculés. Ce calcul sera utilisée dans les prochaines étapes. À commencer par la ligne 97, FLAR va rechercher encore chaque trame d’images webcam pour le marqueur graphique prédéfinie (objet Pattern). Le schéma de détection utilise un objet ActionScript BitmapData, qui ne contient que la dernière trame de vidéo à un moment donné (la fonction BitmapData Draw).

Papervision 3D

Pour l’essentiel, la configuration 3D à la ligne 98 est typique de tout projet Papervision3D. Un objet BasicRenderEngine va gérer l’essentiel des mathématiques vecteur, transformant la géométrie 3D à la 2D graphiques pour les afficher sur l’écran. Le modèle 3D Collada est chargé et inséré dans la scène 3D, et la vue dans cet objet (scène ViewPort) est ajouté à la scène.

routine de mise à jour de la détection
En utilisant l’ENTER_FRAME, la fonction loopToDetectMarkerAndUpdate3D à la ligne 102 sera appelé 30 fois par seconde. Chaque fois, l’objet BitmapData va copier dans la dernière trame de la séquence webcam. FLAR exécute la détection sur l’image fixe. Si le marqueur est détecté, FLAR retourne à jour la position, la rotation et données à grande échelle (objet TransformMatrix). Le conteneur (objet FLARBaseNode) détenant le modèle 3D est mis à jour.

Tutoriel reprenant l’article http://www.adobe.com/devnet/flash/articles/augmented_reality.html




Réalitée augmentée en Flash avec FLARToolKit

Le tutoriel présenté ici sera une mise en application de FLARToolKit, libraire Flash de réalité augmentée. il s’agira de la coupler avec papervision pour interagir avec une scène en 3D.
Cette libraire japonaise Open Source” est de plus en plus utilisée dans les campagnes marketing.
Tout d’abord , nous allons déclarer deux sources externes , la première sont les paramètres de la capture vidéo utilise par FLARToolKit
le deuxième est le pattern en lui même

A noter qu’un éditeur de pattern existe en ligne :

http://flash.tarotaro.org/blog/2008/12/14/artoolkit-marker-generator-online-released/

    [Embed(source="../assets/FLAR/FLARCameraParameters.dat", mimeType="application/octet-stream")]
private var Params:Class;

[Embed(source="../assets/FLAR/FLARPattern.pat", mimeType="application/octet-stream")]
private var Pattern:Class;

Nous devons d’abord initialiser la vidéo .
nous créons un objet video que rattachons avec la méthode getCamera()
puis nous rajoutons le flux video sur la scene

/**
*  initWebCam
*
**/
private function initWebCam():void
{
_video  = new Video(VIDEO_WIDTH, VIDEO_HEIGHT);
_webcam = Camera.getCamera();

if(_webcam != null)
{
_webcam.setMode(WEB_CAMERA_WIDTH, WEB_CAMERA_HEIGHT, VIDEO_FRAME_RATE);
//_webcam.addEventListener(ActivityEvent.ACTIVITY,activityHandler);
_video.attachCamera(_webcam);
addChild(_video);
}
else
{

}

}

pour la détection , c’est la que la librairie FLARtoolkit entre en jeu
1/ chargement des paramètres inclut dans le fichier externe
2/ pour que le pattern soit bien detecte , les valeurs passées dans le FLARCode doivent etre le segment et la tailler que nous avons definit dans le pattern
3/ le detcteur a besoin d’un bitmadata pour localsier le pâttern sur une frame de celui ci , c’est pour cela que nous avons FLARRgbRaster_BitmapData
4/ nous passons dans le constructeur de FLARSingleMarkerDetector, les paramètres , la largeur du marqueur et le flar code.

/**
* initDetection
*/
private function initDetection():void
{
/*   Le fichier parametre corrige les imperfections de l'image de la webcam
Le fichier pattern determine le graphique pour la detection
par l'outil FLAR */
_flarParam = new FLARParam();
_flarParam.loadARParam(new Params() as ByteArray);

_flarCode = new FLARCode(FLAR_CODE_SIZE, FLAR_CODE_SIZE);
_flarCode.loadARPatt(new Pattern());

_bitmapData = new BitmapData(VIDEO_WIDTH, VIDEO_HEIGHT);
_bitmapData.draw(_video);
_flarRgbRaster_BitmapData = new FLARRgbRaster_BitmapData(_bitmapData);
_flarSingleMarkerDetector = new FLARSingleMarkerDetector(_flarParam, _flarCode, MARKER_WIDTH);
}

Papervision : le méthode suivante cree une scene Papervision 3D avec un plan ou une sphère auquel on associe un bitmapFileMaterial afin de les texturer.
le BasicRenderEngine sert de moteur de rendu à la scène.

/**
* initGraph
*/
private function initGraph():void
{
_basicRenderEngine     = new BasicRenderEngine();
_flarTransMatResult     = new FLARTransMatResult();
_viewport3D         = new Viewport3D();
_flarCamera3D        = new FLARCamera3D(_flarParam);
_flarBaseNode        = new FLARBaseNode();
_scene3D           = new Scene3D();
_scene3D.addChild(_flarBaseNode);

// graph
// bitmap material
_pMat = new BitmapFileMaterial("../assets/img/logo004.png");
_pMat.doubleSided = true;
_pMat.interactive = true;

// bitmap material
_sMat = new BitmapFileMaterial("../assets/img/terre-mars.jpg");
_sMat.doubleSided = true;00
_sMat.interactive = true;

_s = new Sphere(_sMat,100,100,30);
_p = new Plane(_pMat,100,100,10,10);

_flarCamera3D.lookAt(_s);
_flarCamera3D.zoom = 1;
_flarCamera3D.focus = 10;

addEventListener(MouseEvent.MOUSE_WHEEL,onZoom,false,0,true);

_flarBaseNode.addChild(_p);
addChild(_viewport3D);
}

pendant le rendu de la scene , la sphere tournera.
le rendu étant une terre effectuant une rotation sur son axe

/**
* rendu scene
*/
protected function renduScene():void
{
_s.yaw(30);

if(_flarCamera3D.zoom <= 25)
{
_flarCamera3D.zoom = _flarCamera3D.zoom+0.1;

}
else
{

}

}

La routine de detection : la fonction la plus importante
elle est appelle dans un enterFrame et grace au bitmap data recherche le pattern dans celui ci
les constantes DETECTION_THRESHOLD (valeur comprise en tre 50 et 100) et DETECTION_CONFIDENCE (valeur comprise entre 0.1 et 1) permettent un réglage plus ou moins précis de la détection du pattern.

si le pattern est détecte , nous effectuons un rendu de la scène papervision.


/**
* loopToDetectMarkerAndUpdate
*/
private function loopToDetectMarkerAndUpdate(e:Event):void
{
/* Copie la derniere still-frame de la video de la webcam
dans l'objet bitmapdata pour la detection */
_bitmapData.draw(_video);
renduScene();

try {

//    Detect *IF* the marker is found in the latest still-frame
if(    _flarSingleMarkerDetector.detectMarkerLite (_flarRgbRaster_BitmapData, DETECTION_THRESHOLD) &&
_flarSingleMarkerDetector.getConfidence() > DETECTION_CONFIDENCE) {

//    Repeatedly Loop and Adjust 3D Model to Match Marker
_flarSingleMarkerDetector.getTransformMatrix(_flarTransMatResult);
_flarBaseNode.setTransformMatrix(_flarTransMatResult);
_basicRenderEngine.renderScene(_scene3D, _flarCamera3D, _viewport3D);
}
}
catch (error : Error)
{
}
}