Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7018 Articles
article-image-introduction-hlsl-language
Packt
28 Jun 2013
8 min read
Save for later

Introduction to HLSL language

Packt
28 Jun 2013
8 min read
(For more resources related to this topic, see here.) Distance/Height-based fog Distance/Height-based fog is an approximation to the fog you would normally see outdoors. Even in the clearest of days, you should be able to see some fog far in the distance. The main benefit of adding the fog effect is that it helps the viewer estimate how far different elements in the scene are based on the amount of fog covering them. In addition to the realism this effect adds, it has the additional benefit of hiding the end of the visible range. Without fog to cover the far plane, it becomes easier to notice when far scene elements are clipped by the cameras far plane. By tuning the height of the fog you can also add a darker atmosphere to your scene as demonstrated by the following image: This recipe will demonstrate how distance/height-based fog can be added to our deferred directional light calculation. See the How it works… section for details about adding the effect to other elements of your rendering code. Getting ready We will be passing additional fog specific parameters to the directional light's pixel shader through a new constant buffer. The reason for separating the fog values into their own constant buffer is to allow the same parameters to be used by any other shader that takes fog into account. To create the new constant buffer use the following buffer descriptor: Constant buffer descriptor parameter   Value   Usage   D3D11_USAGE_DYNAMIC   BindFlags   D3D11_BIND_CONSTANT_BUFFER   CPUAccessFlags   D3D11_CPU_ACCESS_WRITE   ByteWidth   48   The reset of the descriptor fields should be set to zero. All the fog calculations will be handled in the deferred directional light pixel shader. How to do it... Our new fog constant buffer is declared in the pixel shader as follows: cbuffer cbFog : register( b2 ){float3 FogColor : packoffset( c0 );float FogStartDepth : packoffset( c0.w );float3 FogHighlightColor : packoffset( c1 );float FogGlobalDensity : packoffset( c1.w );float3 FogSunDir : packoffset( c2 );FogHeightFalloff : packoffset( c2.w );} The helper function used for calculating the fog is as follows: float3 ApplyFog(float3 originalColor, float eyePosY, float3eyeToPixel){float pixelDist = length( eyeToPixel );float3 eyeToPixelNorm = eyeToPixel / pixelDist;// Find the fog staring distance to pixel distancefloat fogDist = max(pixelDist - FogStartDist, 0.0);// Distance based fog intensityfloat fogHeightDensityAtViewer = exp( -FogHeightFalloff * eyePosY );float fogDistInt = fogDist * fogHeightDensityAtViewer;// Height based fog intensityfloat eyeToPixelY = eyeToPixel.y * ( fogDist / pixelDist );float t = FogHeightFalloff * eyeToPixelY;const float thresholdT = 0.01;float fogHeightInt = abs( t ) > thresholdT ?( 1.0 - exp( -t ) ) / t : 1.0;// Combine both factors to get the final factorfloat fogFinalFactor = exp( -FogGlobalDensity * fogDistInt *fogHeightInt );// Find the sun highlight and use it to blend the fog colorfloat sunHighlightFactor = saturate(dot(eyeToPixelNorm, FogSunDir));sunHighlightFactor = pow(sunHighlightFactor, 8.0);float3 fogFinalColor = lerp(FogColor, FogHighlightColor,sunHighlightFactor);return lerp(fogFinalColor, originalColor, fogFinalFactor);} The Applyfog function takes the color without fog along with the camera height and the vector from the camera to the pixel the color belongs to and returns the pixel color with fog. To add fog to the deferred directional light, change the directional entry point to the following code: float4 DirLightPS(VS_OUTPUT In) : SV_TARGET{// Unpack the GBufferfloat2 uv = In.Position.xy;//In.UV.xy;SURFACE_DATA gbd = UnpackGBuffer_Loc(int3(uv, 0));// Convert the data into the material structureMaterial mat;MaterialFromGBuffer(gbd, mat);// Reconstruct the world positionfloat2 cpPos = In.UV.xy * float2(2.0, -2.0) - float2(1.0, -1.0);float3 position = CalcWorldPos(cpPos, gbd.LinearDepth);// Get the AO valuefloat ao = AOTexture.Sample(LinearSampler, In.UV);// Calculate the light contributionfloat4 finalColor;finalColor.xyz = CalcAmbient(mat.normal, mat.diffuseColor.xyz) * ao;finalColor.xyz += CalcDirectional(position, mat);finalColor.w = 1.0;// Apply the fog to the final colorfloat3 eyeToPixel = position - EyePosition;finalColor.xyz = ApplyFog(finalColor.xyz, EyePosition.y,eyeToPixel);return finalColor;} With this change, we apply the fog on top of the lit pixels color and return it to the light accumulation buffer. How it works… Fog is probably the first volumetric effect implemented using a programmable pixel shader as those became commonly supported by GPUs. Originally, fog was implemented in hardware (fixed pipeline) and only took distance into account. As GPUs became more powerful, the hardware distance based fog was replaced by a programmable version that also took into account things such as height and sun effect. In reality, fog is just particles in the air that absorb and reflect light. A ray of light traveling from a position in the scene travels, the camera interacts with the fog particles, and gets changed based on those interactions. The further this ray has to travel before it reaches the camera, the larger the chance is that this ray will get either partially or fully absorbed. In addition to absorption, a ray traveling in a different direction may get reflected towards the camera and add to the intensity of the original ray. Based on the amount of particles in the air and the distance a ray has to travel, the light reaching our camera may contain more reflection and less of the original ray which leads to a homogenous color we perceive as fog. The parameters used in the fog calculation are: FogColor: The fog base color (this color's brightness should match the overall intensity so it won't get blown by the bloom) FogStartDistance: The distance from the camera at which the fog starts to blend in FogHighlightColor: The color used for highlighting pixels with pixel to camera vector that is close to parallel with the camera to sun vector FogGlobalDensity: Density factor for the fog (the higher this is the denser the fog will be) FogSunDir: Normalized sun direction FogHeightFalloff: Height falloff value (the higher this value, the lower is the height at which the fog disappears will be) When tuning the fog values, make sure the ambient colors match the fog. This type of fog is designed for outdoor environments, so you should probably disable it when lighting interiors. You may have noticed that the fog requires the sun direction. We already store the inversed sun direction for the directional light calculation. You can remove that value from the directional light constant buffer and use the fog vector instead to avoid the duplicate values This recipe implements the fog using the exponential function. The reason for using the exponent function is because of its asymptote on the negative side of its graph. Our fog implementation uses that asymptote to blend the fog in from the starting distances. As a reminder, the exponent function graph is as follows: The ApplyFog function starts off by finding the distance our ray traveled in the fog (fogDepth). In order to take the fog's height into account, we also look for the lowest height between the camera and the pixel we apply the fog to which we then use to find how far our ray travels vertically inside the fog (fogHeight). Both distance values are negated and multiplied by the fog density to be used as the exponent. The reason we negate the distance values is because it's more convenient to use the negative side of the exponential functions graph which is limited to the range 0 to 1. As the function equals 1 when the exponent is 0, we have to invert the results (stored in fogFactors). At this point we have one factor for the height which gets larger the further the ray travels vertically into the fog and a factor that gets larger the further the ray travels in the fog in any direction. By multiplying both factors with each other we get the combined fog effect on the ray: the higher the result is, the more the original ray got absorbed and light got reflected towards the camera in its direction (this is stored in fogFinalFactor). Before we can compute the final color value, we need to find the fog's color based on the camera and sun direction. We assume that the sun intensity is high enough to get more of its light rays reflected towards the camera direction and sun direction are close to parallel. We use the dot product between the two to determine the angle and narrow the result by raising it to the power of 8 (the result is stored in sunHighlightFactor). The result is used to lerp between the fog base color and the fog color highlighted by the sun. Finally, we use the fog factor to linearly interpolate between the input color and the fog color. The resulting color is then returned from the helper function and stored into the light accumulation buffer. As you can see, the changes to the directional light entry point are very minor as most of the work is handled inside the helper function ApplyFog. Adding the fog calculation to the rest of the deferred and forward light sources should be pretty straightforward. One thing to take into consideration is that fog also has to be applied to scene elements that don't get lit, like the sky or emissive elements. Again, all you have to do is call ApplyFog to get the final color with the fog effect. Summary In this article, we learned how to apply fog effect and add atmospheric scenes to the images. Resources for Article : Further resources on this subject: Creating and Warping 3D Text with Away3D 3.6 [Article] 3D Vector Drawing and Text with Papervision3D: Part 1 [Article] 3D Vector Drawing and Text with Papervision3D: Part 2 [Article]
Read more
  • 0
  • 0
  • 23689

article-image-building-chat-application
Packt
27 Jun 2013
4 min read
Save for later

Building a Chat Application

Packt
27 Jun 2013
4 min read
(For more resources related to this topic, see here.) The following is a screenshot of our chat application: Creating a project To begin developing our chat application, we need to create an Opa project using the following Opa command: opa create chat This command will create an empty Opa project. Also, it will generate the required directories and files automatically with the structure as shown in the following screenshot: Let's have a brief look at what these source code files do: controller.opa: This file serves as the entry point of the chat application; we start the web server in controller.opa view.opa: This file serves as an user interface model.opa: This is the model of the chat application; it defines the message, network, and the chat room style.css: This is an external stylesheet file Makefile: This file is used to build an application As we do not need database support in the chat application, we can remove --import-package stdlib.database.mongo from the FLAG option in Makefile. Type make and make run to run the empty application. Launching the web server Let's begin with controller.opa, the entry point of our chat application where we launch the web server. We have already discussed the function Server.start in the Server module section. In our chat application, we will use a handlers group to handle users requests. Server.start(Server.http, [ {resources: @static_resource_directory("resources")}, {register: [{css:["/resources/css/style.css"]}]}, {title:"Opa Chat", page: View.page } ]) So, what exactly are the arguments that we are passing to the Server.start function? The line {resources: @static_resource_direcotry("resources")} registers a resource handler and will serve resource files in the resources directory. Next, the line {register: [{css:["/resources/css/style.css"]}]} registers an external CSS file—style.css. This permits us to use styles in the style.css application scope. Finally, the line {title:"Opa Chat", page: View.page} registers a single page handler that will dispatch all other requests to the function View.page. The server uses the default configuration Server.http and will run on port 8080. Designing user interface When the application starts, all the requests (except requests for resources) will be distributed to the function View.page, which displays the chat page on the browser. Let's take a look at the view part; we define a module named View in view.opa. import stdlib.themes.bootstrap.css module View { function page(){ user = Random.string(8) <div id=#title class="navbar navbar-inverse navbar-fixed-top"> <div class=navbar-inner> <div id=#logo /> </div> </div> <div id=#conversation class=container-fluid onready={function(_){Model.join(updatemsg)}} /> <div id=#footer class="navbar navbar-fixed-bottom"> <div class=input-append> <input type=text id=#entry class=input-xxlarge onnewline={broadcast(user)}/> <button class="btn btn-primary" onclick={broadcast(user)}>Post</button> </div> </div> } ... } The module View contains functions to display the page on the browser. In the first line, import stdlib.themes.bootstrap.css, we import Bootstrap styles. This permits us to use Bootstrap markup in our code, such as navbar, navbar-fixtop, and btn-primary. We also registered an external style.css file so we can use styles in style.css such as conversation and footer. As we can see, this code in the function page follows almost the same syntax as HTML. As discussed in earlier, we can use HTML freely in the Opa code, the HTML values having a predefined type xhtml in Opa. Summary In this article, we started by creating and a project and launching the web server. Resources for Article : Further resources on this subject: MySQL 5.1 Plugin: HTML Storage Engine—Reads and Writes [Article] Using jQuery and jQueryUI Widget Factory plugins with RequireJS [Article] Oracle Web RowSet - Part1 [Article]
Read more
  • 0
  • 0
  • 7844

article-image-using-image-processing-techniques
Packt
27 Jun 2013
8 min read
Save for later

Using Image Processing Techniques

Packt
27 Jun 2013
8 min read
(For more resources related to this topic, see here.) In most of the examples, we will use the following famous test image widely used to illustrate computer vision algorithms and techniques: You can download Lenna's image from Wikipedia (http://hub.packtpub.com/wp-content/uploads/2013/06/FileLenna.png). Transforming image contrast and brightness In this recipe we will cover basic image color transformations using the Surface class for pixel manipulation. How to do it... We will create an application with simple GUI for contrast and brightness manipulation on the sample image. Perform the following steps to do so: Include necessary headers: #include "cinder/gl/gl.h" #include "cinder/gl/Texture.h" #include "cinder/Surface.h" #include "cinder/ImageIo.h" Add properties to the main class: float mContrast,mContrastOld; float mBrightness,mBrightnessOld; Surface32f mImage, mImageOutput; In the setup method an image is loaded for processing and the Surface object is prepared to store processed image: mImage = loadImage( loadAsset("image.png") ); mImageOutput = Surface32f(mImage.getWidth(), mImage.getHeight(), false); Set window size to default values: setWindowSize(1025, 512); mContrast = 0.f; mContrastOld = -1.f; mBrightness = 0.f; mBrightnessOld = -1.f; Add parameter controls to the InterfaceGl window: mParams.addParam("Contrast", &mContrast, "min=-0.5 max=1.0 step=0.01"); mParams.addParam("Brightness", &mBrightness, "min=-0.5 max=0.5 step=0.01"); Implement the update method as follows: if(mContrastOld != mContrast || mBrightnessOld != mBrightness) { float c = 1.f + mContrast; Surface32f::IterpixelIter = mImage.getIter(); Surface32f::IterpixelOutIter = mImageOutput.getIter(); while( pixelIter.line() ) { pixelOutIter.line(); while( pixelIter.pixel() ) { pixelOutIter.pixel(); // contrast transformation pixelOutIter.r() = (pixelIter.r() - 0.5f) * c + 0.5f; pixelOutIter.g() = (pixelIter.g() - 0.5f) * c + 0.5f; pixelOutIter.b() = (pixelIter.b() - 0.5f) * c + 0.5f; // brightness transformation pixelOutIter.r() += mBrightness; pixelOutIter.g() += mBrightness; pixelOutIter.b() += mBrightness; } } mContrastOld = mContrast; mBrightnessOld = mBrightness; } Lastly, we will draw the original and processed images by adding the following lines of code inside the draw method: gl::draw(mImage); gl::draw(mImageOutput, Vec2f(512.f+1.f, 0.f)); How it works... The most important part is inside the update method. In step 6 we checked if the parameters for contrast and brightness had been changed. If they have, we iterate through all the pixels of the original image and store recalculated color values in mImageOutput. While modifying the brightness is just increasing or decreasing each color component, calculating contrast is a little more complicated. For each color component we are using the multiplying formula, color = (color - 0.5) * contrast + 0.5, where contrast is a number between 0.5 and 2. In the GUI we are setting a value between -0.5 and 1.0, which is more natural range; it is then recalculated at the beginning of step 6. While processing the image we have to change color value of all pixels, so later in step 6, you can see that we iterate through later columns of each row of the pixels using two while loops. To move to the next row we invoked the line method on the Surface iterator and then the pixel method to move to the next pixel of the current row. This method is much faster than using, for example, the getPixel and setPixel methods. Our application is rendering the original image on the left-hand side and the processed image on the right-hand side, so you can compare the results of color adjustment. Integrating with OpenCV OpenCV is a very powerful open-source library for computer vision. The library is written in C++ so it can be easily integrated in your Cinder application. There is a very useful OpenCV Cinder block provided within Cinder package available at the GitHub repository (https://github.com/cinder/Cinder-OpenCV). Getting ready Make sure you have Xcode up and running with a Cinder project opened. How to do it… We will add OpenCV Cinder block to your project, which also illustrates the usual way of adding any other Cinder block to your project. Perform the following steps to do so: Add a new group to our Xcode project root and name it Blocks. Next, drag the opencv folder inside the Blocks group. Be sure to select the Create groups for any added folders radio button, as shown in the following screenshot: You will need only the include folder inside the opencv folder in your project structure, so delete any reference to others. The final project structure should look like the following screenshot: Add the paths to the OpenCV library files in the Other Linker Flags section of your project's build settings, for example: $(CINDER_PATH)/blocks/opencv/lib/macosx/libopencv_imgproc.a $(CINDER_PATH)/blocks/opencv/lib/macosx/libopencv_core.a $(CINDER_PATH)/blocks/opencv/lib/macosx/libopencv_objdetect.a These paths are shown in the following screenshot: Add the paths to the OpenCV Cinder block headers you are going to use in the User Header Search Paths section of your project's build settings: $(CINDER_PATH)/blocks/opencv/include This path is shown in the following screenshot: Include OpenCV Cinder block header file: #include "CinderOpenCV.h" How it works… OpenCV Cinder block provides the toOcv and fromOcv functions for data exchange between Cinder and OpenCV. After setting up your project you can use them, as shown in the following short example: Surface mImage, mImageOutput; mImage = loadImage( loadAsset("image.png") ); cv::Mat ocvImage(toOcv(mImage)); cv::cvtColor(ocvImage, ocvImage, CV_BGR2GRAY ); mImageOutput = Surface(fromOcv(ocvImage)); You can use the toOcv and fromOcv functions to convert between Cinder and OpenCV types, storing image data such as Surface or Channel handled through the ImageSourceRef type; there are also other types, as shown in the following table: Cinder types OpenCV types ImageSourceRef Mat Color Scalar Vec2f Point2f Vec2i Point Area Rect In this example we are linking against the following three files from the OpenCV package: libopencv_imgproc.a: This image processing module includes image manipulation functions, filters, feature detection, and more libopencv_core.a: This module provides core functionality and data structures libopencv_objdetect.a: This module has object detection tools such as cascade classifiers You can find the documentation on all OpenCV modules at http://docs.opencv.org/index.html. There's more… There are some features that are not available in precompiled OpenCV libraries packaged in OpenCV Cinder block, but you can always compile your own OpenCV libraries and still use exchange functions from OpenCV Cinder block in your project. Detecting edges In this recipe, we will demonstrate how to use edge detection function, which is one of the image processing functions implemented directly in Cinder. Getting ready Make sure you have Xcode up and running with an empty Cinder project opened. We will need a sample image to proceed, so save it in your assets folder as image.png. How to do it… We will process the sample image with the edge detection function. Perform the following steps to do so: Include necessary headers: #include "cinder/gl/Texture.h" #include "cinder/Surface.h" #include "cinder/ImageIo.h" #include "cinder/ip/EdgeDetect.h" #include "cinder/ip/Grayscale.h" Add two properties to your main class: Surface8u mImageOutput; Load the source image and set up Surface for processed images inside the setup method: mImage = loadImage( loadAsset("image.png") ); mImageOutput = Surface8u(mImage.getWidth(), mImage.getHeight(), false); Use image processing functions: ip::grayscale(mImage, &mImage); ip::edgeDetectSobel(mImage, &mImageOutput); Inside the draw method add the following two lines of code for drawing images: gl::draw(mImage); gl::draw(mImageOutput, Vec2f(512.f+1.f, 0.f)); How it works… As you can see, detecting edges in Cinder is pretty easy because of implementation of basic image processing functions directly in Cinder, so you don't have to include any third-party libraries. In this case we are using the grayscale function to convert the original image color space to grayscale. It is a commonly used feature in image processing because many algorithms work more efficiently on grayscale images or are even designed to work only with grayscale source images. The edge detection is implemented with the edgeDetectSobel function and uses the Sobel algorithm. In this case, the first parameter is the source original grayscale image and the second parameter, is the output Surface object in which the result will be stored. Inside the draw method we are drawing both images, as shown in the following screenshot: There's more… You may find the image processing functions implemented in Cinder insufficient, so you can also include to your project, third-party library such as OpenCV. We explained how we can use Cinder and OpenCV together in the preceding recipe, Integrating with OpenCV. Other useful functions in the context of edge detection are Canny and findContours. The following is the example of how we can use them: vector<vector<cv::Point> > contours; cv::Mat inputMat( toOcv( frame ) ); // blur cv::cvtColor( inputMat, inputMat, CV_BGR2GRAY ); cv::Mat blurMat; cv::medianBlur(inputMat, blurMat, 11); // threshold cv::Mat thresholdMat; cv::threshold(blurMat, thresholdMat, 50, 255, CV_8U ); // erode cv::Mat erodeMat; cv::erode(thresholdMat, erodeMat, 11); // Detect edges cv::Mat cannyMat; int thresh = 100; cv::Canny(erodeMat, cannyMat, thresh, thresh*2, 3 ); // Find contours cv::findContours(cannyMat, contours, CV_RETR_TREE, CV_CHAIN_APPROX_ SIMPLE); After executing the preceding code, the points, which form the contours are stored in the contours variable.
Read more
  • 0
  • 0
  • 4321

article-image-creating-your-first-collection-simple
Packt
26 Jun 2013
7 min read
Save for later

Creating your first collection (Simple)

Packt
26 Jun 2013
7 min read
(For more resources related to this topic, see here.) Getting ready Assuming that you have walked through the tutorial, you should be nearly ready with the setup. Still, it does not hurt to go through the checklist: Be familiar that you know how to start your operating system's shell (cmd.exe on Windows, Terminal/iTerm on Mac, and sh/bash/tch/zsh on Unix). Ensure that running the java –version command on the shell's prompt returns at least Version 1.6. You may need to upgrade if you have an older version. Ensure that you know where you unpacked the Solr distribution and the full path to the example directory within that. You needed that directory for the tutorial, but that's also where we are going to start our own Solr instance. That allows us to easily run an embedded Jetty web server and to also find all the additional JAR files that Solr needs to operate properly. Now, create a directory where we will store our indexes and experiments. It can be anywhere on your drive. As Solr can run on any operating system where Java can run, we will use SOLRINDEXING as a name whenever we refer to that directory. Make sure to use absolute path names when substituting with your real path for the directory. How to do it... As our first example, we will create an index that stores and allows for the searching of simplified e-mail information. For now, we will just look at the addr_from and addr_to e-mail addresses and the subject line. You will see that it takes only two simple configuration files to get the basic Solr index working. Under the SOLR-INDEXING directory, create a collection1 directory and inside that create a conf directory. In the conf directory, create two files: schema.xml and solrconfig.xml. The schema.xml file should have the following content: <?xml version="1.0" encoding="UTF-8" ?><schema version="1.5"><fields><field name="id" type="string" indexed="true" stored="true"required="true"/><field name="addr_from" type="string" indexed="true"stored="true" required="true"/><field name="addr_to" type="string" indexed="true"stored="true" required="true"/><field name="subject" type="string" indexed="true"stored="true" required="true"/></fields><uniqueKey>id</uniqueKey><types><fieldType name="string" class="solr.StrField" /></types></schema> The solrconfig.xml file should have the following content: <?xml version="1.0" encoding="UTF-8" ?><config><luceneMatchVersion>LUCENE_43</luceneMatchVersion><requestDispatcher handleSelect="false"><httpCaching never304="true" /></requestDispatcher><requestHandler name="/select" class="solr.SearchHandler" /><requestHandler name="/update" class="solr.UpdateRequestHandler" /><requestHandler name="/admin" class="solr.admin.AdminHandlers" /><requestHandler name="/analysis/field" class="solr.FieldAnalysisRequestHandler" startup="lazy" /></config> That is it. Now, let's start our just-created Solr instance. Open a new shell (we'll need the current one later). On that shell's command prompt, change the directory to the example directory of the Solr distribution and run the following command: java -Dsolr.solr.home=SOLR-INDEXING -jar start.jar Notice that solr.solr.home is not a typo; you do need the solr part twice. And, as always, if you have spaces in your paths (now or later), you may need to escape them in platform-specific ways, such as with backslashes on Unix/Linux or by quoting the whole value. In the window of your shell, you should see a long list of messages that you can safely ignore (at least for now). You can verify that everything is working fine by checking for the following three elements: The long list of messages should finish with a message like Started SocketConnector@0.0.0.0:8983. This means that Solr is now running on port 8983 successfully. You should now have a directory called data, right next to the directory called conf that we created earlier. If you open the web browser and go to the http:// localhost:8983/ solr/, you should see a web-based admin interface that makes testing and troubleshooting your Solr instance much easier. We will be using this interface later, so do spend a couple of minutes clicking around now. Now, let's load some actual content into our collection: Copy post.jar from the Solr distribution's example/exampledocs directory to our root SOLR-INDEXING directory. Create a file called input1.csv in the collection1 directory, next to the conf and data directories with the following three-line content: id,addr_from,addr_to,subjectemail1,fulan@acme.example.com,kari@acme.example.com,"Kari,we need more Junior Java engineers"email2,kari@acme.example.com,maija@acme.example.com,"Updating vacancy description" Run the import command from the command line in the SOLR-INDEXING directory (one long command; do not split it across lines): java -Dauto -Durl=http://localhost:8983/solr/collection1/update -jar post.jar collection1/input1.csv You should see the following in one of the message lines: "1 files indexed". If you now open a web browser and go to http:// localhost:8983/solr/ collection1/select?q=*%3A*&wt=ruby&indent=true, you should see Solr output with all the three documents displayed on the screen in a somewhat readable format. How it works... We have created two files to get our example working. Let's review what they mean and how they fit together: The schema.xml file in the collection's conf directory defines the actual shape of data that you want to store and index. The fields define a structure of a record. Each field has a type, which is also defined in the same file. The field defines whether it is stored, indexed, required, multivalued, or a small number of other, more advanced properties. On the other hand, the field type defines what is actually done to the field when it is indexed and when it is searched. We will explore all of these later. The solrconfig.xml file also in the collection's conf directory defines and tunes the components that make up Solr's runtime environment. At the very least, it needs to define which URLs can be called to add records to a collection (here, /update), which to query a collection (here, /select), and which to do various administrative tasks (here, /admin and /analysis/field). Once Solr started, it created a single collection with the default name of collection1, assigned an update handler to it at the /solr/collection1/update URL and search handler at the /solr/collection1/select URL (as per solrconfig.xml). At that point, Solr was ready for the data to be imported into the four required fields (as per schema.xml). We then proceeded to populate the index from a CSV file (one of many update formats available) and then verified that the records are all present in an indented Ruby format (again, one of many result formats available). Summary This article helped you create a basic Solr collection and populate it with a simple dataset in CSV format. Resources for Article : Further resources on this subject: Integrating Solr: Ruby on Rails Integration [Article] Indexing Data in Solr 1.4 Enterprise Search Server: Part2 [Article] Text Search, your Database or Solr [Article]
Read more
  • 0
  • 0
  • 4693

article-image-routing-external-activemq-broker
Packt
26 Jun 2013
3 min read
Save for later

Routing to an external ActiveMQ broker

Packt
26 Jun 2013
3 min read
(For more resources related to this topic, see here.) Getting ready In this recipe, we will assume that you have a standalone ActiveMQ broker installed, started, and is operational. To make the examples as simple as possible, we will also assume that the broker is exposed via the tcp://192.168.1.1:61616 TCP transport. How to do it... Create a new Camel routing project using Maven (if you don't know how to do it, refer to the Creating and deploying a new Camel route (Must know) recipe). In the camel-context.xml file, add the new Camel routing rule that produces to or consumes from the Camel ActiveMQ component connected to the standalone router. Build and deploy the routing module to your ServiceMix instance (if you don't know how to do it, refer to the Creating and deploying a new Camel route (Must know) recipe). How it works... Connecting to the external ActiveMQ broker is very similar to working with the embedded ActiveMQ broker provided with ServiceMix. The only difference is that instead of using the default settings of the Camel ActiveMQ component, you need to configure your route to consume from (or produce to) the standalone ActiveMQ broker. The Camel ActiveMQ component can be configured in many ways. One of the possible solutions is to register the component manually in the Spring application context. The following example demonstrates how to register and configure an ActiveMQ component in the Spring context: <camel:camelContext ><camel:route><camel:from uri="timer:jmsMessageTrigger?period=5000"/><camel:to uri="activemq:myQueue"/></camel:route></camel:camelContext><bean id="activemq"class="org.apache.activemq.camel.component.ActiveMQComponent"><property name="brokerURL" value="tcp://192.168.1.1:61616"/></bean> There's more... You can connect to many types of messaging brokers with ServiceMix. The Camel routing engine allows ServiceMix to integrate with any JMS-compatible messaging solutions. Camel also comes with support for some non-JMS messaging systems (such as XMPP, AMPQ, or Amazon SQS). The following is some additional information regarding the messaging support in ServiceMix. Generic JMS connectivity If you need to connect to a JMS broker other than ActiveMQ, use the Camel JMS component instead of the ActiveMQ component. The camel JMS component (http://camel.apache.org/jms.html) can be used to connect to any JMS-compatible messaging server (including ActiveMQ). Keep in mind, however, that if you connect to the ActiveMQ broker, it is better to stick to the dedicated ActiveMQ component, as the latter is optimized for the Apache message broker. As a result, you can expect easier configuration and slighter better performance when using a dedicated ActiveMQ component. ActiveMQ connection pooling A common mistake regarding the usage of JMS is to open a new client connection for each message sent to the broker. Creating a new connection to the broker is an expensive operation. The typical solution to optimize the JMS connection management is to reuse the already opened ones. This approach is called "Connection pooling", ActiveMQ comes with the connection factory that supports connection pooling; it is named org.apache.activemq. pool.PooledConnectionFactory. You should remember it whenever you configure your ActiveMQ connection in Camel. Summary This article explained, with examples, how you can in the real world connect to the standalone JMS broker. Resources for Article : Further resources on this subject: Using the OSGi Bundle Repository in OSGi and Apache Felix 3.0 [Article] Getting Started with Bookshelf Project in Apache Felix [Article] Geronimo Architecture: Part 2 [Article]
Read more
  • 0
  • 0
  • 11922

article-image-article-creating-your-first-heat-map-r
Packt
26 Jun 2013
10 min read
Save for later

Creating your first heat map in R

Packt
26 Jun 2013
10 min read
(For more resources related to this topic, see here.) The following image shows one of the heat maps that we are going to create in this recipe from the total count of air passengers: Image Getting ready Download the script 5644_01_01.r from your account at http://www.packtpub.com and save it to your hard disk. The first section of the script, below the comment line starting with ### loading packages, will automatically check for the availability of the R packages gplots and lattice, which are required for this recipe. If those packages are not already installed, you will be prompted to select an official server from the Comprehensive R Archive Network (CRAN) to allow the automatic download and installation of the required packages. If you have already installed those two packages prior to executing the script, I recommend you to update them to the most recent version by calling the following function in the R command line: code Use the source() function in the R command-line to execute an external script from any location on your hard drive. If you start a new R session from the same directory as the location of the script, simply provide the name of the script as an argument in the function call as follows: code   You have to provide the absolute or relative path to the script on your hard drive if you started your R session from a different directory to the location of the script. Refer to the following example: code   You can view the current working directory of your current R session by executing the following command in the R command-line: code   How to do it... Run the 5644OS_01_01.r script in R to execute the following code, and take a look at the output printed on the screen as well as the PDF file, first_heatmaps.pdf that will be created by this script: code How it works... There are different functions for drawing heat maps in R, and each has its own advantages and disadvantages. In this recipe, we will take a look at the levelplot() function from the lattice package to draw our first heat map. Furthermore, we will use the advanced heatmap.2() function from gplots to apply a clustering algorithm to our data and add the resulting dendrograms to our heat maps. The following image shows an overview of the different plotting functions that we are using throughout this book: Image Now let us take a look at how we read in and process data from different data files and formats step-by-step: Loading packages: The first eight lines preceding the ### loading data section will make sure that R loads the lattice and gplots package, which we need for the two heat map functions in this recipe: levelplot() and heatmap.2(). Each time we start a new session in R, we have to load the required packages in order to use the levelplot() and heatmap.2() functions. To do so, enter the following function calls directly into the R command-line or include them at the beginning of your script: library(lattice) library(gplots)   Loading the data set: R includes a package called data, which contains a variety of different data sets for testing and exploration purposes. More information on the different data sets that are contained in the data package can be found at http:// stat.ethz.ch/ROmanual/ROpatched/library/datasets/. For this recipe, we are loading the AirPassenger data set, which is a collection of the total count of air passengers (in thousands) for international airlines from 1949- 1960 in a time-series format. code Converting the data set into a numeric matrix: Before we can use the heat map functions, we need to convert the AirPassenger time-series data into a numeric matrix first. Numeric matrices in R can have characters as row and column labels, but the content itself must consist of one single mode: numerical. We use the matrix() function to create a numeric matrix consisting of 12 columns to which we pass the AirPassenger time-series data row-by-row. Using the argument dimnames = rowcolNames, we provide row and column names that we assigned previously to the variable rowColNames, which is a list of two vectors: a series of 12 strings representing the years 1949 to 1960, and a series of strings for the 12 three-letter abbreviations of the months from January to December, respectively. code A simple heat map using levelplot(): Now that we have converted the AirPassenger data into a numeric matrix format and assigned it to the variable air_data, we can go ahead and construct our first heat map using the levelplot() function from the lattice package: code The levelplot() function creates a simple heat map with a color key to the righthand side of the map. We can use the argument col.regions = heat.colors to change the default color transition to yellow and red. X and y axis labels are specified by the xlab and ylab parameters, respectively, and the main parameter gives our heat map its caption. In contrast to most of the other plotting functions in R, the lattice package returns objects, so we have to use the print() function in our script if we want to save the plot to a data file. In an interactive R session, the print() call can be omitted. Typing the name of the variable will automatically display the referring object on the screen. Creating enhanced heat maps with heatmap.2(): Next, we will use the heatmap.2() function to apply a clustering algorithm to the AirPassenger data and to add row and column dendrograms to our heat map: code Hierarchical clustering is especially popular in gene expression analyses. It is a very powerful method for grouping data to reveal interesting trends and patterns in the data matrix. Another neat feature of heatmap.2() is that you can display a histogram of the count of the individual values inside the color key by including the argument density.info = NULL in the function call. Alternatively, you can set density. info = "density" for displaying a density plot inside the color key. By adding the argument keysize = 1.8, we are slightly increasing the size of the color key—the default value of keysize is 1.5: code Did you notice the missing row dendrogram in the resulting heat map? This is due to the argument dendrogram = "column" that we passed to the heat map function. Similarly, you can type row instead of column to suppress the column dendrogram, or use neither to draw no dendrogram at all. There's more... By default, levelplot() places the color key on the right-hand side of the heat map, but it can be easily moved to the top, bottom, or left-hand side of the map by modifying the space parameter of colorkey: Replacing top by left or bottom will place the color key on the left-hand side or on the bottom of the heat map, respectively. Moving around the color key for heatmap.2() can be a little bit more of a hassle. In this case we have to modify the parameters of the layout() function. By default, heatmap.2() passes a matrix, lmat, to layout(), which has the following content: code The numbers in the preceding matrix specify the locations of the different visual elements on the plot (1 implies heat map, 2 implies row dendrogram, 3 implies column dendrogram, and 4 implies key). If we want to change the position of the key, we have to modify and rearrange those values of lmat that heatmap.2() passes to layout(). For example, if we want to place the color key at the bottom left-hand corner of the heat map, we need to create a new matrix for lmat as follows: code We can construct such a matrix by using the rbind() function and assigning it to lmat: code Furthermore, we have to pass an argument for the column height parameter lhei to heatmap.2(), which will allow us to use our modified lmat matrix for rearranging the color key: code If you don't need a color key for your heat map, you could turn it off by using the argument key = FALSE for heatmap.2() and colorkey = FALSE for levelplot(), respectively. R also has a base function for creating heat maps that does not require you to install external packages and is most advantageous if you can go without a color key. The syntax is very similar to the heatmap.2() function, and all options for heatmap.2() that we have seen in this recipe also apply to heatmap(): code More information on dendrograms and clustering By default, the dendrograms of heatmap.2() are created by a hierarchical agglomerate clustering method, also known as bottom-up clustering. In this approach, all individual objects start as individual clusters and are successively merged until only one single cluster remains. The distance between a pair of clusters is calculated by the farthest neighbor method, also called the complete linkage method, which is based by default on the Euclidean distance of the two points from both clusters that are farthest apart from each other. The computed dendrograms are then reordered based on the row and column means. By modifying the default parameters of the dist() function, we can use another distance measure rather than the Euclidean distance. For example, if we want to use the Manhattan distance measure (based on a grid-like path rather than a direct connection between two objects), we would modify the method parameter of the dist() function and assign it to a variable distance first: code Other options for the method parameter are: euclidean (default), maximum, canberra, binary, or minkowski. To use other agglomeration methods than the complete linkage method, we modify the method parameter in the hclust() function and assign it to another variable cluster. Note the first argument distance that we pass to the hclust() function, which comes from our previous assignment: code By setting the method parameter to ward, R will use Joe H. Ward's minimum variance method for hierarchical clustering. Other options for the method parameter that we can pass as arguments to hclust() are: complete (default), single, average, mcquitty, median, or centroid. To use our modified clustering parameters, we simply call the as.dendrogram() function within heatmap.2() using the variable cluster that we assigned previously: code We can also draw the cluster dendrogram without the heat map by using the plot() function: code To turn off row and column reordering, we need to turn off the dendrograms and set the parameters Colv and Rowv to NA: code Summary This article has helped us create our first heat maps from a small data set provided in R. We have used different heat map functions in R to get a first impression of their functionalities. Resources for Article :   Further resources on this subject: Getting started with Leaflet [Article] Moodle 1.9: Working with Mind Maps [Article] Joomla! with Flash: Showing maps using YOS amMap [Article]
Read more
  • 0
  • 0
  • 7099
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-understanding-maven
Packt
25 Jun 2013
10 min read
Save for later

Understanding Maven

Packt
25 Jun 2013
10 min read
(For more resources related to this topic, see here.) A Maven project A Maven project is simply a folder in your filesystem (also known as the project root folder) that contains a file called pom.xml, the XML representation of your Project Object Model (POM); this is the first—and most important—Maven convention. This minimal structure allows you to run a mvn command from the project root folder. By default, the mvn command searches for a pom.xml file in the local folder and it stops immediately if it is not able to find it. By convention, all artifacts generated by the build are delivered in a folder—relative to the pom.xml location—known as Build Directory (the target by default). Since the target is generated on each build, it is: Safe to delete it anytime Crucial to ignore it when sharing the project using a Version Control System software A Maven project defines a packaging, which identifies the main objective of the build, which in turn specifies the artifact that is going to be produced by the invocation of the build.Default JAR (other values are EAR, EJB, RAR, PAR, WAR, and POM). POM packaging is an exception, since: The Maven build does not produce an artifact The Maven build considers—as the only artifact—the main pom.xml file of the Maven project A POM Maven project can be useful for the following activities: Aggregate dependencies (for more information, you can navigate to Lifecycle | Dependency Management) Parent POM (for more information, you can navigate to Lifecycle | Multi-module project) Super POM Every Maven POM implicitly inherits from Super POM (more information is available at http://maven.apache.org/ref/3.0.5/maven-model-builder/super-pom.html), which contains all the default values that are needed to perform built-in Maven features, as we will see later in this book. Super POM is provided by the Maven installation. It is not intended to be changed—as it would cause build portability issues (more information is available at http://www.devx.com/Java/Article/32386)–but it is definitely interesting to read and investigate it further (more information is available at http://maven.apache.org/ guides/introduction/introduction-to-the-pom.html) in order to be more confident when using/overriding values in your pom.xml file. Artifact An artifact–in a Maven context–is a file that is (or has been) produced by a build execution and represents an application binary (of a specific version) that is subject to a lifecycle. An artifact can have diff erent purposes, listed as follows: A project's library: JAR, WAR, EAR, ZIP, or any file extension you may want to integrate in your build. Maven plugin: A JAR application containing the logic to execute build executions. Maven archetype: A JAR application containing the logic to create a Maven project with a pre-defined file and folder content. Archetypes will be introduced in the next section. Project descriptor: As we'll see shortly, a POM is itself an artifact. The following coordinates uniquely identify an artifact: groupId: This coordinate represents the group, company, team, organization, and/ or project related with the given artifact. The convention is the same as that of the Java packages (more information is available at http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html6). For example, projects from Apache Software Foundation would have a groupId coordinate that starts with org.apache. A good rule of thumb for deciding the granularity of groupId is by following the project's structure. For example, com.mycompany.myproject.persistence, com.mycompany.myproject.api, and so on. artifactId: This coordinate represents the name of the project (or module) related with the given artifact. artifactId must not contain any version-related information; if the artifact is a module, it is advised to join the project name with the module one (that is, commons-logging). Using only lowercase letters and the dash (-) as a separator is a clear and consolidated strategy. Good examples for artifactID are maven-core, commons-math, and log4j-over-sl4j. type: The extension (and filetype) of the artifact; the default type is JAR, but it can have any extension, such as WAR, EAR, or any other. version: This coordinate is a specific release of a project. It consists of a group of literals separated by dots; for example, 1.0, 2.0.1-RC1, and 2.0.0.1-alpha-2. If the version ends with the –SNAPSHOT literal, it means that the artifact is a nightly build and therefore not released yet. In order to take full advantage of Maven's version management, every work in progress project should have a –SNAPSHOT version; we will discuss this later. classifier: This is an additional coordinate to handle two (or more) artifacts having the same coordinates but containing a diff erent content; empty by default. For example, Maven identifies binary and source (source artifacts are archives containing the source code of your application, for more on this you can visit http://maven.apache.org/ plugin-developers/cookbook/attach-source-javadoc-artifacts.html) artifacts using the same coordinates, but with diff erent classifiers. For more information about naming conventions, check the official Maven documentation available at http://maven.apache.org/guides/mini/guide-naming-conventions.html Minimal pom.xml An example of a minimal pom.xml file is as follows: <project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>app-web</artifactId> <version>1.0-SNAPSHOT</version> <type>war</type> </project> The pom.xml file is the central configuration file for a Maven project, and it is fundamental to understand it deeply; it contains information about the project structure, metadata, and confi guration related with the plugin's executions. The modelVersion element is in every pom.xml; it represents the pom.xml XML schema version and is set to 4.0.0 for all Maven 2.x and 3.x-based builds. The–SNAPSHOT suffix in a pom.xml file specifies that the artifacts produced by this build are unreleased, and therefore they considered nightly builds. In this section we will introduce the most important elements of a POM; some will not be mentioned, while some others will be briefl y introduced. Parent (also known as POM Inheritance) A pom.xml file can define a parent as a pointer to a POM artifact. As a result, all parent's Maven configurations will be inherited. <parent> <groupId>com.mycompany.myproject</groupId> <artifactId>my-parent-pom</artifactId> <version>1.0-SNAPSHOT</version> </parent> Plugin A Maven plugin is a JAR Maven artifact containing Java classes that implement one or more goals using the Maven Plugin API (more information is available at http://maven.apache. org/ref/3.0.5/maven-plugin-api), and declares a public short name (that is, tomcat7x). <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <url>http://127.0.0.1:8080/manager</url> <server>Tomcat</server> <path>/app-web</path> </configuration> </plugin> </plugins> </build> The goal identifies a build task; it has a unique name (that is, run) within the same plugin. It can access and change the POM and provide a wide range of operations, from zipping a folder to performing a remote deployment on a Tomcat server. A goal can be executed by the mvn command using the following syntax: <plugin_shortname>:<goal_name>, for example mvn tomcat7x:run A goal can be invoked by the lifecycle phase; you will read more about it in the Maven lifecycle section. All plugins having an artifactId coordinate starting with maven- are directly supported by Maven projects (more information is available at http://maven.apache.org/plugins/). In order to understand how to use a Maven Plugin, search for its official documentation page; the Usage page (available at http://tomcat.apache.org/maven-plugin-2.0/tomcat7- maven-plugin/usage.html) explains how to add the plugin to your build; the Goals page (available at http://tomcat.apache.org/maven-plugin-2.0/tomcat7-maven-plugin/plugin-info.html) lists all goals and parameters that you can set. Repository A Maven Repository is a folder with a specific layout that can optionally be located remotely: <repositories> <repository> <id>my-custom-repo</id> <url>http://artifacts.mysite.com/repository</url> </repository> </repositories> The Repository layout is a key convention in Maven that allows you to uniquely locate an artifact: <repository_url>/<groupId>/<artifactId>/<version>/<artifactId>- <classifier>-<version>.<type> For example, you can have the following coordinates for the preceding artifact: groupId: org.apache.solr artifactId: solr version: 4.3.0 type: .war For a remote URL, the Repository URL can be: http://repo.maven.apache.org/maven2 The layout key will hence be: http://repo.maven.apache.org/maven2/org/apache/solr/solr/4.3.0/solr-4.3.0.war For a local URL, the Repository URL can be: file: ///Users/mau/.m2/repository The layout key will hence be: file: ///Users/mau/.m2/repository/org/apache/solr/solr/4.3.0/ solr-4.3.0.war A Maven Repository is the source and the destination of artifacts in the following scenarios: Source: When a Maven build depends on one or more artifacts, the Maven Repository is the place where these fi les are resolved and downloaded from Destination: When a Maven build produces one or more artifacts, it may be— optionally—deployed on a Maven Repository A Maven Repository can restrict the download/upload artifact operations depending whether the artifact's version is a -SNAPSHOT literal or not. This way, you can easily defi ne nightly builds, repositories, and defi ne tailored maintenance operations (that is, remove -SNAPSHOT artifacts after 30 days). -SNAPSHOT artifacts are a special case for Maven Repositories: When uploaded, the -SNAPSHOT literal of the artifact name will be replaced with the current timestamp (more information is available at http://docs.oracle.com/javase/6/docs/api/java/sql/Timestamp.html) When downloaded, the revolved artifact will be the one with the highest timestamp (most recently uploaded amongst all other –SNAPSHOT artifacts having the same coordinates) The Super POM defines two very special repositories: Local Repository: This repository is a local folder located in ~/.m2/repository (~ means user home in Linux, Unix, and OS X environments). The Local Repository works as a cache for all remotely-fetched artifacts: every time Maven downloads an artifact for you, it will do it only once. This rule does not apply to -SNAPSHOT artifacts, since these versions are supposed to change frequently; in this case, the build will ask the Maven Repository whether the -SNAPSHOT artifact was updated since the last fetch. Maven Central Repository: This is a remote Maven repository containing the official releases of Maven core plugins—which deliver all built-in functionalities of Apache Maven—and the biggest collection of Java artifacts in the world (Java.net and Oracle are hosted here, and many other companies, projects, and communities). Maven Central is open (more information is available at http://www.sonatype.org/central/participate ) to the contribution of anyone who wants to share their artifacts with the rest of the world.You can browse Maven Central using http://search.maven.org (shown in the following screenshot). Maven Central is hosted by Sonatype (more information is available at http://www.sonatype.org/). Summary Thus this article helps us understand Maven vocabulary more closely as it is very important to have all the concepts clear in order to gain a complete understanding of Maven. Resources for Article : Further resources on this subject: Integrating Scala, Groovy, and Flex Development with Apache Maven [Article] Implementing Software Engineering Best Practices and Techniques with Apache Maven [Article] Setting Up Tools to Build Applications Using jBPM: Part 2 [Article]
Read more
  • 0
  • 0
  • 4524

article-image-linking-section-access-multiple-dimensions
Packt
25 Jun 2013
3 min read
Save for later

Linking Section Access to multiple dimensions

Packt
25 Jun 2013
3 min read
(For more resources related to this topic, see here.) Getting ready Load the following script: Product:LOAD * INLINE [ ProductID, ProductGroup, ProductName 1, GroupA, Great As 2, GroupC, Super Cs 3, GroupC, Mega Cs 4, GroupB, Good Bs 5, GroupB, Busy Bs];Customer:LOAD * INLINE [ CustomerID, CustomerName, Country 1, Gatsby Gang, USA 2, Charly Choc, USA 3, Donnie Drake, USA 4, London Lamps, UK 5, Shylock Homes, UK];Sales:LOAD * INLINE [ CustomerID, ProductID, Sales 1, 2, 3536 1, 3, 4333 1, 5, 2123 2, 2, 45562, 4, 1223 2, 5, 6789 3, 2, 1323 3, 3, 3245 3, 4, 6789 4, 2, 2311 4, 3, 1333 5, 1, 7654 5, 2, 3455 5, 3, 6547 5, 4, 2854 5, 5, 9877];CountryLink:Load Distinct Country, Upper(Country) As COUNTRY_LINKResident Customer;Load Distinct Country, 'ALL' As COUNTRY_LINKResident Customer;ProductLink:Load Distinct ProductGroup, Upper(ProductGroup) As PRODUCT_LINKResident Product;Load Distinct ProductGroup, 'ALL' As PRODUCT_LINKResident Product;//Section Access;Access:LOAD * INLINE [ ACCESS, USERID, PRODUCT_LINK, COUNTRY_LINKADMIN, ADMIN, *, * USER, GM, ALL, ALL USER, CM1, ALL, USA USER, CM2, ALL, UK USER, PM1, GROUPA, ALL USER, PM2, GROUPB, ALL USER, PM3, GROUPC, ALL USER, SM1, GROUPB, UK USER, SM2, GROUPA, USA];Section Application; Note that there is a loop error generated on reload because there is a loop in the data structure. How to do it… Follow these steps to link Section Access to multiple dimensions: Add list boxes to the layout for ProductGroup and Country. Add a statistics box for Sales. Remove // to uncomment the Section Access statement. From the Settings menu, open Document Properties and select the Opening tab. Turn on the Initial Data Reduction Based on Section Access option. Reload and save the document. Close QlikView. Re-open QlikView and open the document. Log in as the Country Manager, CM1, user. Note that USA is the only country. Also, the product group, GroupA, is missing—there are no sales of this product group in USA. Close QlikView and then re-open again. This time, log in as the Sales Manager, SM2. You will not be allowed access to the document. Log into the document as the ADMIN user. Edit the script. Add a second entry for the SM2 user in the Access table as follows: USER, SM2, GROUPA, USA USER, SM2, GROUPB, UK Reload, save, and close the document and QlikView. Re-open and log in as SM2. Note the selections. How it works… Section Access is really quite simple. The user is connected to the data and the data is reduced accordingly. QlikView allows Section Access tables to be connected to multiple dimensions in the main data structure without causing issues with loops. Each associated field acts in the same way as a selection in the layout. The initial setting for the SM2 user contained values that were mutually exclusive. Because of the default Strict Exclusion setting, the SM2 user cannot log in. We changed the script and included multiple rows for the SM2 user. Intuitively, we might expect that, as the first row did not connect to the data, only the second row would connect to the data. However, each field value is treated as an individual selection and all of the values are included. There's more… If we wanted to include solely the composite association of Country and ProductGroup, we would need to derive a composite key in the data set and connect the user to that. In this example, we used the USERID field to test using QlikView logins. However, we would normally use NTNAME to link the user to either a Windows login or a custom login. Resources for Article : Further resources on this subject: Pentaho Reporting: Building Interactive Reports in Swing [Article] Visual ETL Development With IBM DataStage [Article] A Python Multimedia Application: Thumbnail Maker [Article]
Read more
  • 0
  • 0
  • 6166

article-image-monitoring-weblogic-server-12c
Packt
21 Jun 2013
6 min read
Save for later

Monitoring WebLogic Server 12c

Packt
21 Jun 2013
6 min read
(For more resources related to this topic, see here.) Customizing the Administration Console tables The Administration Console is the central tool for managing, configuring, and monitoring WebLogic. This recipe shows how to customize the Administration Console tables to display more columns and more information and data that are hidden by default. This is a simple but essential feature to help monitor the WebLogic Server. Getting ready Access the Administration Console. The following procedure customizes the threads' monitoring table of the Managed Server Self-Tuning's thread pool. How to do it... Carry out the following steps to customize the Administration Console tables: Access the Administration Console with your web browser at http://adminhost.domain.local:7001/console. Click on the plus sign to open the Environment tree on the left and then click on the Servers link. Click on any server, such as the PROD_Server01 link. Click on the Monitoring tab and then on the Threads tab to open the Threads page. Click on the second Customize this table link of the Self-Tuning Thread Pool Threads table. Click on the >> button to add the columns Application, Module, and Work Manager. Change the Number of rows displayed per page value to 1000. Click on the Apply button. How it works... The Administration Console allows the user to customize and add more columns to the monitoring tables. In this recipe, the Application, Module, and Work Manager columns are added to the Self-Tuning Thread Pool Threads table. The added columns are very useful to monitor the application requests being processed. The following table displays thread 0 processing a request of the testWeb application and thread 1 processing a request of the myApp application. Customizing the Administration Console monitoring tables is a common task and can be applied in a variety of tables, such as the data sources, the JMS queues, and transactions. Using the JRockit Mission Control Management Console Mission Control is a monitoring and troubleshooting application provided with Oracle JRockit. From a monitoring point of view, Mission Control provides a Management Console to monitor the garbage-collection behavior, processor utilization by the JVM, memory allocation, thread utilization, and some other useful monitoring metrics. Mission Control is a standalone application, so it must be started either locally from the same machine that WebLogic is running on or from a remote workstation. If you run Mission Control locally on the Linux server prod01, an X window must be available. This recipe will run Mission Control in a Microsoft Windows desktop and will remotely connect and monitor the PROD_Server01 Managed Server. Getting ready Oracle JRockit must be downloaded and installed in the Windows desktop. Download Oracle JRockit 6 for Microsoft Windows at http://www.oracle.com/technetwork/ middleware/jrockit/downloads. The filename is jrockit-jdk1.6.0_XXX-windowsiaYY. exe, where XXX stands for the JRockit release and JDK version and YY stands for 32 bits or 64 bits. Choose the version that matches your desktop and accept the license agreement to download it. How to do it... Enable the PROD_Server01 Managed Server to accept JMX connections from Mission Control: Access the Administration Console with your web browser at http://adminhost.domain.local:7001/console. Click on the Lock & Edit button to start a new edit session. Click on the plus sign to open the Environment tree on the left and then click Servers. Click on the PROD_Server01 link and then click the Server Start tab. Add the following to the Arguments field and click on the Save button: -Xmanagement:autodiscovery=false,authenticate=false,ssl=false,interface=prodsrv01.domain.local,port=8081 -Djava.rmi.server.hostname=prodsrv01.domain.local -Djavax.management.builder.initial=weblogic.management.jmx.mbeanserver.WLSMBeanServerBuilder Click on the Activate Changes button. Restart PROD_Server01. Start Mission Control on the desktop, as follows: Start Mission Control by double-clicking on the Oracle JRockit Mission Control icon. On the JVM Browser panel to the left, right-click on the Connectors folder and click on the New Connection option. Type prodsrv01.domain.local in the Host field and 8081 in the Port field and click on the Finish button. Right-click on the newly created connection, prodsrv01.domain.local:8081, and click on the Start Console menu option. How it works... Mission Control connects to the specified host and port defined in the Xmanagement start-up argument and starts monitoring the PROD_Server01 JVM. Add the start-up arguments to monitor the other WebLogic instances. Mission Control can connect to any running JRockit JVM. Monitoring Linux with SAR A Linux host with Red Hat Enterprise Linux or Oracle Linux can be monitored using the SAR command-line utility. The SAR is included in the SYSSTAT package bundle and is usually included with these Linux distributions. SAR retrieves activity counters of the operational system, such as CPU, memory, disk, and network I/O usage. By default, it keeps a history of seven days, so it is a very useful tool to retrieve past reports and quickly search for behavioral patterns. This recipe will retrieve some statistics from the prod01 machine using the SAR command-line utility. Getting ready SAR should already be installed in a Red Hat or Oracle Linux distribution. If it is not installed, you can use the yum package management utility to install the SYSSTAT package, which includes SAR. As root user, execute the yum command and follow the onscreen instructions to install SYSSTAT: [root@prod01]$ yum install sysstat All SAR commands are executed from the Linux shell. Log in to the host first. The root user is used only to install the SYSSTAT package. How to do it... To retrieve the queue length and load averages from the current day, as a wls user execute the following command: [wls@prod01]$ sar -q This will display the following result: To retrieve a past CPU usage from the 21st day of the month, as a wls user execute the following command: [wls@prod01]$ sar –u –f /var/log/sa/sa21 This will display the following result: The default SAR configuration keeps historical data for a week. How it works... The SAR utility is very flexible and provides a quick way to watch the host behavior for the current day and for the past seven days. SAR runs every 10 minutes and saves a summary at the end of the day. These are the default values in the crontab and can be adjusted. There's more... Apart from the options discussed earlier, we can view more fine-grained data as well. Collecting SAR data every minute SAR can store statistical data in a more fine-grained time interval. Log in as a root user to the shell and execute the following command: [root@prod01]$ vi /etc/cron.d/sysstat Locate the following lines: # run system activity accounting tool every 10 minutes*/10 * * * * root /usr/lib64/sa/sa1 1 1 Change these lines to the following: # run system activity accounting tool every 1 minute*/1 * * * * root /usr/lib64/sa/sa1 1 1 Type :wq! to save and close the file.
Read more
  • 0
  • 0
  • 3239

article-image-more-odi
Packt
20 Jun 2013
18 min read
Save for later

More on ODI

Packt
20 Jun 2013
18 min read
(For more resources related to this topic, see here.) Invoking an external program that requires a password There are many instances of external programs that will require a password to be used. For instance, you may be required to establish a VPN connection before you can even connect to your databases. The example we will use here, because it is easier to demonstrate, will be to decrypt an encrypted file using an external program. Getting ready The external program that we will use to build in this recipe is AES Encrypt, an open source encryption application that can be downloaded at http://www.aescrypt.com/ download.html. After downloading the console version of the program for your operating system, unzip the executable and put it in a folder next to the file that you will want to encrypt and decrypt. The steps of this recipe assume that we have copied the executable in the c: temp directory. If you are using another operating system or if you decide to put the program in a different folder, you will have to adapt the instructions of this recipe accordingly. How to do it... Open a command line console and go to the c:temp directory. Add a text file of your choice in this directory. Here we are using a file called foo.txt: Run the following from the command line: c:temp> aescrypt -e -p oracledi foo.txt This will generate an encrypted file named foo.txt.aes that is protected with the password oracledi. If you try to edit the content of this new file, you will see that it has indeed been encrypted: Rename the original foo.txt file to foo.txt.ori. We will now use ODI to decrypt the file and restore the original content. In ODI Topology, create a new Data Server in the File technology called AESCRYPT. We will use this data server as a placeholder for the password that we need for this operation. We can also use this to locate the file that we will work on. You can leave the User name empty (we do not need it here), but do set the Password value to oracledi. In the JDBC tab, select the ODI File JDBC Driver and use the default JDBC URL: jdbc:snps:dbfile. Create a physical schema under that server. Set both the Schema name and Work Schema name to c:temp. Create a logical schema to point to this physical schema for the default context: FILE_AESCRYPT Switch to the Designer operator, and in the project of your choice, create a procedure called Ch11_Password_Decrypt_File. Add a new step to this procedure, and call this Decrypt. In the Command on Target tab, set the Technology to OS Command (leave all other fields to their default values) and type the following command (this is all one line of code, so please ignore the carriage returns that are only due to formatting): <%=odiRef.getInfo("SRC_SCHEMA")%>aescrypt -d -p <@=odiRef.getInfo("SRC_PASS")@> <%=odiRef.getInfo("SRC_SCHEMA")%>foo.txt.aes The technology OS Command can be used to execute any type of command at the OS level. One note of caution though: if the script or program that you execute returns anything else than 0, ODI will consider that the program failed. Typically there are two techniques that can be combined to solve this problem: Redirect any output or errors to other files. For instance, dir 1>dir.out 2>dir.err, where 1 represents the standard output and 2 represents the errors output. Use Jython code to execute the code and retrieve the return code in a variable. Then evaluate this variable to see whether the returned value indicates an error or is simply informational. In the Command on Source tab, set the Technology to File, set the logical Schema name to FILE_AESCRYPT, and leave all other fields to their default values. There will be no code on the source side. Save the procedure and run it. Now, if you go to the Operator window to look into the details of the code that was generated, you can see that the password is not revealed in the operator logs. We are looking in the following screenshot at the code generated for the task: Now back to the console, we can see that the original un-encrypted file, foo.txt, was restored along with its original content. You can choose to keep or delete the encrypted file foo.txt.aes. How it works... It is very common to have code written on both the source and target commands of a KM (LKMs and multi-technology IKMs) or in a procedure step. One technique that is not necessarily well known is to use the source technology as a placeholder for connectivity information that can be used in the code on the target side. In our example, the technology on the target side is OS Command, which does not give us much flexibility in terms of configuration and connectivity parameters. So, we use an artificial source connection to point to the data server that contains the information we need. Then we leverage that information as needed by leveraging the odiRef.getInfo substitution method to extract the required parameters. In addition, ODI makes sure that the password that we retrieve and pass into the external tool is never revealed in the operator logs, as it is encapsulated with the <@@> syntax. There's more... The selection of logical schemas in the source and target connections allows us to leverage any of the parameters defined in either connection. If we were to establish a VPN connection for instance, we could leverage the Host (Data Server) entry to retrieve the name of the remote server then retrieve the username and password to authenticate against this server. When dealing with external components, which require this type of information, think of leveraging Topology to securely store the connectivity information. The additional benefit is that if you leverage ODI contexts, the same logic will work across all environments, from development to production. Tuning a standalone ODI agent An ODI agent orchestrates all ODI executions. As such, it is a central piece of the infrastructure. Properly tuning the agent and understanding what requires tuning will help you better manage your infrastructure. Getting ready All we need for this recipe is to have a standalone agent already installed on one of your systems. If you do not have an agent available, you can run the ODI installer and select the ODI Standalone Agent option. For the recipe that follows, we assume that the agent was installed on a Windows machine, in the folder c:oraclediproducts11.1.1.6. If your agent is installed in a different directory or on a different operating system, keep this in mind as you follow these steps. How to do it... Go to the bin directory of the agent: c:oraclediproducts11.1.1.6oraclediagentbin Edit the file odiparams.bat (you will have to edit the odiparams.sh file on Linux or Unix systems). In the file, identify the parameters ODI_INIT_HEAP and ODI_MAX_HEAP. You will notice that the default values are 32 and 256 respectively. Double these values to 64 and 512, then save the file: If your agent is already running, you will have to stop and restart the agent to take these new values into consideration. How it works... The parameters we have modified control how much memory is initially allocated to the ODI agent (ODI_INIT_HEAP) and what is the maximum amount of memory that the agent can request (ODI_MAX_HEAP). These parameters are very important because they control the amount of memory available to the agent. Most actions performed by the agents are running in memory: When data is fetched with a JDBC connection, this data will be transferred through the agent memory space If you use the ODI memory engine, this in-memory database will actually use the memory space of the agent The default behavior of the ODI JDBC driver for XML is to load data into the in-memory database, and as such, it uses more of the agent's allocated memory These operations are in addition to the orchestrations that the agent always performs: Reading the scenarios from the repository Completing the scenario code according to the context selected for the execution of that scenario Updating the logs with execution results and statistics The more operations you ask for the agent to run in memory, the more memory you will need to allocate to the agent. Keep in mind that if you are running several agents on the same server, each agent will have its own memory space. This can be useful to segregate memory intensive operations, but this will also use more of the available memory on that server. The same will be true with JEE agents, but in this case, the memory parameters will be part of the configuration of the server itself. Refer to your WebLogic Server documentation for more details. When we are using JDBC to connect to the source and target servers, we can influence how much memory will be used to transfer data from source to target. If you edit any of the data servers in the Topology navigator, you will see two parameters at the bottom of the window: Array Fetch Size and Batch Update Size: The JDBC protocol is defined so that records are processed in limited batches to make sure that memory is not exhausted immediately with very large queries. By default, these two parameters have a value of 30 when ODI ships, which means that JDBC will process the records 30 at a time. By increasing the value of these parameters, you can improve performance by retrieving more records each time. Just keep in time that by doing so, you are using more of the agent's memory. When changing the values of the Array Fetch Size and Batch Update Size parameters, it is recommended to have the same value for the Array Fetch Size on the source data server and the Batch Update Size on the target data server. Different values can result in buffering at the agent level, which can be counter-productive in terms of performance. There's more... Increasing the agent's memory parameters will only work as long as there is enough memory available on the server hosting the agent. Before looking into increasing the value of these parameters, we should always try to use less memory. Techniques that do not leverage the memory space of the agent usually have better performance, if only because the agent does not have to handle the data anymore and simply behaves as an orchestrator: Instead of using JDBC, try and use database loading utilities: external tables, sqlldr, and data pump are some examples available on Oracle databases. Similar utilities are available on other databases: ODI ships out of the box with KMs that support most of these utilities. The in-memory engine has one large drawback: it runs in-memory, and as such is more limited than actual databases. Let's be clear: it will perform well only as long as there is enough physical memory available. After that, we are talking about degrading performance, as memory blocks are swapped to disk to leverage virtual memory. You are usually better off using an actual database, and databases today do cache data in memory when it makes sense to do so. If you are handling very large XML files that cannot fit easily in memory, use the driver's property db_props and point to a .properties file that contains all the necessary parameters to connect to an external database. The benefit of this approach is that it allows you to process a lot more files in parallel (files processed in parallel all share the same agent memory space), and also much bigger files. You can look back to Chapter 9, XML and Web Services, where this topic is discussed in details. Loading a file containing a LOB Loading large objects always requires special considerations. Here, we will create a multi-technology IKM (that is, an IKM that connects to a remote source data server) that loads CLOBs and BLOBs using an external table definition. Getting ready For this recipe, we will need to create three files on disk: CLOB.TXT: use notepad and write This is a CLOB in this file. Save it on disk in your c:temp directory. BLOB.DOC: use a word processor program and create this file. Write this is a BLOB in the file and save it in your c:temp directory. Use notepad and create the file DATA.TXT with the following record: "Sample record with CLOB and BLOB", "CLOB.TXT", "BLOB.DOC" Save this file in your c:temp directory. Create a table to load the LOBs in your database: Create table LOB_SAMPLE(Description VARCHAR2(100),CLOB_Data CLOB,BLOB_Data BLOB); You will have to reverse engineer the file DATA.TXT in a file model. Define the file with no header, use the comma as the field separator, and use the following names for the three columns: Description, Clob_File, and Blob_File. Use the double quote character (") for the text delimiter. You will have to reverse engineer the LOB_SAMPLE table. You will need a project where the KM IKM SQL Control Append has been imported. How to do it... Make a copy of IKM SQL Control Append and rename the new KM IKM File SQL Append (LOB). Expand the KM in the tree and remove all the options except for INSERT, COMMIT, and DELETE_TEMORARY_OBJECTS. Edit the IKM. In the Definition tab, select the option Multi-Connections. Then, set the source Technology to File and the target Technology to Oracle. We will simplify the IKM, so delete all the steps except for the following ones: Drop flow table Create flow table I$ Insert new rows Commit Transaction Drop flow table Add a new step, name this step Create Oracle Directory, and copy this code: create or replace directory dat_dir AS '<%=snpRef.getSrcTablesList("", "[SCHEMA]", "", "")%>' Move this step up to make it the second step in the list after the first Drop flow table. Edit the two steps named Drop flow table. At the very bottom of the steps details, expand the Option entry and select Always Execute: in the original IKM, these steps were conditioned by the FLOW_CONTROL option, which we have removed. Edit the step Create Flow table I$. At the very bottom of the step details, expand the Option entry and select Always Execute. Then replace the original code with this: create table <%=odiRef.getTable("L", "INT_NAME", "A")%>(<%=snpRef.getColList("", "[COL_NAME]t[DEST_WRI_DT]",",nt", "","")%>)ORGANIZATION EXTERNAL(TYPE ORACLE_LOADERDEFAULT DIRECTORY dat_dirACCESS PARAMETERS(RECORDS DELIMITED BY NEWLINECHARACTERSET "WE8ISO8859P1 "BADFILE '<%=snpRef.getSrcTablesList("","[RES_NAME]", "", "")%>.bad'LOGFILE '<%=snpRef.getSrcTablesList("","[RES_NAME]", "", "")%>.log'DISCARDFILE '<%=snpRef.getSrcTablesList("","[RES_NAME]", "", "")%>.dsc'SKIP <%=snpRef.getSrcTablesList("","[FILE_FIRST_ROW]", "", "")%>FIELDS TERMINATED BY '<%=snpRef.getSrcTablesList("","[SFILE_SEP_FIELD]", "", "")%>'<% if(snpRef.getSrcTablesList("", "[FILE_ENC_FIELD]","", "").equals("")){%><%} else {%>OPTIONALLY ENCLOSED BY'<%=snpRef.getSrcTablesList("", "[FILE_ENC_FIELD]", "","")%>' AND '<%=snpRef.getSrcTablesList("","[FILE_ENC_FIELD]", "", "")%>' <%}%>MISSING FIELD VALUES ARE NULL(<%=snpRef.getColList("", " " +"<? if(u0022[DEST_WRI_DT]u0022.equals(u0022CLOBu0022)){?> [EXPRESSION] <?}else if(u0022[DEST_WRI_DT]u0022.equals(u0022BLOBu0022)){?> [EXPRESSION] <?}else{?>[COL_NAME] <?}?>"+»CHAR([LONGC])», «,nttt», «»,»»)%>)COLUMN TRANSFORMS (<%=odiRef.getColList(«», «[COL_NAME]from LOBFILE ([EXPRESSION]) from (dat_dir)CLOB»,»,/n»,»»,»UD1»)%><%=odiRef.getColList(«,», «[COL_NAME] from LOBFILE([EXPRESSION]) from (dat_dir) BLOB»,»,/n»,»»,»UD2»)%>))LOCATION (<%=snpRef.getSrcTablesList(«»,«'[RES_NAME]'», «», «»)%>))PARALLEL 2REJECT LIMIT UNLIMITED Simplify the code of the step Insert new rows to only keep the following: insert into <%=odiRef.getTable("L","TARG_NAME","A")%>(<%=odiRef.getColList("", "[COL_NAME]", ",nt", "", "((INS and!TRG) and REW)")%><%=odiRef.getColList(",", "[COL_NAME]", ",nt", "", "((INSand TRG) and REW)")%>)select <%=odiRef.getColList("", "[COL_NAME]", ",nt", "", "((INSand !TRG) and REW)")%><%=odiRef.getColList(",", "[EXPRESSION]", ",nt", "", "((INSand TRG) and REW)")%>From <%=odiRef.getTable("L","INT_NAME","A")%> Now save the IKM and create a new interface called Load LOBs where you are using the file DATA.TXT as a source and the table LOB_SAMPLE as a target. In the Overview tab, select Staging Area Different From Target and select the File logical schema that points to the DATA.TXT file in the schema drop down (no worries, we will not actually create anything in the staging area). Then, map the columns as follows: Source columns Target Columns Mappings Description Description DAT.Description Clob_File CLOB_Data Clob_File Blob_File BLOB_Data Blob_File We have removed the alias name from the mapping of both LOBs using Clob_File and Blob_File instead of DAT.Clob_File and DAT.Blob_File, otherwise this would generate invalid code for the external table definition used in the KM. In the QuickEdit tab, select the option UD1 for the CLOB column, and the option UD2 for the BLOB column. In the Flow tab, click on the target data server to select the IKM File SQL Append (LOB). Save and run the interface. You can check for successful execution in the operator navigator. If you run a select statement against the table LOB_SAMPLE, you can confirm that you have just loaded a CLOB and a BLOB. How it works... In this recipe, we are taking advantage of multiple elements that are at our disposal in the redaction of KMs. Multi-technology IKMs can only be used when the staging area is not on the target server, hence their name, since they can connect to a different technology to access the staging area. By forcing the staging area on the source data server, we eliminate the need for an LKM; source and staging are now on the same server. As a result, by using this multi-technology IKM, we bypass the creation of both C$ and I$ staging tables. First, since we do not have an LKM, there is no C$ table. Second, since we use an external table to map the file into the database, the data will go straight from the file into the target table, removing the need for an I$ table. One thing to remember with such IKMs though: you must use the source technology as your staging area as we did in step 9 of this recipe. This can be quite counter-intuitive when you are using a flat file as your source, but since we are not creating any staging table, we are safe doing so. Next, because of the specific nature of CLOBs and BLOBs, we need to use the name of the columns of both the target table and source table to generate the proper code that will create the external table (this is the reason why we are removing the alias name from the mappings in step 9). This allows us to leverage the tags [COL_NAME] and [EXPRESSION] to retrieve the target and source column names respectively, as we did in step 8: COLUMN TRANSFORMS (<%=odiRef.getColList(", "[COL_NAME] from LOBFILE([EXPRESSION]) from (dat_dir) CLOB",",/n","","UD1")%> Finally, we take advantage of the flags available in the QuickEdit view to explicitly mark the CLOBs and BLOBs columns as UD1 and UD2, so that the proper code from the KM can be applied to these columns specifically. There's more... We have over-simplified the KM to focus on the load of the LOBs, but the techniques used here can be expanded upon. By relying on other techniques as described in this book, you can avoid the manipulation of the aliases in the mappings, for instance. This could have been done by using the .replace() function available in Java. Likewise, listing the CLOBs and BLOBs in the COLUMN TRANSFORMS section of the external table definition could have been handled with a for loop. Several KMs have been written by the ODI community. A Google search on ODI and LOB returns too many results for us to list them all here, but they are worth a look if you are interested in this subject. A good starting point is http://java.net/projects/oracledi, where KMs are shared by the ODI developers community.
Read more
  • 0
  • 0
  • 4560
article-image-responsive-design-media-queries
Packt
19 Jun 2013
6 min read
Save for later

Responsive Design with Media Queries

Packt
19 Jun 2013
6 min read
(For more resources related to this topic, see here.) Web design for a multimedia web world As noted in the introduction to this article, recent times have seen an explosion in the variety of media through which people interact with websites, particularly the way smart phones and tablets are defining the browsing experience more and more. Moreover, as noted, a web page design that is appropriate may be necessary for a wide-screen experience but is often inappropriate, overly cluttered, or just plain dysfunctional on a tiny screen. The solution is Media Queries—a new element of CSS stylesheets introduced with CSS3. But before we examine new media features in CSS3, it will be helpful to understand the basic evolutionary path that led to the development of CSS3 Media Queries. That background will be useful both in getting our heads around the concepts involved and because in the crazy Wild West state of browsing environments these days (with emerging and yet-unresolved standards conflicts), designing for the widest range of media requires combining new CSS3 Media Queries with older CSS Media detection tools. We'll see how this plays out in real life near the end of this article, when we examine particular challenges of creating Media Queries that can detect, for example, an Apple iPhone. How Media Queries work Let's look at an example. If you open the Boston Globe (newspaper) site (http://www.bostonglobe.com/) in a browser window the width of a laptop, you'll see a three-column page layout (go ahead, I'll wait while you check; or just take a look at the following example). The three-column layout works well in laptops. But in a smaller viewport, the design adjusts to present content in two columns, as shown in the following screenshot: The two-column layout is the same HTML page as the three-column layout. And the content of both pages (text, images, media, and so on) is the same. The crew at the Globe do not have to build a separate home page for tablets or smartphones. But a media query has linked a different CSS file that displays in narrower viewports. A short history of Media Queries Stepping back in time a bit, the current (pre-CSS3) version of CSS could already detect media, and enable different stylesheets depending on the media. Moreover, Dreamweaver CS6 (also CS5.5, CS5, and previous versions) provided very nice, intuitive support for these features. The way this works in Dreamweaver is that when you click the Attach Style Sheet icon at the bottom of the CSS Styles panel (with a web page open in Dreamweaver's Document window), the Attach External Style Sheet dialog appears. The Media popup in the dialog allows you to attach a stylesheet specifically designed for print, aural (to be read out loud by the reader software), Braille, handheld devices, and other "traditional" output options, as well as newer CSS3-based options. The handheld option, shown in the following screenshot, was available before CSS3: So, to summarize the evolutionary path, detecting media and providing a custom style for that media is not new to HTML5 and its companion CSS3, and there is support for those features in Dreamweaver CS6. Detecting and synchronizing styles with defined media has been available in Dreamweaver. However, what is relatively new is the ability to detect and supply defined stylesheets for specific screen sizes. And that new feature opens the door to new levels of customized page design for specific media. HTML5, CSS3, and Media Queries With HTML5 and CSS3, Media Queries have been expanded. We can now define all kinds of criteria for selecting a stylesheet to apply to a viewing environment, including orientation (whether or not a mobile phone, tablet, and so on, is held in the portrait [up-down] or landscape [sideways] view), whether the device displays color, the shape of the viewing area, and—of most value—the width and height of the viewing area. All these options present a multitude of possibilities for creating custom stylesheets for different viewing environments. In fact they open up a ridiculously large array of possibilities. But for most designers, simply creating three appropriate stylesheets, one for laptop/desktop viewing, one for mobile phones, and one for tablets, is sufficient. In order to define criteria for which stylesheet will display in an environment, HTML5 and CSS3 allow us to use if-then statements. So, for example, if we are assigning a stylesheet to tablets, we might specify that if the width of the viewing area is greater than that of a cell phone, but smaller than that of a laptop screen, we want the tablet stylesheet to be applied. Styling for mobile devices and tablets While a full exploration of the aesthetic dimensions of creating styles for different media is beyond the scope of our mission in this book, it is worth noting a few basic "dos and don'ts" vis-à-vis styling for mobile devices. I'll be back with more detailed advice on mobile styling later in this article, but in a word, the challenge is: simplify. In general, this means applying many or all of the following adjustments to your pages: Smaller margins Larger (more readable) type Much less complex backgrounds; no image backgrounds No sidebars or floated content (content around which other content wraps) Often, no containers that define page width Design advice online: If you search for "css for mobile devices" online, you'll find thousands of articles with different perspectives and advice on designing web pages that can be easily accessed with handheld devices. Media Queries versus jQuery Mobile and apps Before moving to the technical dimension of building pages with responsive design using Media Queries, let me briefly compare and contrast media queries to the two other options available for displaying content differently for fullscreen and mobile devices. One option is an app. Apps (short for applications) are full-blown computer programs created in a high-level programming language. Dreamweaver CS6 includes new tools to connect with and generate apps through the online PhoneGap resources. The second option is a jQuery Mobile site. jQuery Mobile sites are based on JavaScript. But, as we'll see later in this book, you don't need to know JavaScript to build jQuery Mobile sites. The main difference between jQuery Mobile sites and Media Query sites with mobile-friendly designs is that jQuery Mobile sites require different content while Media Query sites simply repackage the same content with different stylesheets. Which approach should you use, Media Queries or JavaScript? That is a judgment call. What I can advise here is that Media Queries provides the easiest way to create and maintain a mobile version of your site.
Read more
  • 0
  • 0
  • 1845

article-image-choosing-your-shipping-method
Packt
19 Jun 2013
9 min read
Save for later

Choosing your shipping method

Packt
19 Jun 2013
9 min read
(For more resources related to this topic, see here.) Getting ready To view and edit our shipping methods we must first navigate to System | Configuration | Shipping Methods. Remember, our Current Configuration Scope field is important as shipping methods can be set on a per website scope basis. There are many shipping methods available by default, but the main generic methods are Flat Rate, Table Rates, and Free Shipping. By default, Magento comes with the Flat Rate method enabled. We are going to start off by disabling this shipping method. Be careful when disabling shipping methods; if we leave our Magento installation without any active shipping methods then no orders can be placed—the customer would be presented with this error in the checkout: Sorry, no quotes are available for this order at this time. Likewise through the administration panel manual orders will also receive the error. How to do it... To disable our Flat Rate method we need to navigate to its configuration options in System | Configuration | Shipping Methods | Flat Rate and choose Enabled as No, and click on Save. The following screenshot highlights our current configuration scope and disabled Flat Rate method: Next we need to configure our Table Rates method, so we need to now click on the Table Rates tab and set Enabled to Yes , within Title enter National Delivery and within Method Name enter Shipping. Finally, for the Condition option select Weight vs. Destination (all the other information can be left as default as it will not affect our pricing for this scenario). To upload our spreadsheet for our new Table Rates method we need to first change our scope (shipping rates imported via a .csv file are always entered at a website view level). To do this we need to select Main Website (this wording can differ depending on System | Manage Stores Settings) from our Current Configuration Scope field. The following screenshot shows the change in input fields when our configuration scope has changed: Click on the Export CSV button and we should start downloading a blank .csv file (or if there are rates already, it will give us our active rates). Next we will populate our spreadsheet with the following information (shown in the screenshot) so that we can ship to anywhere in the USA: After finishing our spreadsheet we can now import it, so (with our Current Configuration Scope field set to our Website view) click on the Choose File/Browse button and upload it. Once the browser has uploaded the file we can click on Save. Next we are going to configure our Free Shipping method to run alongside our Table Rates method, so to start with we need to switch back to our Default Config scope and then click on the Free Shipping tab Within this tab we will set Enabled to Yes and Minimum Order Amount to 50. We can leave the other options as default. How it works... The following is a brief explanation of each of our main shipping methods. Flat Rate The Flat Rate method allows us to specify a fixed shipping charge to be applied either per item or per order. The Flat Rate method also allows us to specify a handling fee—a percentage or fixed amount surcharge of the flat rate fee. With this method we can also specify which countries we wish to make this shipping method applicable for (dependent solely on the customers' shipping address details). Unlike the Table Rates method, you cannot specify multiple flat rates for any given region of a country nor can you specify flat rates individually per country. Table Rates The Table Rates method uses a spreadsheet of data to increase the flexibility of our shipping charges by allowing us to apply different prices to our orders depending on the criteria we specify in the spreadsheet. Along with the liberty to specify which countries this method is applicable for and giving us the option to apply a handling fee, the Table Rates method also allows us to choose from a variety of shopping cart conditions. The choice that we select from these conditions affects the data that we can import via the spreadsheet. Inside this spreadsheet we can specify hundreds of rows of countries along with their specific states or Zip/Postal Codes. Each row has a condition such as weight (and above) and also a specific price. If a shopping cart matches the criteria entered on any of the rows, the shipping price will be taken from that row and set to the cart. In our example we have used Weight vs. Destination; there are two other alternative conditions which come with a default Magento installation that could be used to calculate the shipping: Price vs. Destination: This Table Rates condition takes into account the Order Subtotal (and above) amount in whichever currency is currently set for the store # of Items vs. Destination: This Table Rates condition calculates the shipping cost based on the # of Items (and above) within the customer's basket Free Shipping The Free Shipping method is one of the simplest and most commonly used of all the methods that come with a default Magento installation. One of the best ways to increase the conversion rate through your Magento store is to offer your customers Free Shipping. Magento allows you to do this by using its Free Shipping method. Selecting the countries that this method is applicable for and inputting a minimum order amount as the criteria will enable this method in the checkout for any matching shopping cart. Unfortunately, you cannot specify regions of a country within this method (although you can still offer a free shipping solution through table rates and promotional rules). Our configuration As mentioned previously, the Table Rates method provides us with three types of conditions. In our example we created a table rate spreadsheet that relies on the weight information of our products to work out the shipping price. Magento's default Free Shipping method is one of the most popular and useful shipping methods and its most important configuration option is Minimum Order Amount. Setting this value to 50 will tell Magento that any shopping cart with a subtotal greater than $50 should provide the Free Shipping method for the customer to use; we can see this demonstrated in the following screenshot: The enabled option is a standard feature among nearly all shipping method extensions. Whenever we wish to enable or disable a shipping method, all we need to do is set it to Yes for enabled and No to disable it. Once we have configured our Table Rates extension, Magento will use the values inputted by our customer and try to match them against our imported data. In our case if a customer has ordered a product weighing 2.5 kg and they live anywhere in the USA, they will be presented with our $6.99 price. However, a drawback of our example is if they live outside of the USA, our shipping method will not be available. The .csv file for our Weight vs. Destination spreadsheet is slightly different to the spreadsheet used for the other Table Rates conditions. It is therefore important to make sure that if we change our condition, we export a fresh spreadsheet with the correct column information. One very important point to note when editing our shipping spreadsheets is the format of the file—programs such as Microsoft Excel sometimes save in an incompatible format. It is recommended to use the free, downloadable Open Office suite to edit any of Magento's spreadsheets as they save the file in a compatible format. We can download Open Office here: www.openoffice.org If there is no alternative but to use Microsoft Excel then we must ensure we save as CSV for Windows or alternatively CSV (Comma Delimited). A few key points when editing the Table Rates spreadsheet: The * (Asterisk) is a wildcard—similar to saying ANY Weight (and above) is really a FROM weight and will set the price UNTIL the next row value that is higher than itself (for the matching Country, Region/State, and Zip/ Postal Code)—the downside of this is that you cannot set a maximum weight limit The Country column takes three-letter codes—ISO 3166-1 alpha-3 codes The Zip/Postal Code column takes either a full USA ZIP code or a full postal code The Region/State column takes all two-letter state codes from the USA or any other codes that are available in the drop-down select menus for regions on the checkout pages of Magento One final note is that we can run as many shipping methods as we like at the same time—just like we did with our Free Shipping method and our Table Rates method. There's more... For more information on setting up the many shipping methods that are available within Magento please see the following link: http://innoexts.com/magento-shipping-methods We can also enable and disable shipping methods on a per website view basis, so for example we could disable a shipping method for our French store. Disabling Free Shipping for French website If we wanted to disable our Free Shipping method for just our French store, we could change our Current Configuration Scope field to our French website view and then perform the following steps: Navigate to System | Configuration | Shipping Methods and click on the Free Shipping tab. Uncheck Use Default next to the Enabled option and set Enabled to No, and then click on Save Config. We can see that Magento normally defaults all of our settings to the Default Config scope; by unchecking the Use Default checkbox we can edit our method for our chosen store view. Summary This article explored the differences between the Flat Rate, Table Rates, and Free Shipping methods, as well as taught us how to disable a shipping method and configure your Table Rates. Resources for Article : Further resources on this subject: Magento Performance Optimization [Article] Magento: Exploring Themes [Article] Getting Started with Magento Development [Article]
Read more
  • 0
  • 0
  • 5787

article-image-using-jquery-and-jqueryui-widget-factory-plugins-requirejs
Packt
18 Jun 2013
5 min read
Save for later

Using jQuery and jQueryUI Widget Factory plugins with RequireJS

Packt
18 Jun 2013
5 min read
(For more resources related to this topic, see here.) How to do it... We must declare the jquery alias name within our Require.js configuration file. require.config({// 3rd party script alias namespaths: {// Core Libraries// --------------// jQuery"jquery": "libs/jquery",// Plugins// -------"somePlugin": "libs/plugins/somePlugin"}}); If a jQuery plugin does not register itself as AMD compatible, we must also create a Require.js shim configuration to make sure Require.js loads jQuery before the jQuery plugin. shim: {// Twitter Bootstrap plugins depend on jQuery"bootstrap": ["jquery"]} We will now be able to dynamically load a jQuery plugin with the require() method. // Dynamically loads a jQuery plugin using the require() methodrequire(["somePlugin"], function() {// The callback function is executed after the pluginis loaded}); We will also be able to list a jQuery plugin as a dependency to another module. // Sample file// -----------// The define method is passed a dependency array and a callbackfunctiondefine(["jquery", "somePlugin"], function ($) {// Wraps all logic inside of a jQuery.ready event$(function() {});}); When using a jQueryUI Widget Factory plugin, we create Require.js path names for both the jQueryUI Widget Factory and the jQueryUI Widget Factory plugin: "jqueryui": "libs/jqueryui","selectBoxIt": "libs/plugins/selectBoxIt" Next, create a shim configuration property: // The jQueryUI Widget Factory depends on jQuery"jqueryui": ["jquery"],// The selectBoxIt plugin depends on both jQuery and the jQueryUIWidget Factory"selectBoxIt": ["jqueryui"] We will now be able to dynamically load the jQueryUI Widget Factory plugin with the require() method: // Dynamically loads the jQueryUI Widget Factory plugin, selectBoxIt,using the Require() methodrequire(["selectBoxIt"], function() {// The callback function is executed after selectBoxIt.js(and all of its dependencies) have been loaded}); We will also be able to list the jQueryUI Widget Factory plugin as a dependency to another module: // Sample file// -----------// The define method is passed a dependency array and a callbackfunctiondefine(["jquery", "selectBoxIt"], function ($) {// Wraps all logic inside of a jQuery.ready event$(function() {});}); How it works... Luckily for us, jQuery adheres to the AMD specification and registers itself as a named AMD module. If you are confused about how/why they are doing that, let's take a look at the jQuery source: // Expose jQuery as an AMD moduleif ( typeof define === "function" && define.amd && define.amd.jQuery ){define( "jquery", [], function () { return jQuery; } );} jQuery first checks to make sure there is a global define() function available on the page. Next, jQuery checks if the define function has an amd property, which all AMD loaders that adhere to the AMD API should have. Remember that in JavaScript, functions are first class objects, and can contain properties. Finally, jQuery checks to see if the amd property contains a jQuery property, which should only be there for AMD loaders that understand the issues with loading multiple versions of jQuery in a page that all might call the define() function. Essentially, jQuery is checking that an AMD script loader is on the page, and then registering itself as a named AMD module (jquery). Since jQuery exports itself as the named AMD module, jquery, you must use this exact name when setting the path configuration to your own version of jQuery, or Require.js will throw an error. If a jQuery plugin registers itself as an anonymous AMD module and jQuery is also listed with the proper lowercased jquery alias name within your Require.js configuration file, using the plugin with the require() and define() methods will work as you expect. Unfortunately, most jQuery plugins are not AMD compatible, and do not wrap themselves in an optional define() method and list jquery as a dependency. To get around this issue, we can use the Require.js shim object configuration like we have seen before to tell Require. js that a file depends on jQuery. The shim configuration is a great solution for jQuery plugins that do not register themselves as AMD modules. Unfortunately, unlike jQuery, the jQueryUI does not currently register itself as a named AMD module, which means that plugin authors that use the jQueryUI Widget Factory cannot provide AMD compatibility. Since the jQueryUI Widget Factory is not AMD compatible, we must use a workaround involving the paths and shim configuration objects to properly define the plugin as an AMD module. There's more... You will most likely always register your own files as anonymous AMD modules, but jQuery is a special case. Registering itself as a named AMD module allows other third-party libraries that depend on jQuery, such as jQuery plugins, to become AMD compatible by calling the define() method themselves and using the community agreed upon module name, jquery, to list jQuery as a dependency. Summary This article demonstrates how to use jQuery and jQueryUI Widget Factory plugins with Require.js. Resources for Article : Further resources on this subject: So, what is KineticJS? [Article] HTML5 Presentations - creating our initial presentation [Article] Tips & Tricks for Ext JS 3.x [Article]
Read more
  • 0
  • 0
  • 2982
article-image-fundamental-razor-syntaxes
Packt
18 Jun 2013
2 min read
Save for later

Fundamental Razor syntaxes

Packt
18 Jun 2013
2 min read
(For more resources related to this topic, see here.) Getting ready In this view page you can try all the Razor syntaxes given in this section. How to do it... Here, let's start learning the fundamene written using three different approaches: inline, code block, and mixed. Inline code expressions Inline code expressions are always written in a single line, as follows: I always enjoy @DateTime.Now.DayOfWeek with my family. At runtime, the inline code expression, which is @DateTime.Now.DayOfWeek, will be converted into a day, such as Sunday. This can be seen in the following screenshot: Let's look at one more example, which will pass the controller's ViewBag and ViewData messages on the view. The rendered output will be as follows: Code block expression Code block expression is actually a set of multiple code lines that start and end with @{}. The use of opening (@{) and closing (}) characters is mandatory, even for single line of C# or VB code; as shown in the following screenshot: This will render the following output: Mixed code expression Mixed code expression is a set of multiple inline code expressions in a code block where we switch between C# and HTML. The magical key here is @:, which allows writing HTML in a code block, as follows: This will render the following output: So, this is all about how we write the code on Razor view page. Summary This article thus you learned about inline code expressions, code block expressions, and mixed code expressions. Resources for Article : Further resources on this subject: Deploying HTML5 Applications with GNOME [Article] Making the World Wide Web an Easier Place to Talk About [Article] The Best Way to Create Round Cornered Boxes with CSS [Article]
Read more
  • 0
  • 0
  • 9505

Packt
18 Jun 2013
21 min read
Save for later

Atmosfall – Managing Game Progress with Coroutines

Packt
18 Jun 2013
21 min read
(For more resources related to this topic, see here.) What do we build? We'll explore this idea of managing progress through a game schedule by completing a scrolling shooter in the tradition of games such as Xevious or River Run, where the player maneuvers a ship around the screen, avoiding enemy fire and destroying enemy ships until he/she reaches a boss target. Enemies will include ships that fly various patterns across the screen, turrets that follow the scrolling background and turn to face the player's ship, and a boss at the end that takes many hits to destroy and moves in various directions, firing multiple weapons. The player's ship will be able to fire at points on the screen that the player touches. What does it do? The player maneuvers their ship around the screen as the ground scrolls under them from the top of the screen downward. As the scrolling background reaches certain points, enemies appear and fly or scroll around the screen, firing bullets as they go; the player must avoid the bullets as well as the ship itself while shooting back. Like the Deep Black game, this project will be based on Corona's Box2D-based physics library. In this version, most objects will be "sensors", meaning they only detect collisions, and do not bounce off of each other or transfer momentum. We'll also use Box2D's collision filters so that we don't need to process enemy ships colliding with each other, or bullets hitting each other. In the TranslationBuddy project, we got a taste of coroutines and how they can be used to bookmark a task that you're in the middle of and come back to it later. In this project, we'll take that concept much further, using coroutines to create scripted behavior that's carried out over time, which is the simplest part of what game players and developers usually refer to as AI. We'll create computer-controlled fighters that follow flight assignments which are predictable at run time, but easily customized by the designer. Why is it great? In addition to coroutines, we'll also use a Lua feature called environments to create a minimal language of very simple functions controlling intervals and enemy actions. Features like this can become useful in larger projects, where programmers and designers must collaborate on a project. In such projects, programmers are responsible for the code that carries out the actions which the enemies will take, but the decisions of what the enemies should do, when, and in what order, are made by game designers, and frequently have to be adjusted for best balance and fun. For this reason, it's good to let the designers edit these schedules and scripts themselves. Designers are usually not experienced programmers, although they often have a little knowledge of programming and scripting, so this simple language will make it much easier for the designers. How are we going to do it? For this project, we'll review the design, rather than creating it. While an indie programmer will often be designing their own games and then coding from their designs, any programmer at a studio is likely to be coding from a document given to them by a designer or design team. We will be managing game progress with the help of the following game coroutines: Founding the framework Moving the player Scheduling enemies Scripting behavior Controlling the boss Cleaning up and making the game playable What do I need to get started? First, open the file design.txt from the project pack and read through it, noting the [NYI] tags that indicate features still pending. In full development projects, this sort of status tracking will usually be carried out by a more complex database or dedicated tracking program, but even in small projects, a simple record of what has yet to be accomplished can be very useful. At this point, the game has files describing various ships (a broad category which also includes the ground-based, immobile turrets) as well as the various weapons with which the ships are equipped, the bullets they fire, and the explosions when they land. It also includes code that handles things taking damage—including events that can be tracked by user interface elements or gameplay progress tracking, processing user input into commands, and a long background made up of large tiles. A splash screen is already completed and appears when the game is launched. What the game still needs is actual level design. Enemies need to appear as time advances in the level, and carry out various plans of attack against the player. This means that there are two kinds of schedules required; the schedule of which enemies appear when, and the individual schedules of the enemies that dictate how each one flies and attacks after it is created. To make this happen, we'll not only create scheduling behaviors, but also modules that attach these behaviors and other suitable characteristics, such as orientation, to our predefined ships to make them into bosses and enemies. This distinction between units in a game that have statistics and behaviors, and the actors that represent them in the game world, is very powerful for scaling projects up in complexity. Once that's done, create a new project folder, Atmosfall, and copy the contents of the version 0 folder in the project pack directory. You should be able to load this project into the simulator and advance the game past the splash screen, seeing a ship in the middle of a swamp background. In the simulator, you can click anywhere on the game screen and watch the ship fire double bullets at the point at which you click. You can also build the game for a device and watch the ship slide around the screen as you tilt the device. The prototype responds to user input and is ready to start adding the scheduling and AI enemy control. Tracking progress through the level Games like this typically trigger enemy appearances and events based on how far the background has scrolled past the screen. To focus on the challenges of the project, we'll import the background itself and just add the scrolling logic to issue events that track this progress. Getting ready You should have already copied the partly completed project from the version 0 folder into your new project directory; if you haven't, do that now. Getting on with it We'll start by loading the new marsh background into the Ground layer of the game's view, instead of the blank rectangle that the project uses by default. Open the game.lua file and change the createScene function to load this module as the new background, as shown in the following code snippet: local group = self.viewself.Ground = require "level.marsh"(group)self.Mobs = display.newGroup() Then we adjust the scale of the background to make it fit into the width of the screen: self.Ground = require "level.marsh"(group)local scale = display.contentWidth / self.Ground.widthself.Ground.xScale, self.Ground.yScale = scale, scaleself.Mobs = display.newGroup() If you test the code at this point, you should see a static background filling the screen behind the player ship. Sliding the background Pinning a moving rectangle to another moving rectangle so that it doesn't slide too far and show the back side of the world requires a little bit of math, but we can offload this math onto Corona with a little ingenuity. First, when the scene starts, we'll make sure that in the willEnterScene responder, the background is lined up, with its bottom edge along the bottom edge of the screen. physics.setGravity(0, 0)self.Ground:setReferencePoint(display.BottomCenterReferencePoint)self.Ground.xOrigin, self.Ground.y = display.contentCenterX,display.contentHeightfor _, coordinates in ipairs(walls) do Before we can start the background moving, we need to know how long it's supposed to take. This could vary from level to level in a real game, so we will let the schedule we load provide this value (the file in question doesn't exist yet; creating it will be part of our next task, so make a note that it will need to return its total length) as shown in the following code: physics.setGravity(0, 0)self.Duration = require "level.marsh-enemies" (self)self.Ground:setReferencePoint(display.BottomCenterReferencePoint) Now that we know the desired length of the background scroll, we can start the transition in response to the enterScene event, once the scene has finished loading as shown in the following code snippet. This is where the magic happens and the explanation will follow once you've had a chance to scan it. self.Exit = nillocal bounds = self.Ground.contentBoundsself.Ground.Pan = transition.to(self.Ground,{time = self.Duration * 1000;y = 0, yReference = bounds.yMin - bounds.yMax;onComplete = function(object)object.enterFrame, object.Pan = nil, nilend})self.Lives = 2 The key here is that transition.to (and transition.from) can tween any value on any object that can be indexed, even though it usually gets applied to the visible properties of display objects. The yReference value has no visible effect on an object by itself; changing an object's yReference value changes what value its y position ends up being in its parent coordinates, but doesn't cause anything to move on the screen. However, when you combine this with continually adjusting the y position, it has the effect of changing the scale of the motion; setting the y position moves the object so that the point designated internally as its yReference value sits at the specified coordinate in its parent system. Image We also needed to know the distance to move the reference point in the group; the marsh module builds a group with its zero point, or origin, at its bottom. So eventually, we need to move the yReference value to a negative value equal to the functional height of the background. We figure this out from the height of its bounding rectangle. If you want to test this out, you'll have to temporarily replace self.Duration = require "marsh-enemies" (group) with self.Duration = 60. To temporarily sub out values like this, I often create an end-of-line comment, making the line look like the following: self.Duration = 60 -- require "level.marsh-enemies" (self) Then, to replace the old code, I can just delete the part that says 60 ––. Once you've inserted this place-holder, you can test the code and you'll see the background crawl past. However, if you're using a tall profile device, such as an iPhone 5, you may see that the bottom edge of the background first creeps down across the empty bar at the bottom of the screen, before filling it completely, and at the end, it will expose a bar of black at the top as it creeps into place. This is because when Corona uses letterbox alignment, it only positions the reference frame for drawing to leave space at the edges; it doesn't actually crop out anything that was hanging over the edges of the screen. But we can do this fairly easily by adding a mask to the display stage, the way we did on scenes in Project 3 to hide bits of overhanging text during transitions. To enforce this letterboxing globally throughout the program, we can add the mask in main.lua. require "input"display.currentStage:setMask(graphics.newMask("effect/masking-frame.png"))display.currentStage.maskX, display.currentStage.maskY = display.contentCenterX, display.contentCenterYlocal storyboard = require "storyboard" Tracking the background progress We need to track how far the schedule for the level will have advanced. We want this to be as fine-grained as we can manage, so we'll check the value as frequently as Corona will do, which is every frame. This means that an enterFrame listener is the logical vector which is shown in the following code snippet: Runtime:addEventListener('enterFrame', self.Ground)function self.Ground:enterFrame(event)endself.Lives = 2 For the schedule, we need to track time elapsed since the schedule starts. We'll make a note of the time when the scene begins, and post the Progress events that indicate what time the schedule has reached at, as shown in the following code: self.Ground.Start = system.getTimer()Runtime:addEventListener('enterFrame', self.Ground)function self.Ground:enterFrame(event) The other thing we want to track is the actual motion of the background. The first enemy we'll create is a turret, so if it doesn't appear to move in sync with the ground, the user will find that very distracting. This is a little trickier, since the ground's position and reference are both moving on the same sliding scale. Fortunately, there is another point that moves more predictably—the origin. The origin of an object is whichever point is considered (0, 0) for placing that object's reference point. For most objects, it's fixed at the object's center; for groups, it's (0, 0) in the group's coordinates, wherever the group's children happen to be placed in relation to it. In our case, what we're really concerned with is that the object's origin is fixed with respect to its visible contents in a way that the reference point isn't. So if the group moves down by three pixels on the screen, you can say confidently that its yOrigin value also increased by three (assuming that it's not parented to a group with a different yScale value). We can track the position of the ground's yOrigin value from frame to frame in order to determine how much it has visibly moved. We can include that information in the events we dispatch to supply the progress information as shown in the following code: self.Ground.Start = system.getTimer()self.Ground.oldY = self.Ground.yOriginRuntime:addEventListener('enterFrame', self.Ground)function self.Ground:enterFrame(event)scene:dispatchEvent{name = 'Progress'; time = (event.time - self.Start) / 1000; delta = self.yOrigin - self.oldY}self.oldY = self.yOriginend Our schedule module will feed off these events to update its progress and trigger actions. First, however, we need something for the schedule module to do; schedules for levels will consist of spawning enemies at different intervals. Constructing the enemy behavior For the schedule to spawn new enemies, we need one to exist. We'll start with a simple one, that just moves in sync with the ground to start with. Once that works, we'll add tracking and weapon fire.. Getting on with it Add a new file, turret.lua, and open it. This file will add turret behavior to a ship sprite and physics description specified in the starting project. Creating an enemy Add the basic description of the turret object's appearance and physics as shown in the following code: local ship = require "ship.ship"local category = require "category"local groundFilter = {groupIndex = category.enemy;}return function(game, x, y)local self = ship.turret(game, game.Mobs.Ground, groundFilter)self.x, self.y = x, yself.bodyType = 'static'return selfend This creates a turret that just sits perfectly still and does nothing, which isn't very interesting. The next thing we'll do is make it move in sync with the ground by following the Progress events that we added to the game in the last section as shown in the following code: self.bodyType = 'static'game:addEventListener('Progress', self)function self:Progress(event)if self.y thenself.y = self.y + event.deltaelsegame:removeEventListener('Progress', self)endendreturn self Since each Progress event contains the amount the screen moved, we can move the turret by the same amount. Now that this object has some basic behavior, it's time to start linking it into the game and at aching the scheduling mechanism. Save and switch to game.lua, and add a simple table before the event definitions using the following code: local scene = storyboard.newScene()scene.Spawn = {turret = require "turret";}function scene:createScene( event ) This means that we can call game.Spawn.turret(game, x, y) to create a new turret at x, y in the world for game. However, we're not going to call it directly. We'll create a schedule that contains functions to spawn enemies in a common, shared context (the game) to save the code having to contain the same values repeated an awful lot. Creating a schedule In order to have our turret appear at the right point, we'll create a schedule that spawns new enemies as the level progresses. Getting on with it Save game.lua for the moment and create a new file in the level folder called marsh-enemies.lua. This file will define a schedule module that's 60 seconds long, so that's the first thing we'll define using the following code: return function(game)local duration = 60return durationend At this point, if you previously put a placeholder duration in game.lua to test the background scroll, you can revert that to the code that uses this file. Next we'll use the schedule function (from a module that isn't created yet) to start our custom schedule function against the current game. This will take a few steps to make it fully clear, but showing you how it will be used in the following code should help you see why this setup is worth engineering: local duration = 60schedule(game,function()at (0.3) spawn.turret(50, -20)end)return duration Now here's the catch—the at and spawn.turret functions haven't been defined anywhere yet, and they won't be made local in this file or defined as globals, even though this function uses them as global. Our schedule function will create them in a custom environment. So for each schedule function, we'll create an environment that contains simplified actions on the game the schedule is for, and use it for the function that defines the schedule. We'll combine this with making the schedule function part of a coroutine, so that it can suspend itself, such as when it is waiting for a particular time to come up in the schedule. So, before moving on, load the module you're about to create into marsh-enemies.lua using the following code: local schedule = require "schedule"return function(game)local duration = 60 Building a schedule framework Save marsh-enemies.lua and create the file schedule.lua at the top of your project. This le won't actually be very long. The core is a function that starts each newly created coroutine, at aching the environment supplied to the schedule, running that schedule until it's complete, and finally disconnecting the schedule from the game so that it won't throw errors or take up processing time as shown in the following code: local function bind(game, listener, actions, schedule)setfenv(schedule, actions)schedule()game:removeEventListener('Progress', listener)end The rest of the module will be a function that does the work of setting it up. It'll create an environment that contains bridge actions to the main game, start a coroutine using our glue function, and start that coroutine with the schedule function and environment. This coroutine will wake up every time the game object receives a Progress event to see if there are any enemies it needs to spawn, create those required, and go back to waiting. We'll start by creating a blank environment and our coroutine: game:removeEventListener('Progress', listener)endreturn function(game, schedule)local actions = {}local self = coroutine.wrap(bind)end Unlike coroutine.create, coroutine.wrap returns a function that resumes the new coroutine each time it's called. It's usually a little more convenient, but be a little careful with coroutine.wrap because if any error is thrown inside the coroutine, it will bubble right up and affect the code calling the resume function. We'll then connect the new coroutine to be resumed for each Progress event sent to the game using the following code: local self = coroutine.wrap(bind)game:addEventListener('Progress', self)end We'll create a listener that stops feeding new Progress events into the schedule module when the game ends, such as when the player loses all of his or her lives as shown in the following code. This efiectively terminates the schedule; there's no way anymore to resume it and it will get garage-collected. game:addEventListener('Progress', self)local function close(event)if event.action == 'ended' thengame:removeEventListener('Progress', self)game:removeEventListener('Game', close)endendgame:addEventListener('Game', close)end Then, we'll start the coroutine with its schedule and environment, as well as the information it needs to clean itself up, and return the new coroutine in case the calling code has some use for it: game:addEventListener('Game', close)self(game, self, actions, schedule)return selfend Building the scheduled actions Of the two functions we've described, the at action is the simpler one. It checks the elapsed time in the schedule module; if it's not enough, it yields to keep waiting, but if its designated time has arrived, it returns from its loop and lets the schedule advance. This means the code is very straightforward as follows: local actions = {}function actions.at(time)repeatlocal progress = coroutine.yield()until progress.time >= timeendlocal self = coroutine.wrap(bind) Calling the function at may seem a little strange, but it allows the schedule calls to read much more like normal language. We could have called the function waitUntil and written the function waiting for it on another line, but Lua's loose syntax allows us to use this very compact format. The spawn functions are a little more complex. We could simply build them all like the following: function actions.spawn.turret(x, y)return game.Spawn.turret(game, x, y)end However, since each one would follow the same pattern, we can use another pattern based on metatables and the __index lookup, the self-populaing table. __index on a table's metatable is only used when the requested key isn't in the original table; this means that the function that creates the requested value can store it in the table, and next time, it will simply be retrieved from the table instead of being looked up in the __index table again. This makes the spawn action family easy to summarize in one block. actions.spawn = setmetatable({},{__index = function(t, name)local function cue(...)return game.Spawn[name](game, ...)endt[name] = cuereturn cueend})local self = coroutine.wrap(bind) That's actually all there is to the schedule system! You can test it out and watch the turret appear in the upper-left hand slide-down corner across the screen, mindlessly facing right and doing nothing. Bringing an enemy to life Making the turret work requires only two main steps. The first is to have it track the player. Since the game object keeps a reference to the Player object, this boils down to basic trigonometry. Open turret.lua and add a line to the Progress handler as shown in the following code: if self.y thenself.y = self.y + event.deltaself.rotation = math.deg(math.atan2(game.Player.y - self.y,game.Player.x - self.x))else The arctangent of the y and x distances gives the facing direction from the turret to the player, which we can use as the rotation angle to make the turret point at the player. If you try the code again you should see the turret stay pointed at the player as it scrolls down and the player moves. The last step is to give the turret a weapon. While we have the turret file open, have it start firing as soon as it is created: endself.Weapons.AntiAir:start(self, 100, 0)return self This depends on the turret having a weapon called AntiAir, which in this case was already created in the existing partial code. The turret is complete for the time being. If you test the code, it should shoot at the player, and bullets fired at it should vanish when they hit it. Currently, however, nothing gets destroyed no matter how many bullets hit it. What did we do? A lot of stuff happened in this section! Using coroutines to track a continuously advancing schedule of new enemies, using Lua environments to wrap up some complicated actions in some simple wrappers, and adding an advancing series of progression data to drive everything else. What else do I need to know? Environments are a powerful feature of Lua; each function has an associated environment which is just a table linked to that function. Whenever a function needs to use a global, it looks up the global name as a string key in that table. By changing a function's environment, we can give it access to a totally different set of global functions. The real power here will come from the fact that the environment that we link to our schedule will be stocked with functions that use the normal environment, and can therefore take actions whose particulars are hidden from the code using them. Lua creates a default environment to link to its code when it's started, containing all the standard global functions, and stores a link to that environment in that environment under the name _G, which you've probably used already.
Read more
  • 0
  • 0
  • 2223
Modal Close icon
Modal Close icon