Versions
The following versions of japicmp are available:
0.17.2 (2023-03-01)
- Less warnings in maven build. #358
- No warning in maven 3.9.1 due to unused parameter. #357
0.17.1 (2022-11-16)
- Added new compatibility check METHOD_MOVED_TO_SUPERCLASS. #350
- No IllegalStateException when method is removed from sub and super class. #349
0.17.0 (2022-11-16)
- Support for generic type information in class files. #337
- Check implemented interfaces for default methods of super interface. #343
- Detect methods moved to super class with added final. #344
0.16.0 (2022-08-30)
- Detect method varargs changes (METHOD_NOW_VARARGS, METHOD_NO_LONGER_VARARGS). #331
- Constructors are now also reported in case of changes of checked exceptions. #332
- Reduced the maximum memory requirement of the JarArchiveComparator. #333
Available at Maven Central.
0.15.7 (2022-02-27)
- Do not report new methods in super-interface of interface as METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE. #281
- Detect default implementation in newly added interface. #321
Available at Maven Central.
0.15.6 (2022-01-30)
- Correct resolution of artifact to compare for ejb packaging. #302
- Add option to include SNAPSHOTs in compared version. #301
- Build runs concurrently. #300
- Fix typo in sample configuration. #287
Available at Maven Central.
0.15.5 (2022-01-29)
- METHOD_NEW_DEFAULT is not reported in case default methods overrides other default method. #312
- Valid processing in case interface becomes class and vice versa. #309
- Refine CONSTRUCTOR_LESS_ACCESSIBLE evaluation. #310
- Packing type bundle is supported correctly. #280
- Upgrade to ant 1.10.11 due to security issues
- Upgrade to jsoup 1.14.2 due to security issues
Available at Maven Central.
0.15.4 (2021-10-06)
- ignoreNonResolvableArtifacts is honored for first version of artifact. #288
- Do not report METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE if it is implemented in superclass. #281
- EOptional.or() returns secondChoice instead of this. #297
Available at Maven Central.
0.15.3 (2021-03-06)
- Exceptions during the artifact resolution do no longer break the build if the artifact was found. #284
- Option ignoreNonResolvableArtifacts is evaluated correctly again. #283
- Workaround was added to support bundle packaging. #280
Available at Maven Central.
0.15.2 (2021-01-07)
- New annotations or new enums are no longer tracked as incompatible (regression) #279
- Updated guava version to 30.0-jre
Available at Maven Central.
0.15.1 (2020-12-30)
- No ArtifactNotFoundException when comparison list is build from maven project #278
Available at Maven Central.
0.15.0 (2020-12-30)
- The maven plugin runs with Java-16. This also made changes necessary to migrate to the maven-resolver-API. Hence; the japicmp-maven plugin only runs with maven >= 3.1.0 #275
- New change that is detected: METHOD_DEFAULT_ADDED_IN_IMPLEMENTED_INTERFACE. #267
- StdoutOutputGenerator does not print superclass if class itself has changed.
Available at Maven Central.
0.14.4 (2020-10-17)
- New compatibility change METHOD_NO_LONGER_THROWS_CHECKED_EXCEPTION. #263
- More detailed output message when skipping the build. #273
- StdoutOutputGenerator prints modified superclasses if outputOnlyModifications=false. #272
Available at Maven Central.
0.14.3 (2019-12-24)
- METHOD_REMOVED_IN_SUPERCLASS is no longer reported if method is moved from superclass to superclass of superclass. #253
- Added METHOD_ADDED_TO_PUBLIC_CLASS as a new compatibility check. #241
Available at Maven Central.
0.14.2 (2019-11-23)
- New properties: japicmp.skip, japicmp.skipXmlReport, japicmp.skipHtmlReport, japicmp.breakBuildOnModifications, japicmp.breakBuildOnBinaryIncompatibleModifications, japicmp.breakBuildOnSourceIncompatibleModifications, japicmp.breakBuildBasedOnSemanticVersioning, japicmp.breakBuildBasedOnSemanticVersioningForMajorVersionZero. #240
- Do not filer modified classes when outputOnlyModifications is set and all members are unchanged. #247
Available at Maven Central.
0.14.1 (2019-05-01)
- New option reportLinkName that allows to define a name for the report link in site reports when using report sets. #236
Available at Maven Central.
0.14.0 (2019-04-14)
- Invocations from different reportSets supported. #232
- Optional dependencies are included, but can be excluded using the new option ignoreMissingOptionalDependency. #230
- Update to javassist 3.24.0-GA (no longer support for Java 1.7).
- Fixed ErrorOnSemanticIncompatibilityForMajorVersionZero. #227
- Support ‘effective final’. #229
- Changed link to javassist site. #233
Available at Maven Central.
0.13.1 (2019-02-25)
- Do not report METHOD_REMOVED_IN_SUPERCLASS if method is pulled up. #222
Available at Maven Central.
0.13.0 (2018-08-20)
- Use javassist 3.23.1-GA.
- Added configuration option to . #213
- CLI support to error on incompatibilities. #216
- japicmp-maven-plugin writes now errors to the console if an incompatible change is detected. #215
- Use MavenParameters to obtain mavenProject in isPomModuleNeedingSkip(). #210
- CompatibilityChanges only detects ANNOTATION_DEPRECATED_ADDED if the annoation has change status NEW or MODIFIED. #213
- The two compatibility changes METHOD_NEW_DEFAULT and METHOD_ADDED_TO_INTERFACE are not set simultaneously. #201
- Added maven dependency on javax.activation. #177
Available at Maven Central.
0.12.0 (2018-05-02)
- If a change is evaluated as binary or source incompatible can be configured. #209
- Proper error message in case class could not loaded from old and new classpath if specified separately. #200
- Skip pom modules in site report. #207
- The parameters for exclusive filters are checked in CLI tool. #202
Available at Maven Central.
0.11.1 (2018-03-12)
- Reduced extraneous whitespace in the generated HTML report. #197
- Addition of a default method to an interface or making a previously abstract method to a default method is marked as incompatible. #201
- japicmp can be build under Oracle JDK 9.0.1 [#182(https://github.com/siom79/japicmp/issues/182)
- If maven artifact is a directory, no stacktrace is printed to stdout. #196
- If interface/class has been removed/added, the class file format version is printed as ‘n.a.’. #194
Available at Maven Central.
0.11.0 (2017-10-18)
- japicmp has now an Ant task. #171
- Two new options includeExclusively and excludeExclusively allow to exclude or include sub-packages. #170
- Changed class file format version is reported. #168
- Square brackets are escaped in regular expressions for behavior matching. #176
- japicmp can be build and run under JDK 1.9. #182
Available at Maven Central.
0.10.0 (2017-04-02)
- The maven plugin relaxes the semantic versioning check for 0.x.x versions, it can be enabled with the option breakBuildBasedOnSemanticVersioningForMajorVersionZero. #165
- The XML report contains now the old and new version of the two archives compared. #164
- The maven plugin execution can be skipped using a CLI property (-Djapicmp.skip=true).
- Removed unnecessary XML element titleOptional from report. #161
- SemverOut now returns 0.0.0 in case no JApiClass(es) are there. #162
- Method throwing in new version RuntimeException is no longer reported to be source incompatible. #167
Available at Maven Central.
0.9.4 (2017-02-16)
- exclude option now also works for inner classes and methods/fields of inner classes. #157
- Added threadSafe=true to @Mojo annotation to suppress @threadSafe warning in parallel builds. #158
- The automatic detection of old version does no longer only compares the qualifier with SNAPSHOT but checks if it ends with SNAPSHOT. #160
Available at Maven Central.
0.9.3 (2016-11-27)
- Added new parameters includeModules and excludeModules to maven plugin. #154
- Do not break build if breakBuildBasedOnSemanticVersioning=true and ignoreMissingOldVersion=true and old version missing. #153
- FIELD_STATIC_AND_OVERRIDES_STATIC and FIELD_LESS_ACCESSIBLE_THAN_IN_SUPERCLASS should not be reported in case field in superclass is also new. #154
- Cover more edit operations on class and interface inheritance hierarchies. #155
Available at Maven Central.
0.9.2 (2016-11-07)
- Illegal characters are removed from filename of diff report. #152
- If method is new, new checked exception is not considered as source incompatible. #151
Available at Maven Central.
0.9.1 (2016-09-28)
- Added new option reportOnlyFilename. #144
- Added new parameter ignoreMissingNewVersion. #148
- Reworked CompatibilityChanges.forAllSuperclasses() such that evaluate() cannot be called recursively. #146
Available at Maven Central.
0.9.0 (2016-08-17)
- Added new option –ignore-missing-classes-by-regex for CLI-Tool. #124
- Added new maven plugin option: breakBuildIfCausedByExclusion. #126
- Implemented skipDiffReport. #139
- Changed the maven site japicmp report to be more descriptive. #141
- Extended automatic detection for new version in case packaging is not jar. #137
- Moving abstract method to interface is binary compatible. #140
Available at Maven Central.
0.8.1 (2016-07-09)
- Variable mavenProject in maven-plugin is read from MavenParameters and not from injected field. #128
- Moving protected field to superclass is no longer marked as binary incompatible. #132
- Maven plugin can be run without section. #134
Available at Maven Central.
0.8.0 (2016-05-14)
- Declaration of and should be optional. #129
- New parameter allows filtering of automatically chosen old version. #129
- Better error message in case exception class could not be loaded from classpath. #130
- serialVersionUID is ignored for enums. #131
Available at Maven Central.
0.7.2 (2016-03-20)
- maven plugin has new option to skip XML, HTML and diff report. #121
- maven-plugin has new option to ignore missing old version. #122
- Interface moved to abstract class is no longer reported to be source incompatible. #123
- When file for optional dependency could not be resolved the plugin does not break the build with ‘could not resolve dependency…’. #125
- Improved output if build is broken due to found incompatibilities.
Available at Maven Central.
0.7.1 (2016-03-14)
- Exceptions declared in method signatures are now tracked and evaluated regarding source incompatibility. #110
- HTML report now names all incompatible changes additional to the markers * and !. #115
- Maven plugin has new parameter postAnalysisScript that allows execution of Groovy script after comparison phase. #120
- Method added to new interface is no longer detected as source incompatible. #112
- Method that overrides in subinterface method from superinterface is no longer detected as source incompatible. #113
- Excluded xerces vom maven-reporting dependency in order to prevent warnings from SAXParserImpl. #109
- Indirectly implemented interfaces are no longer reported as removed. #119
Available at Maven Central.
0.7.0 (2016-02-21)
- japicmp now also supports tracking of source incompatible changes. #59
- The XML report now also outputs the reason for each change for better traceability. #105
- The maven plugin comes now with the new option
breakBuildBasedOnSemanticVersioning
that breaks the build based on the version numbers of the old and new archive(s) assuming they are using semantic versioning. #108
- The maven plugin now has a new option
packagingSupported
that lets you define for which packaging types the plugin should be invoked. This is useful when the plugin is defined in the root pom and should be executed for all submodules, but you want to exclude some of these submodules by packaging type. #100
- Fixed NPE in JavadocLikePackageFilter when matching against classes in the default (empty) package. #106
Available at Maven Central.
0.6.2 (2015-12-20)
- Change tracking can now be filtered by annotations (#88).
- Improved error message in case dependent class is not found (#95).
- Added new option
<ignoreNonResolvableArtifacts/>
for the maven plugin (#94).
- Added option
--no-annotations
to CLI tool and maven plugin (#91).
- fixed: Values for XML attribute value of JApiAnnotationElementValue are escaped properly (#90).
Available at Maven Central.
0.6.1 (2015-11-09)
- semantic versioning information is now also part of the HTML report (#81)
- title of HTML report is now configurable via maven-plugin (#84)
- line numbers are reported for methods and constructors (#77)
- support for
type
attribute in japicmp.maven.Dependency
- resolution filter that does not include optional dependencies(#76)
- fixed: methods pulled up to new superinterface are no longer reported as binary incompatible (#85)
- fixed: project dependencies are not resolved transitively (#83)
Available at Maven Central.
0.6.0 (2015-10-11)
- You can provide now a list of jar files to the CLI tool or a list of dependencies to the maven plugin in order to assemble a report over a bunch of jar files (#67).
- It is now possible to provide separate classpaths for the old and new version (#61).
- You can now provide a classifier for the old and new version (#73).
- The maven plugin provides now an option to specify a separate CSS file for the HTML report (#63).
- Dependencies declared with one of the dependency elements (
<dependencies/>
, <oldDependencies/>
, <newDependencies/>
) are now resolved transitively and all transitive dependencies are added to the classpath (#75).
Available at Maven Central.
0.5.3 (2015-08-16)
- Supported maven version is now 3.0.3 instead of 3.1.0 (#70).
- maven plugin does not fail if run consecutively (#68).
Available at Maven Central.
0.5.2 (2015-08-08)
- The maven plugin now also includes transitive dependencies (#65).
- The maven plugin now automatically skips pom modules and can be configured to not skip them if necessary (#66).
Available at Maven Central.
0.5.1 (2015-07-19)
- The new option
--ignore-missing-classes
allows to ignore superclasses or implemented interfaces from third party libraries that are not available on the classpath. #52
- The maven plugin can be invoked by the
maven-site-plugin
and therewith integrated into the site report. #33
- The new option
--html-stylesheet
allows to provide an individual CSS stylesheet for the HTML report. #33
- The maven plugin has a new parameter
skip
that allows to skip its execution (e.g. based on some profile properties). #56
- Fixed: Exception when class has no superclass (comparing rt.jar).
- Fixed: Parameter
includes
is ignored by the maven plugin.
Available at Maven Central.
0.5.0 (2015-05-26)
- The parameters
--exclude
and --include
have been extended such that next to packages now also classes, methods and fields can be excluded or only included. The syntax is similar to the one used for javadoc references: package.to.include;package.ClassToInclude;package.Class#methodToInclude();package.Class#fieldToInclude
. Please note that the separator char has changed from ,
to ;
. This was necessary as the ,
is now used to separate the arguments for a method in package.Class#methodWithParams(long,int)
(#51).
- Parameters classes for methods with the same name but different signatures are no longer resolved through the classpath (#55).
Available at Maven Central.


NOTE: The examples on this page require a VR capable device. Without one they won't work. See previous article as to why
In the previous article we went over a very simple VR example using three.js and we discussed the various kinds of VR systems.
The simplest and possibly most common is the Google Cardboard style of VR which is basically a phone put into a $5 - $50 face mask. This kind of VR has no controller so people have to come up with creative solutions for allowing user input.
The most common solution is "look to select" where if the user points their head at something for a moment it gets selected.
Let's implement "look to select"! We'll start with
an example from the previous article
and to do it we'll add the PickHelper
we made in
the article on picking. Here it is.
class PickHelper { constructor() { this.raycaster = new THREE.Raycaster(); this.pickedObject = null; this.pickedObjectSavedColor = 0; } pick(normalizedPosition, scene, camera, time) { // restore the color if there is a picked object if (this.pickedObject) { this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor); this.pickedObject = undefined; } // cast a ray through the frustum this.raycaster.setFromCamera(normalizedPosition, camera); // get the list of objects the ray intersected const intersectedObjects = this.raycaster.intersectObjects(scene.children); if (intersectedObjects.length) { // pick the first object. It's the closest one this.pickedObject = intersectedObjects[0].object; // save its color this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex(); // set its emissive color to flashing red/yellow this.pickedObject.material.emissive.setHex((time * 8) % 2 > 1 ? 0xFFFF00 : 0xFF0000); } } }
For an explanation of that code see the article on picking.
To use it we just need to create an instance and call it in our render loop
+const pickHelper = new PickHelper(); ... function render(time) { time *= 0.001; ... + // 0, 0 is the center of the view in normalized coordinates. + pickHelper.pick({x: 0, y: 0}, scene, camera, time);
In the original picking example we converted the mouse coordinates from CSS pixels into normalized coordinates that go from -1 to +1 across the canvas.
In this case though we will always pick where the camera is
facing which is the center of the screen so we pass in 0
for
both x
and y
which is the center in normalized coordinates.
And with that objects will flash when we look at them
Typically we don't want selection to be immediate. Instead we require the user to keep the camera on the thing they want to select for a few moments to give them a chance not to select something by accident.
To do that we need some kind of meter or gauge or some way to convey that the user must keep looking and for how long.
One easy way we could do that is to make a 2 color texture and use a texture offset to slide the texture across a model.
Let's do this by itself to see it work before we add it to the VR example.
First we make an OrthographicCamera
const left = -2; // Use values for left const right = 2; // right, top and bottom const top = 1; // that match the default const bottom = -1; // canvas size. const near = -1; const far = 1; const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
And of course update it if the canvas changes size
function render(time) { time *= 0.001; if (resizeRendererToDisplaySize(renderer)) { const canvas = renderer.domElement; const aspect = canvas.clientWidth / canvas.clientHeight; + camera.left = -aspect; + camera.right = aspect; camera.updateProjectionMatrix(); } ...
We now have a camera that shows 2 units above and below the center and aspect units left and right.
Next let's make a 2 color texture. We'll use a DataTexture
which we've used a few other
places.
function makeDataTexture(data, width, height) { const texture = new THREE.DataTexture(data, width, height, THREE.RGBAFormat); texture.minFilter = THREE.NearestFilter; texture.magFilter = THREE.NearestFilter; texture.needsUpdate = true; return texture; } const cursorColors = new Uint8Array([ 64, 64, 64, 64, // dark gray 255, 255, 255, 255, // white ]); const cursorTexture = makeDataTexture(cursorColors, 2, 1);
We'll then use that texture on a TorusGeometry
const ringRadius = 0.4; const tubeRadius = 0.1; const tubeSegments = 4; const ringSegments = 64; const cursorGeometry = new THREE.TorusGeometry( ringRadius, tubeRadius, tubeSegments, ringSegments); const cursorMaterial = new THREE.MeshBasicMaterial({ color: 'white', map: cursorTexture, transparent: true, blending: THREE.CustomBlending, blendSrc: THREE.OneMinusDstColorFactor, blendDst: THREE.OneMinusSrcColorFactor, }); const cursor = new THREE.Mesh(cursorGeometry, cursorMaterial); scene.add(cursor);
and then in render
lets adjust the texture's offset
function render(time) { time *= 0.001; if (resizeRendererToDisplaySize(renderer)) { const canvas = renderer.domElement; const aspect = canvas.clientWidth / canvas.clientHeight; camera.left = -aspect; camera.right = aspect; camera.updateProjectionMatrix(); } + const fromStart = 0; + const fromEnd = 2; + const toStart = -0.5; + const toEnd = 0.5; + cursorTexture.offset.x = THREE.MathUtils.mapLinear( + time % 2, + fromStart, fromEnd, + toStart, toEnd); renderer.render(scene, camera); }
THREE.MathUtils.mapLinear
takes a value that goes between fromStart
and fromEnd
and maps it to a value between toStart
and toEnd
. In the case above we're
taking time % 2
which means a value that goes from 0 to 2 and maps
that to a value that goes from -0.5 to 0.5
Textures are mapped to geometry using normalized texture coordinates
that go from 0 to 1. That means our 2x1 pixel image, set to the default
wrapping mode of THREE.ClampToEdge
, if we adjust the
texture coordinates by -0.5 then the entire mesh will be the first color
and if we adjust the texture coordinates by +0.5 the entire mesh will
be the second color. In between with the filtering set to THREE.NearestFilter
we'll be able to move the transition between the 2 colors through the geometry.
Let's add a background texture while we're at it just like we covered in the article on backgrounds. We'll just use a 2x2 set of colors but set the texture's repeat settings to give us an 8x8 grid. This will give our cursor something to be rendered over so we can check it against different colors.
+const backgroundColors = new Uint8Array([ + 0, 0, 0, 255, // black + 90, 38, 38, 255, // dark red + 100, 175, 103, 255, // medium green + 255, 239, 151, 255, // light yellow +]); +const backgroundTexture = makeDataTexture(backgroundColors, 2, 2); +backgroundTexture.wrapS = THREE.RepeatWrapping; +backgroundTexture.wrapT = THREE.RepeatWrapping; +backgroundTexture.repeat.set(4, 4); const scene = new THREE.Scene(); +scene.background = backgroundTexture;
Now if we run that you'll see we get a circle like gauge and that we can set where the gauge is.
A few things to notice and try.
We set the cursorMaterial
's blending
, blendSrc
and blendDst
properties as follows
blending: THREE.CustomBlending, blendSrc: THREE.OneMinusDstColorFactor, blendDst: THREE.OneMinusSrcColorFactor,
This gives as an inverse type of effect. Comment out those 3 lines and you'll see the difference. I'm just guessing the inverse effect is best here as that way we can hopefully see the cursor regardless of the colors it is over.
We use a TorusGeometry
and not a RingGeometry
For whatever reason the RingGeometry
uses a flat
UV mapping scheme. Because of this if we use a RingGeometry
the texture slides horizontally across the ring instead of
around it like it does above.
Try it out, change the TorusGeometry
to a RingGeometry
(it's just commented out in the example above) and you'll see what I
mean.
The proper thing to do (for some definition of proper) would be
to either use the RingGeometry
but fix the texture coordinates
so they go around the ring. Or else, generate our own ring geometry.
But, the torus works just fine. Placed directly in front of the camera
with a MeshBasicMaterial
it will look exactly like a ring and the
texture coordinates go around the ring so it works for our needs.
Let's integrate it with our VR code above.
class PickHelper { - constructor() { + constructor(camera) { this.raycaster = new THREE.Raycaster(); this.pickedObject = null; - this.pickedObjectSavedColor = 0; + const cursorColors = new Uint8Array([ + 64, 64, 64, 64, // dark gray + 255, 255, 255, 255, // white + ]); + this.cursorTexture = makeDataTexture(cursorColors, 2, 1); + + const ringRadius = 0.4; + const tubeRadius = 0.1; + const tubeSegments = 4; + const ringSegments = 64; + const cursorGeometry = new THREE.TorusGeometry( + ringRadius, tubeRadius, tubeSegments, ringSegments); + + const cursorMaterial = new THREE.MeshBasicMaterial({ + color: 'white', + map: this.cursorTexture, + transparent: true, + blending: THREE.CustomBlending, + blendSrc: THREE.OneMinusDstColorFactor, + blendDst: THREE.OneMinusSrcColorFactor, + }); + const cursor = new THREE.Mesh(cursorGeometry, cursorMaterial); + // add the cursor as a child of the camera + camera.add(cursor); + // and move it in front of the camera + cursor.position.z = -1; + const scale = 0.05; + cursor.scale.set(scale, scale, scale); + this.cursor = cursor; + + this.selectTimer = 0; + this.selectDuration = 2; + this.lastTime = 0; } pick(normalizedPosition, scene, camera, time) { + const elapsedTime = time - this.lastTime; + this.lastTime = time; - // restore the color if there is a picked object - if (this.pickedObject) { - this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor); - this.pickedObject = undefined; - } + const lastPickedObject = this.pickedObject; + this.pickedObject = undefined; // cast a ray through the frustum this.raycaster.setFromCamera(normalizedPosition, camera); // get the list of objects the ray intersected const intersectedObjects = this.raycaster.intersectObjects(scene.children); if (intersectedObjects.length) { // pick the first object. It's the closest one this.pickedObject = intersectedObjects[0].object; - // save its color - this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex(); - // set its emissive color to flashing red/yellow - this.pickedObject.material.emissive.setHex((time * 8) % 2 > 1 ? 0xFFFF00 : 0xFF0000); } + // show the cursor only if it's hitting something + this.cursor.visible = this.pickedObject ? true : false; + + let selected = false; + + // if we're looking at the same object as before + // increment time select timer + if (this.pickedObject && lastPickedObject === this.pickedObject) { + this.selectTimer += elapsedTime; + if (this.selectTimer >= this.selectDuration) { + this.selectTimer = 0; + selected = true; + } + } else { + this.selectTimer = 0; + } + + // set cursor material to show the timer state + const fromStart = 0; + const fromEnd = this.selectDuration; + const toStart = -0.5; + const toEnd = 0.5; + this.cursorTexture.offset.x = THREE.MathUtils.mapLinear( + this.selectTimer, + fromStart, fromEnd, + toStart, toEnd); + + return selected ? this.pickedObject : undefined; } }
You can see the code above we added all the code to create the cursor geometry, texture, and material and we added it as a child of the camera so it will always be in front of the camera. Note we need to add the camera to the scene otherwise the cursor won't be rendered.
+scene.add(camera);
We then check if the thing we're picking this time is the same as it was last time. If so we add the elapsed time to a timer and if the timer reaches its limit we return the selected item.
Now let's use that to pick the cubes. As a simple example we'll add 3 spheres as well. When a cube is picked with hide the cube and un-hide the corresponding sphere.
So first we'll make a sphere geometry
const boxWidth = 1; const boxHeight = 1; const boxDepth = 1; -const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth); +const boxGeometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth); + +const sphereRadius = 0.5; +const sphereGeometry = new THREE.SphereGeometry(sphereRadius);
Then let's create 3 pairs of box and sphere meshes. We'll
use a Map
so that we can associate each Mesh
with its partner.
-const cubes = [ - makeInstance(geometry, 0x44aa88, 0), - makeInstance(geometry, 0x8844aa, -2), - makeInstance(geometry, 0xaa8844, 2), -]; +const meshToMeshMap = new Map(); +[ + { x: 0, boxColor: 0x44aa88, sphereColor: 0xFF4444, }, + { x: 2, boxColor: 0x8844aa, sphereColor: 0x44FF44, }, + { x: -2, boxColor: 0xaa8844, sphereColor: 0x4444FF, }, +].forEach((info) => { + const {x, boxColor, sphereColor} = info; + const sphere = makeInstance(sphereGeometry, sphereColor, x); + const box = makeInstance(boxGeometry, boxColor, x); + // hide the sphere + sphere.visible = false; + // map the sphere to the box + meshToMeshMap.set(box, sphere); + // map the box to the sphere + meshToMeshMap.set(sphere, box); +});
In render
where we rotate the cubes we need to iterate over meshToMeshMap
instead of cubes
.
-cubes.forEach((cube, ndx) => { +let ndx = 0; +for (const mesh of meshToMeshMap.keys()) { const speed = 1 + ndx * .1; const rot = time * speed; - cube.rotation.x = rot; - cube.rotation.y = rot; -}); + mesh.rotation.x = rot; + mesh.rotation.y = rot; + ++ndx; +}
And now we can use our new PickHelper
implementation
to select one of the objects. When selected we hide
that object and un-hide its partner.
// 0, 0 is the center of the view in normalized coordinates. -pickHelper.pick({x: 0, y: 0}, scene, camera, time); +const selectedObject = pickHelper.pick({x: 0, y: 0}, scene, camera, time); +if (selectedObject) { + selectedObject.visible = false; + const partnerObject = meshToMeshMap.get(selectedObject); + partnerObject.visible = true; +}
And with that we should have a pretty decent look to select implementation.
I hope this example gave some ideas of how to implement a "look to select" type of Google Cardboard level UX. Sliding textures using texture coordinates offsets is also a commonly useful technique.
Next up let's allow the user that has a VR controller to point at and move things.


On 12 January 2019, 12 new articles were published in 9 journals.
Using Plan-Do-Study-Act and Participatory Action Research to Improve Use of Risk Needs Assessments
Code in Transition? the Evolution of Code of the Street Adherence in Adolescence
Cyberbullying Perpetration: An Empirical Test of Social Learning Theory in Iran
Sexual Assault in Intimate Relationships: Introduction to Special Issue
The Effect of Exposure to Racialized Cues on White and Black Public Support For Justice Reinvestment
Controlling the Courts: New Public Management and the Dutch Judiciary
Nearly Twenty Years On: the Canadian Police Executive Community Revisited
Human Rights and the Grammar of Corporate Social Responsibility
Book Review: A Jurisprudence of Movement: Common Law, Walking, Unsettling Place
Homophobia and Homonationalism: Lgbtq Law Reform in Canada


Club Championship 2023 Event 13
| Interclub 25
Middleton |
05 July 23 |
|
25 |
miles |
| Name |
Position |
Road bike |
Actual Time |
Avg. mph |
| |
|
|
|
|
| Sam
Woodfield |
1 |
|
00:55:34 |
26.99 |
| Hudson
Hendry |
2 |
|
00:58:29 |
25.65 |
| Adam
Wells |
3 |
|
01:00:24 |
24.83 |
| Peter
Bradshaw |
4 |
|
01:00:47 |
24.68 |
| Will
Deely |
5 |
|
01:00:52 |
24.64 |
| Warren
Drew |
6 |
|
01:01:29 |
24.40 |
| Chris
Fowler |
7 |
|
01:01:35 |
24.36 |
| Phil
Wilkinson |
7 |
|
01:01:35 |
24.36 |
| Graeme
Hall |
9 |
|
01:02:06 |
24.15 |
| Phil
Rayner |
10 |
|
01:02:33 |
23.98 |
| Jonathan
Durnin |
11 |
|
01:02:53 |
23.85 |
| Nik
Kershaw |
12 |
|
01:03:36 |
23.58 |
| Jamie
Kershaw |
13 |
|
01:03:52 |
23.49 |
| Rhys
Thomas |
14 |
1 |
01:03:58 |
23.45 |
| Nigel
Tomlinson |
15 |
|
01:04:32 |
23.24 |
| Jason
Williams |
16 |
2 |
01:05:16 |
22.98 |
| Lawrence
Cox |
16 |
|
01:05:16 |
22.98 |
| Steve
Coombs |
18 |
|
01:05:31 |
22.89 |
| Doug
Tincello |
19 |
|
01:07:13 |
22.32 |
| Andy
King |
20 |
3 |
01:07:39 |
22.17 |
| Gary
Ashwell |
21 |
|
01:08:53 |
21.78 |
| Steve
Whickham |
22 |
|
01:09:15 |
21.66 |
| Linda
Hubbard |
23 |
|
01:09:44 |
21.51 |
| Andrew
Brown |
24 |
|
01:09:58 |
21.44 |
| Robert
Warrander |
25 |
|
01:10:42 |
21.22 |
| James
Brown |
26 |
|
01:10:44 |
21.21 |
| Giles
Barringham |
27 |
|
01:10:57 |
21.14 |
| Simon
Ward |
28 |
|
01:11:43 |
20.92 |
| Paul
White |
29 |
|
01:11:50 |
20.88 |
| Michael
Deely |
30 |
|
01:12:30 |
20.69 |
| Milly
Pinnock |
31 |
4 |
01:12:35 |
20.67 |
| Pete
Wilson |
32 |
|
01:12:36 |
20.66 |
| David
Hill |
33 |
5 |
01:13:41 |
20.36 |
| Steven
Coulam |
34 |
|
01:15:36 |
19.84 |
| Paul
Ryan |
35 |
|
01:16:30 |
19.61 |
| Jax
Roberts |
36 |
6 |
01:17:56 |
19.25 |
| Alistair
Watson |
37 |
7 |
01:18:17 |
19.16 |
| Vic
Barnett |
38 |
8 |
01:19:58 |
18.76 |
| John
Welsford |
39 |
9 |
01:20:51 |
18.55 |
| Debbie
Ward |
40 |
10 |
01:24:55 |
17.66 |
| Jane
Moore |
41 |
11 |
01:26:25 |
17.36 |
| Sophie
Ward |
42 |
|
01:26:43 |
17.30 |
| Lynne
Scofield |
43 |
|
01:26:48 |
17.28 |
| Amanda
McPherson |
44 |
12 |
01:32:13 |
16.27 |
| Karen
Foster |
|
|
DNS |
|
| Gary
Roberts |
|
|
DNS |
|
|
|
|
|
|


1. FULL NAME 2. PHONE AND FAX NUMBER 3. ADDRESS WERE YOU WANT THEM TO SEND THE ATM CARD TO(P.O BOX NOT ACCEPTABLE) 4. YOUR AGE AND CURRENT OCCUPATION 5. ATTACH COPY OF YOUR IDENTIFICATION HOWEVER, KINDLY FIND BELOW THE CONTACT PERSON: DIRECTOR, ATM PAYMENT DEPARTMENT CELL: 234-80-54260-455<EMAIL_ADDRESS>THE ATM CARD PAYMENT CENTER HAS BEEN MANDATED TO ISSUE OUT $8,300,000.00 AS PART PAYMENT FOR THIS FISCAL YEAR 2006.   REGARDS, MRs.Susan Harrison CHIEF AUDITOR TO THE PRESIDENT FEDERAL REPUBLIC OF NIGERIA Endlich Sicherheit bei Computerabstuerzen - Phoenix Recover Pro. http://internetzugang.tiscali.de/internet_services/detail_rec.php


Personal Loan
Put your plans
Put your plans
into actions
Online applications
Up to $50.000
The money is
yours in 48h


The SPIPort class writes to and reads from the one of the two SPI bus On Chip Peripheral (OCP) devices integrated into the Beaglebone Black CPU.
In the Linux operating system there are two ways of accessing the SPI devices; the first, called SPIDev, is the
standard and approved Linux technique. SPIDev is a device driver and if the existence of a SPI port is detected during boot time,
the device driver interface will be exposed as a file in the /dev
directory of the Beaglebone Black file system. The
SPI ports will only be detected if that port is appropriately configured in the Device Tree.
The second type of access is called Memory Mapped access and this treats the Beaglebone Black's RAM memory as if it were a file. The SPI port is manipulated as if it were a bit at an offset in a virtual file. The BBBCSIO library does not, at this time, provide a class which utilizes Memory Mapped access to the SPI port - the file system based SPIDev class named SPIPortFS is the only one available.
There are two SPI ports on a Beaglebone Black. If these ports have been correctly configured in the Device Tree, the SPIDev device driver for that port will be accessible
via a file in the /dev
directory. This file will have names in the format spidev<spiport+1>.<ssline>
where <spiport+1>
is
one greater (nobody knows why) than the number of the spi port and the <ssline>
is the number of the slave select (also known as the chip select) line.
Thus the file /dev/spidev1.0
is the interface for the SPIDev device driver on SPI port 0 and slave select line 0. If you do not see the spidev*
file
in the /dev
directory this means that the SPI device is not correctly configured in the Device Tree. By the way, these files are not the sort of files you
can open and write text to in order control the port (like you do with the SYSFS GPIO subsystem). The spidev files are true device files and if you want them to do anything
you have to use an ioctl()
call.
Each device on an SPI bus has its own enable/disable line called the Slave Select (or sometimes Chip Select) line and the Beaglebone Black functions as the SPI bus master.
Each of the two OCP SPI devices on the Beaglebone Black can theoretically control up to 4 Slave Select lines, however only 2 of the lines (CS0 and CS1) are made available
to the CPU's PinMux sub-system. Effectively this means that, using the SPI ports dedicated slave select lines, it is only possible to have 2 slave devices per SPI port.
Even worse, it appears that the cpu pin controlling CS1 of SPI0 has not been made visible on the I/O headers by the designers of the Beaglebone Black and so the SPI0 port can
only interact with one slave device if one uses the dedicated CS lines of the SPI port. Using the nomeclature of the above SPIDev device filenames, only the files
/dev/spidev1.0
, /dev/spidev1.1
, /dev/spidev2.0
and /dev/spidev2.1
can ever be possibly be present and even then
the slave select line for the /dev/spidev1.1
device (SPI0, CS1) cannot be used. However, not to worry, the SPIPortFS class has additional functionality
which enables GPIO ports to be used as slave select lines and hence quite a large number of SPI bus devices are possible. In addition, be aware that the MOSI, MISO, CLK and slave select
lines used by the SPI1 port are also the GPIO's used by the HDMI subsystem (just a different MuxMode). To use SPI1 you will need to disable HDMI first - which is why you will probably never
see it configured in the Beaglebone Black Device Tree by default.
Please note that for the SPIPortFS class you cannot mix and match GPIO based slave select lines and the SPI ports chip select lines. If you use one GPIO based slave select line then all lines must be GPIO based on that port. This is because the chip select lines are tied to the SPIDev device file. Once the spidev* file is opened (and it needs to be in order to write anything at all to the device) the SPI port will always activate the dedicated chip select line for each write and there is no way to stop it doing this. The best you can do is ignore the dedicated chip select line (by leaving it unconnected and floating electrically) and using the GPIO based slave selects to activate each device.
The SPI port used in the examples below is SPI0 - this must be configured in the Device Tree in order for it to be available for use.
The second example code below uses GPIOs 48 and 49 which are exposed as pin 15 and 23 on the P9 header.  Any one GPIO can be used for any of about half a dozen different peripherals (UART, PWM, A/D, HDMI, EMMC etc.) and it is not a given that a GPIO used in these examples or any specific GPIO is available. The GPIO's used in these examples may be used for other things on your Beaglebone Black.
Before using a GPIO as a slave select line on any SPIPort class you will need to ensure that the GPIOs are configured as outputs in exactly the same way as if you were going to use them with one of the OutputPort classes. There is no code you can call in the BBBCSIO library which does this. You MUST configure the GPIOs externally prior to running your executable - usually this is done by editing the device tree.
The armhf Linux running on the Beaglebone Black is not a real-time operating system. However, an OCP peripheral such as the SPIPort has its own internal clock and so, once the information is given to the port, it will be transmitted and received at the configured port speed with no interruptions. However, transfers made via multiple calls to the SPI port can, and will, have variable gaps between transmissions as the process is pre-emptively swapped in and out by the kernel.
Also note it is possible to set the transmission speed for the SPI Port as a default on the port and also to specifically override that speed individually for a slave device. For the SPIPortFS class it is much faster, by a factor of about 10, to set the port speed on the port and let the slave device use it as a default than to set it individually on each device. This is true even if the slave device speed is set to the exact same speed as the global SPI port default value. There is something in the SPIDev device driver that has to activate internally on each transmission (one or more ioctl() calls probably) to override the SPI port default speed for an individual device and this is time consuming. It is far better to set the speed on the SPI port rather than the slave device unless you really need to have different speeds for each device.
There are two examples below. The first shows a write and read from a SPI slave device using the internal CS0 line. The second example illustrates the use of two GPIO base slave select lines to control two different SPI slave devices.
The code below illustrates how to use the SPIPortFS class to write and read the a memory location in a slave SPI device and to output that value to the console.
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Writes to and reads from an SPI port. SPIDev version. This version /// uses the internal slave select SPI_SLAVEDEVICE_CS0 for the chip /// select line. The SPI device being accessed is an Arduino Gameduino v1.2 /// VGA shield. At its most basic, it is read from by sending two address bytes /// and then a follow up byte of 0x00 to clock in the byte at that address. /// /// NOTE: /// This code assumes that the SPI port associated with the spiID has been /// properly configured in the device tree. If it is not /// then the port may not work correctly. See the associated documentation /// /// NOTE: /// Make sure you are aware of how SPI buses work and properly /// configure the MISO, MOSI, CLK and CS lines. /// /// </summary> /// <param name="spiID">The spi port ID</param> /// <history> /// 21 Dec 14 Cynic - Originally written /// </history> public void SPIPortFSTest(SPIPortEnum spiID) { // open the port SPIPortFS spiPort = new SPIPortFS(spiID); // open the slave device SPISlaveDeviceHandle ssHandle = spiPort.EnableSPISlaveDevice(SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CS0); if (ssHandle == null) { throw new Exception("ssHandle == null"); } // set the mode spiPort.SetMode(SPIModeEnum.SPI_MODE_0); // just for testing, get the mode, not really necessary SPIModeEnum spiMode = spiPort.GetMode(); Console.WriteLine("spi mode=" + spiMode.ToString()); // set our speed spiPort.SetDefaultSpeedInHz(5000000); // set up our transmit and receive buffers int bufLen = 3; byte[] txByteBuf = new byte[bufLen]; byte[] rxByteBuf = new byte[bufLen]; // we wish to read the byte at address 0x0001 which is // the Gameduino's major and minor version number // populate the transmit buffer with data, we send // two bytes of address and a 0x00 to clock in the // returning information txByteBuf[0] = 0x00; txByteBuf[1] = 0x01; txByteBuf[2] = 0x00; // transfer the data spiPort.SPITransfer(ssHandle, txByteBuf, rxByteBuf, bufLen); // report the received data, the first two bytes will // be random values as the Gameduino was still receiving // the address at that time. The third byte will have // the major and minor revision number in each nibble. Console.Write("Received data as hex:"); for(int i=0; i < bufLen; i++) { Console.Write(" "+ rxByteBuf[i].ToString("x2")); } Console.Write("\n"); // close the port spiPort.ClosePort(); spiPort.Dispose(); }In the above code, the
spiID
is passed in when the function was called. This value is a member of the
SPIPortEnum class
which lists all possible SPI ports which
can be present on the Beaglebone Black. The above code is called via a line which looks like:
SPIPortFSTest(SPIPortEnum.SPIPORT_0);
The code below illustrates how to use the SPIPortFS class to use GPIO pins as slave select lines and how to write values to those devices.
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Writes to an SPI port. SPIDev version. This version uses two GPIO pins /// as slave select lines for the SPI devices. /// /// The device being written to is a Sparkfun 7-Segment Serial Display /// which has an SPI interface but which cannot write back any information /// on the SPI bus - it is write only. The basic mode of operation is just /// to write the ASCII values of the four characters you wish to display /// to the device. With two devices we have 8 digits. /// /// NOTE: /// This code assumes that the SPI port associated with the spiID has been /// properly configured in the device tree. If it is not then the port /// may not work correctly. In addition, the two GPIOs (GPIO_48 and GPIO_49) /// must also be configured as outputs. /// /// NOTE: /// Make sure you are aware of how SPI buses work and properly /// configure the MISO, MOSI, CLK and CS lines. /// /// </summary> /// <param name="spiID">The spi port ID</param> /// <history> /// 21 Dec 14 Cynic - Originally written /// </history> public void SPIPortFSGPOISSTest(SPIPortEnum spiID) { // open the port SPIPortFS spiPort = new SPIPortFS(spiID); // open the slave devices. We are using GPIO_48 and GPIO_49 as slave selects // but we always have to open CS0 or CS1 in order to get access to the SPI // device itself - but we cannot use this slave select line and it must // not be electrically connected to anything. SPISlaveDeviceHandle ssHandleCS1 = spiPort.EnableSPISlaveDevice(SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CS1); if (ssHandleCS1 == null) { throw new Exception("ssHandleCS1 == null"); } SPISlaveDeviceHandle ssHandleGPIO48 = spiPort.EnableSPIGPIOSlaveDevice(GpioEnum.GPIO_48); if (ssHandleGPIO48 == null) { throw new Exception("ssHandleGPIO48 == null"); } SPISlaveDeviceHandle ssHandleGPIO49 = spiPort.EnableSPIGPIOSlaveDevice(GpioEnum.GPIO_49); if (ssHandleGPIO49 == null) { throw new Exception("ssHandleGPIO48 == null"); } // set the mode spiPort.SetMode(SPIModeEnum.SPI_MODE_0); // set our speed spiPort.SetDefaultSpeedInHz(25000); // set up our transmit and receive buffers int bufLen = 4; byte[] txByteBuf = new byte[bufLen]; byte[] rxByteBuf = new byte[bufLen]; // populate the transmit buffer with data "1234" txByteBuf[0] = 0x31; txByteBuf[1] = 0x32; txByteBuf[2] = 0x33; txByteBuf[3] = 0x34; // NOTE that other than opening ssHandleCS1 we do not write to it. The pin // where the CS1 appears should not be electrically attached to any SPI device // When using GPIO based slave selects lines all SPI devices must use them // when using the internal CS0 or CS1 lines all devices must use those. You // cannot mix and match GPIO and CS* slave select lines. // transfer the data to the device listening on GPIO48 spiPort.SPITransfer(ssHandleGPIO48, txByteBuf, rxByteBuf, bufLen); // populate the transmit buffer with new data "5678" txByteBuf[0] = 0x35; txByteBuf[1] = 0x36; txByteBuf[2] = 0x37; txByteBuf[3] = 0x38; // transmit the new data to the device listening on GPIO49 spiPort.SPITransfer(ssHandleGPIO49, txByteBuf, rxByteBuf, bufLen); // close the port spiPort.ClosePort(); spiPort.Dispose(); }In the above code, the
spiID
is passed in when the function was called. This value is a member of the
SPIPortEnum class
which lists all possible SPI ports which
can be present on the Beaglebone Black. The above code is called via a line which looks like:
SPIPortFSGPOISSTest(SPIPortEnum.SPIPORT_0);


**Expressive Vector Engine**
`eve::sqr`
(insert ../../../crumbs.html here)
Synopsis
====================================================================================================
**Required header:**
Function object representing the sqr operation.
Member functions
====================================================================================================
Performs the [element-wise](../../../glossary.html#element-wise) sqr operation on its parameter.
Parameters
----------------------------------------------------------------------------------------------------
`x`
: [value](../../concepts.html#value).
Return value
----------------------------------------------------------------------------------------------------
Computes [element-wise](../../../glossary.html#elment-wise) the square of `x`.
!!! WARNING
For [real integral signed values](../../concepts.html#value) if `saturated_(abs)(x)`
is greater than [`eve::Sqrtvalmax(as(x))`](../../constants/limits/sqrtvalmax.html) the corresponding element result is undefined .
Supported Decorators
====================================================================================================
[`saturated_`](../decorators.html#saturated_)
: The expression `saturated_(abs)(x)` computes a saturated square of `x`. Contrary to the [regular case](../decorator/regular.html), this guarantees
that the result is [`element-wise`](../../../glossary.html#element-wise) greater or equal than 0. More specifically, for any signed integer value
`x`, the expression:
`saturated_(sqr)(x)`
evaluates to:
[`eve::Valmax(as(x))`](../../constants/limits/valmax.html) as soon as `saturated_(abs)(x)`
is greater than [`eve::Sqrtvalmax(as(x))`](../../constants/limits/sqrtvalmax.html).
(insert ../../src/sqr.src.html here)
(insert ../../out/sqr.out.html here)
See Also
====================================================================================================
[eve::abs](eve::abs), [eve::sqr_abs](eve::sqr_abs),


--- layout: jsdoc title: "Class: Router" jsdocNav: - Modules: - workbox-background-sync - workbox-broadcast-cache-update - workbox-build - workbox-cache-expiration - workbox-cacheable-response - workbox-google-analytics - workbox-precaching - workbox-range-requests - workbox-routing - workbox-runtime-caching - workbox-sw - workbox-webpack-plugin ---
The Router takes one or more Routes and allows you to apply that routing logic to determine the appropriate way of handling requests inside of a service worker.
It also allows you to define a "default" handler that applies to any requests
that don't explicitly match a Route
, and a "catch" handler that responds
to any requests that throw an exception while being routed.
You can use the handleRequest()
method to pass a FetchEvent
through the
router and ultimately get a "routed response" back.
If you'd like this to be handled automatically for you, calling
addFetchListener()
will cause the Router
to respond to fetch
events.
// The following example sets up two routes, one to match requests with
// "assets" in their URL, and the other for requests with "images", along
// different runtime caching handlers for each.
// Both the routes are registered with the router, and any requests that
// don't match either route will be handled using the default NetworkFirst
// strategy.
const assetRoute = new RegExpRoute({
regExp: /assets/,
handler: new workbox.runtimeCaching.StaleWhileRevalidate(),
});
const imageRoute = new RegExpRoute({
regExp: /images/,
handler: new workbox.runtimeCaching.CacheFirst(),
});
const router = new workbox.routing.Router();
router.addFetchListener();
router.registerRoutes({routes: [assetRoute, imageRoute]});
router.setDefaultHandler({
handler: new workbox.runtimeCaching.NetworkFirst(),
});
Constructs a new Router
instance, without any registered routes.
This will register a fetch
event listener on your behalf which will check
the incoming request to see if there's a matching route, and only respond
if there is a match.
const imageRoute = new RegExpRoute({
regExp: /images/,
handler: new CacheFirst(),
});
const router = new Router();
router.registerRoute({route: imageRoute});
router.addFetchListener();
| Returns |
boolean
Returns |
|---|
This can be used to apply the routing rules to generate a response for a
given request inside your own fetch
event handler.
const imageRoute = new RegExpRoute({
regExp: /images/,
handler: new CacheFirst(),
});
const router = new Router();
router.registerRoute({route: imageRoute});
self.addEventListener('fetch', (event) => {
event.waitUntil((async () => {
let response = await router.handleRequest({event});
// Do something with response, and then eventually respond with it.
event.respondWith(response);
})());
});
| Parameters |
|
||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Returns |
Promise.<Response>
|
undefined
Returns a promise for a response,
taking the registered routes into account. If there was no matching route
and there's no |
Registers a single route with the router.
router.registerRoute({
route: new Route({ ... })
});
| Parameters |
|
|---|
Registers an array of routes with the router.
router.registerRoutes({
routes: [
new RegExpRoute({ ... }),
new ExpressRoute({ ... }),
new Route({ ... }),
]
});
| Parameters |
|
|---|
If a Route throws an error while handling a request, this handler
will be called and given a chance to provide a response.
router.setCatchHandler(({event}) => {
if (event.request.mode === 'navigate') {
return caches.match('/error-page.html');
}
return Response.error();
});
| Parameters |
|
|---|
An optional handler
that's called by default when no routes
explicitly match the incoming request.
If the default is not provided, unmatched requests will go against the network as if there were no service worker present.
router.setDefaultHandler({
handler: new workbox.runtimeCaching.NetworkFirst()
});
| Parameters |
|
|---|
Unregisters a single route with the router.
const route = new RegExpRoute({ ... });
router.registerRoute({route});
// Later, if you no longer want the route to be used:
router.unregisterRoute({route});
| Parameters |
|
|---|
Unregisters an array of routes with the router.
const firstRoute = new RegExpRoute({ ... });
const secondRoute = new RegExpRoute({ ... });
router.registerRoutes({routes: [firstRoute, secondRoute]});
// Later, if you no longer want the routes to be used:
router.unregisterRoutes({routes: [firstRoute, secondRoute]});
| Parameters |
|
|---|


Developer
Social network for movie lovers
Share your travel recommendations
Firebase rail timetables
Timed multiple-choice game
Online storefront using Node
Dating App using Node.js and Express


Password Generator
Generate a Password
Your Secured Password
Generate Password
Copy Password To Clipboard


A view of a problem that extracts the essential information relevant to a particular purpose and ignores the remainder of the information[IEEE, 1983]
It's a powerful technique for dealing with complexity
Unable to master a complex object, we choose to ignore its non-essential details, dealing instead with a generalized and idealized model
Encapsulation or equivalently information hiding refers to the practice of including within an object everything it needs, and furthermore doing this in such a way that no other object need ever be aware of this internal structure[Graham, 1991]
Encapsulation helps with the loose coupling between objects, resulting in software easier to maintain and scale
Through Abstraction we identify the essential aspects of a system
With Encapsulation we expose to the user of the system a clear and well defined interface, hiding all the complex implementation details
Objects in a software program interact with other objects through their public interfaces
Define a relationship between classes where one class
contains or owns another class
Aggregation or Composition define HAS-A relationships
Relationship between classes where derived (sub) classes inherit state and behaviour from base (super) classes
Allows reuse of existing code with little or no modification
Inheritance defines IS-A relationships
Both mechanisms allow for reusing existing classes
| Inheritance | Composition | |
|---|---|---|
| Relation | static (compile time) | dynamic (run time) |
| Encapsulation | breaks encapsulation | favours encapsulation |
| Coupling | high | low |
| Cohesion | low | high |
| Boilerplate | low | high |
Always favor composition over inheritance
Use inheritance only if there is a clear hierarchical relationship between classes
Inheritance gives us polymorphism, which etymologically means many forms and provides one of the most powerful programming techniques of the object-oriented paradigm
Instead of binding the method call to the actual code at compile time (static binding), the compiler will defer the decision on which method to call to runtime. This is known as dynamic or late binding
// References of the base class type can point to the type of the descendent class
Shape shape1 = new Rectangle();
Shape shape2 = new Triangle();
// Which methods will be called?
shape1.getArea();
shape2.getArea();
Java single inheritance severely cripples the benefits of polymorphism, but a restricted form of multiple inheritance is supported through interfaces, unleashing the full power of polymorphism
Designing object-oriented software is hard and designing reusable object-oriented software is even harder.
Design should be specific but also general enough for future problems and requirements. Avoid redesign, or at least minimize it.
Reusable and flexible designs are difficult if not impossible
to get right the first time.
Yet, experienced object-oriented designers do make good designs.
They reuse solutions that have worked for them in the past
When they find a good solution, they use it again, and again...
Such experience is part of what makes them experts
Wouldn't it be great if this knowledge could be shared for novice developers?
A general repeatable solution to a commonly occurring
problem in software design
A description or template to solve a problem that can be used in many different situations
A finished design that can be transformed directly into code
Some very simple OOP principles form the basis
of almost every design pattern:
If some aspect of your code is changing or likely to change, it needs to be pulled out and separated from the rest
If a single requirement changes, it should only require us to change the related code in a single place
But if the code base is badly designed, the change will be difficult and have high risk of causing unexpected effects
Programming to an interface is saying
"I need this functionality and I don't care where it comes from"
Interfaces allow a class to use functionality
without depending on the implementing class directly
If you decide to use a different implementation, you only
need to change the code where the concrete object is instantiated
Inheritance is not bad per se and is a very powerful (and essential) tool in Object Oriented Design
However when not used properly it creates code that is
inflexible, tightly coupled and hard to maintain
Polymorphic behaviour, code reuse and greater flexibility can be achieved through composition rather than inheritance
The "Gang of Four" divided the design patterns they documented into three fundamental groups:
Design patterns that abstract the instantiation process
They help make a system independent of how its objects are created, composed and represented
Examples include the
Factory,
Builder,
Prototype
or Singleton design patterns
Patterns concerned with how classes and objects are
composed to form larger structures
Examples include the Decorator, Facade or Proxy design patterns
These patterns are all about how objects
communicate with each other
Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects
Examples include the
Strategy,
Command,
Template
or Observer design patterns
The creation logic for an object or family of related objects
can quickly become complex
The user of such objects has to be aware of
every concrete class and its respective creation logic
If you need to remove or add new objects to the family
or make changes to their creation logic,
all users of such objects will require modification!
Encapsulate all the object creation logic inside a method { Remember: Encapsulate what changes! }
This method is called the Factory Method and replaces the usage of the new operator for each implementation
The object user becomes unaware of concrete implementation or creation logic and can take advantage of polymorphism { Remember: Program to an interface, not an implementation! }
It's now possible to create new concrete implementations or modify creation logic without changing any code besides the factory itself
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() { System.out.println("Drawing a circle!"); }
}
public class Square implements Shape {
@Override
public void draw() { System.out.println("Drawing a square!"); }
}
This is our Shape interface, along with its concrete implementations
public class ShapeFactory {
public Shape create(ShapeType type) {
switch(type) {
case CIRCLE:
return new Circle();
case SQUARE:
return new Square();
default:
return new Circle();
}
}
}
public enum ShapeType {
CIRCLE,
SQUARE
}
ShapeFactory is a class containing the factory method
public class Main {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
Shape s2 = shapeFactory.create(ShapeType.CIRCLE);
Shape s1 = shapeFactory.create(ShapeType.SQUARE);
s1.draw(); // "Drawing a circle!"
s2.draw(); // "Drawing a square!"
}
}
Sometimes we want to add features to individual objects at runtime
Inheritance defines the object functionality at compile-time and shares it among all descendants
If multiple features need to be added, we would need to
create classes for each possible
feature combination
Objects are wrapped by objects of the same type (decorators),
which may add or change functionality before/after
delegating to the wrapped object.
{ Remember: Favour composition over inheritance! }
As they share the same type, it's possible to nest decorators recursively, allowing an unlimited number of added responsibilities
Imagine you own a car dealer
Every time you sell a car, you want to be able to add some extras
You want to invoice your sale with the proper description
and the final price of the car
You could, but how easily would this get out of control?
Considering only 4 accessories you would need
24 different classes to satisfy all possible combinations
Ever heard about class explosion?
And we're not even addressing the problem of a change of price on one of the accessories!
1. Create your car interface
public interface Car {
String getAccessories();
int getPrice();
}
2. And a concrete class for a car with no extras whatsoever
public class BasicCar implements Car {
@Override
public String getAccessories() {
return "Basic Car"
}
@Override
public int getPrice() {
return 4000;
}
}
3. Create your abstract decorator (which implements car)
public abstract class CarDecorator implements Car {
private Car decoratedCar;
public CarDecorator(Car decoratedCar) {
this.decoratedCar = decoratedCar;
}
@Override
public String getAccessories() {
return decoratedCar.getAccessories();
}
@Override
public int getPrice() {
return decoratedCar.getPrice();
}
}
Have you noticed how your car has-a car?
4. Create your concrete decorators - Expandable Rooftop
public class ExpandableRooftop extends CarDecorator {
public ExpandableRooftop(Car decoratedCar) {
super(decoratedCar);
}
@Override
public String getAccessories() {
return decoratedCar.getAccessories() + " with Expandable Rooftop";
}
@Override
public int getPrice() {
return decoratedCar.getPrice() + 200;
}
}
4. Create your concrete decorators - Air Conditioner
public class AirConditioner extends CarDecorator {
public AirConditioner(Car decoratedCar) {
super(decoratedCar);
}
@Override
public String getAccessories() {
return decoratedCar.getAccessories() + " with Air Conditioner";
}
@Override
public int getPrice() {
return decoratedCar.getPrice() + 200;
}
}
5. Make a Sale - Test your decorator pattern
public class Main {
public static void main(String[] args) {
Car car = new AirConditioner(new ExpandableRooftop(new BasicCar()));
System.out.println("Accessories: " + car.getAccessories());
// "Accessories: Basic Car with Expandable Rooftop with Air Conditioner"
System.out.println("Price: " + car.getPrice());
// "Price: 4300"
}
}
You’ve got multiple algorithms to achieve the same functionality and they must be interchangeable at runtime
A single method is edited often in order to fulfill new requirements, normally leading to a lot of if/else clauses or switch statements, introducing complexity in your code
Create an interface that declares the behaviour
{ Remember: Program to the interface! }
Encapsulate each algorithm inside an implementing class
{ Remember: Encapsulate what varies! }
All clients that need to perform these operations interchangeably should refer to the interface and not to a particular implementation
public interface Transport {
void goTo(String place);
}
public class Taxi implements Transport {
@Override
public void goTo(String place) {
System.out.println("Going to " + place + " by taxi!");
}
}
public class Bus implements Transport {
@Override
public void goTo(String place) {
System.out.println("Going to " + place + " by bus!");
}
}
This is our Transport interface, along with its concrete implementations
public class Person {
private Transport transport;
public void goToWork() {
transport.goTo("work");
}
public void goHome() {
transport.goTo("home");
}
public void setTransport(Transport transport) {
this.transport = transport;
}
}
This is a class with a Transport as a field, which may store different Transport implementations
public class Main {
public static void main(String[] args) {
Person person = new Person();
Transport taxi = new Taxi();
Transport bus = new Bus();
person.setTransport(taxi);
person.goToWork(); // "Going to work by taxi!"
person.setTransport(bus);
person.goToWork(); // "Going to work by bus!"
}
}
Apply relevant design patterns of your choice
Patterns occur at many levels
Design Patterns are at the level of several collaborating objects
Architecture Patterns are the largest in scope and cover the fundamental organization of the system, providing reusable solutions to common problems in software architecture
Software architecture pattern that divides a given software application into three interconnected parts, separating internal representation of data from the way data is presented to or accepted from the user
Multiple variations to the classic MVC pattern exist
They all build upon the idea to separate the data (model) from the specific presentation technology (view) and encapsulating the logic that hooks the two behind some sort of controller object
Refactor and improve existing code base by applying the MVC architectural pattern
Where should we place the business logic of our applications?
A cleaner and scalable approach is to extract business logic and data access code into a Service Layer
Using a Service Layer Pattern, the Controller must take request data and pass it into the Service layer
The service layer returns data for the Controller
to inject into a View for rendering
public interface CustomerService {
Customer get(Integer id);
List<Customer> list();
Set<Integer> listCustomerAccountIds(Integer id);
double getBalance(int customerId);
void add(Customer customer);
}
public interface AccountService {
void add(Account account);
void deposit(int id, double amount);
void withdraw(int id, double amount);
void transfer(int srcId, int dstId, double amount);
}
public interface AuthService {
boolean authenticate(Integer id);
Customer getAccessingCustomer();
}
Refactor and improve existing code by applying the service layer pattern


Jinia:
All I have left to tell you is about the Divine Sword. After that, I will ask something very important of you.
Ask about the Divine Sword.


Carbon Supplychain Tracker using Blockchain
Track your Carbon Emissions through Supplychain on Blockchain.
Create Batch:
Create Batch
Transfer Item and enter carbon emission:
Transfer Asset
Track Asset Details:
Track Asset Details
Item Name:
??
Manufacturer:
??
Current Location:
??
Total Emissions (till now):
??


--- layout: post title: Sooner or later, someone is going to come along and create something that kills Photoshop, just as InDesign killed XPress. And Adobe will deserve it. date: '2009-06-01T23:06:00+01:00' tags: [] tumblr_url: http://backlog.robbevan.com/post/116444374/sooner-or-later-someone-is-going-to-come-along ---
Sooner or later, someone is going to come along and create something that kills Photoshop, just as InDesign killed XPress. And Adobe will deserve it.—Will someone do to Photoshop what InDesign did to Quark? | Technovia


Home
About Me
My Works
How To Reach Me
My
Work
Check out some of my finished projects...
And some
unfinished drafts
...
project


You don't seem to be connected to the Ethereum blockchain. May we suggest one of the following to help with that:
A plugin for Chrome that allows you to connect directly to the Ethereum blockchain.
A new Browser, built for privacy, with MetaMask built in.
Advanced users can use Mist to talk to the Unum Smart Contract directly.


IITK Senate Voting System
Toggle navigation
Vote
Add Candidates
Candidates Result
ID
Name
Votes
Election Winners :


Integrations
Connect with G Suite
Connect with Slack
Configuration
Company name
Company logo
eSignatures NDA template ID
eSignatures API token
Save


Building binaries
Prerequisites
Following tools are required to build RadarGun from sources
- git >= 1.6.5, needed for obtaining the source code. Alternatively, a ZIPped archive of the sources can be used instead.
- A Java Development Kit version 1.7 or above is required.
- Apache Maven >= 3.0, needed for downloading dependencies and building the source code
Download source code
Move to a directory where you will build the binaries and run:
$ git clone git://github.com/radargun/radargun.git
Compile and build distribution
$ cd radargun
$ mvn clean install -DskipTests
This operation might take a few minutes, as it will download the dependencies needed by the various plugins (e.g., it will download Infinispan and its dependencies, EHCache and its dependencies, etc). Once the install is complete, you will find the distribution in ./target/distribution/RadarGun-<version>.zip
. Unzip this on cluster nodes where you wish to run RadarGun.
Distributions's content (for RadarGun 2.1.x)
|-bin
|-conf
|-lib
|-plugins
|---ehcache26
|-----conf
|-----lib
|---hazelcast3
|-----conf
|-----lib
|---infinispan80
|-----conf
|-----lib
|---...
|-reporters
|-schema
|-results (optional)
The
bin
directory contains scripts for launching the master, the slaves and some other utility scripts. This directory also contains scripts to run RadarGun in local mode. For details on how to run a distributed benchmark see Benchmark configuration and Using scripts.The
conf
directory contains configuration files for running distributed benchmarks (benchmark-dist.xml
), local benchmarks (local-benchmark.xml
) and the log4j cofiguration file.The
plugins
directory contains a directory for each (product,version) combo that can be benchmarked.conf
folder contains configuration files used by the product, which can be referenced in benchmark configuration.lib
folder contains all libraries and other resources needed to run selected product.The
reporters
directory includes reporters, which provide way to process and output statistics gathered during test runs.The
schema
directory includes XSD schema files which can be used to verify whether benchmark configuration is correct.By default the
results
directory is created once the test finishes and reporters generate results. Result location is configurable.


attachment_fu is a significant rewrite of acts_as_attachment, so a lot of the codebase has changed. You should consider upgrading your application, if possible.
If you would like to resize images based on whatever, (here the params[:adtype].keys of the AdsController having a certain value) you can do this:
1. Add a custom version of this to your model
{% highlight ruby %}
#...
include ObjectSpace
# change :ad to whatever model your image belongs to.
belongs_to :ad
def self.find_size
# Change AdsContoller to the controller of your model and params[adtype].keys to
# whatever value you want to test for.
if adtype = ObjectSpace.each_object(AdsController) {|a| p a.params[:adtype].keys unless a.params[:adtype] == nil}
@size = case adtype
# customize your sizes and conditions
when 1
'150x100'
when 2
'200x150'
when 3
'250x200'
else
'250x250'
end
end
return @size || false
end
#modify this as you normally would, but without the :resize_to parameter
has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 500.kilobytes,
:path_prefix => "public/images/"
validates_as_attachment
{% endhighlight %}
2. Add this to attachment-fu.rb (after the definition for "has_attachment"
{% highlight ruby %}
# ...
# this allows for dynamic resizing written in the model itself - PS
options[:resize_to] = self.find_size if self.respond_to?(find_size) and find_size != false
# ...
{% endhighlight %}
mlaSeptember 22, 2007
This wasn't working for me. Calling public_filename(:thumb) was always returning the full version since full_filename() is no longer honoring the thumbnail parameter.
I think that customizing filenames should be in attacment_fu by default, but it seems that the people at TechnoWeenie no longer develop this plugin (at least for the seven past months). I had made my approach to this topic in a different way than you.
Basically, I hook the filename itself before the whole attachment being saved. This is made with a simple method and before callback. Works well when we are keepping the filename bound to its owner model's name or whatever.
For creations:
{% highlight ruby %}
def before_create
self.filename = "some-name-taken-out-from-othe-model-or-filename-normalization" # The file isn't yet save so we change the filename itself without touching any other thing. The thumbnails will be saved using this filename as well.
end
{% endhighlight %}
The example above could be a before_add statement in a model association, which is the case I are actually using in muy application. Remember that in Rails versions prior to 2.0 the before_add statement only works if you do model.association << object
For updates:
{% highlight ruby %}
def filename!(new_filename)
self.temp_path = full_filename # This tells attachment_fu where the old file is and that in the save method it has to rename it.
serf.filename = new_filename # This simply override the actual filename to, when attachment_fu saves the record, rewrite all files, including thumbnails, using this filename.
end
{% endhighlight %}
And that's all. Enjoy it!
Steven GNovember 29, 2007
Great article!
I am struggling a bit with custom directories though. My assets are called "mugshots" and they belong_to :artist. I am trying to hook this up so that the shots get saved in a folder with the artists name.
http://pastie.caboo.se/123257
I can save the main image successfully in the artist folder, but oddly enough the thumbnails are still defaulting to numbered folders.
Thanks for the post about the extra credit: like if your attachments belong to another model (like a user) and you need to store the photo_id but I had a question about this that I can not seem to find the answer to.
Basically I am building a social network where you have to upload images and want to store the photo_id of the image into different models. For example, a Users Profile would be one, a Group would be another, and possible even a blog could have its own image. I was looking for a DRY method that would allow me to easily store these photo_id once they upload the photo and update the model with these photo_id.
So you said you can use:
{% highlight ruby %}
before_thumbnail_saved do |record, thumbnail|
thumbnail.user_id = record.user_id
end
{% endhighlight %}
But how does this work if you have 3 models you need to store the id of the photo you just uploaded? Or if anyone else has any ideas, they would be greatly appreciated.
Polymorphic Associations is what you'd need. Look into that.
aFebruary 25, 2008
Hey, Thanks for the post about the extra credit: like if your attachments belong to another model (like a user) and you need to store the photo_id but I had a question about this that I can not seem to find the answer to. Basically I am building a social network where you have to upload images and want to store the photo_id of the image into different models. For example, a Users Profile would be one, a Group would be another, and possible even a blog could have its own image. I was looking for a DRY method that would allow me to easily store these photo_id once they upload the photo and update the model with these photo_id. So you said you can use before_thumbnail_saved do |record, thumbnail| thumbnail.user_id = record.user_id end But how does this work if you have 3 models you need to store the id of the photo you just uploaded? Or if anyone else has any ideas, they would be greatly appreciated.
I wanted to save all files in a single directory yet change the names.. but gave me so many errors since thumbnail does not exist on creation time.. and we have to use self.thumbnail. did some mods to the code and hope this would be the best place to post the changes (i also got the inspiration to modify the code from here).. thanks for great tips.
modified code will hopefully work for any thumbnail. doesnt have to specify "thumb1", "thumb2" trying to minimize the code size and keeping it generic as possible.
{% highlight ruby %}
class Photo < ActiveRecord::Base
has_attachment :content_type => :image,
:storage => :file_system,
:size => 1.kilobyte .. 10.megabytes,
:resize_to => '500>',
:thumbnails => { :thumb => '125' },
:path_prefix => 'public/images/product_pics',
:processor => 'MiniMagick'
validates_as_attachment
belongs_to :product
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix]
@thumb = self.thumbnail unless (@thumb = thumbnail) #checks if thumbnails info already exisit
File.join(RAILS_ROOT, file_system_path, self.product.code, thumbnail_name_view(@thumb, self.product.code))
end
def thumbnail_name_view(thumbnail = nil, asset = nil)
suffix = "_#{thumbnail}" unless thumbnail.blank? #sets suffix by the name of the thumbnail set in has_attachment options
extension = filename.scan(/\.\w+$/) # extracts extension
return "#{asset}#{suffix}#{extension}"
end
before_thumbnail_saved do |record, thumbnail|
thumbnail.product_id = record.product_id
end
end
{% endhighlight %}
I'd been playing with this for a while. I needed unique filenames so that i could use paranoid delete and based on your example i chose this naming scheme: public/images/photos/small/parent_id.extension. One of my goals was to make image tags easy within the app. I wanted to be able to write something like image_tag("photos/large#{photo.id}#{photo.extension}").This works for me:
{% highlight ruby %}
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix]
#stole this out of the attachment fu controller.. greek to me
ext = nil
basename = self.filename.gsub /\.\w+$/ do |s|
ext = s; ''
end
case self.thumbnail
when "medium"
File.join(RAILS_ROOT, file_system_path, 'medium', "#{self.parent_id.to_s}#{ext}")
when "small"
File.join(RAILS_ROOT, file_system_path, 'small', "#{self.parent_id.to_s}#{ext}")
else
File.join(RAILS_ROOT, file_system_path, 'large', "#{self.parent_id.to_s}#{ext}")
end
end
{% endhighlight %}
I hope this is helpful to anyone searching for a way to make unique filenames with attachment_fu
overwrite thumbnail_name_for(thumbnail = nil) in attachment_fu.rb to change the name stored in the database. plugins are loaded when the server starts, so don't make the mistake i did and spend an hour wondering why your changes aren't working: restart the server.
danAugust 12, 2008
nicolas b if this works for you it should work for me right I've got a few errors here m8!
Anyway if you could pasting the model file I would like so greatfull!
Despite correctly renaming the file itself and saving it in the correct location, the name of the file in the 'filename' field in the database doesn't get updated...it stays the same as the name of the file itself instead of getting renamed.
Not to my knowledge, Leonardo.
attachment_fu is a significant rewrite of acts_as_attachment, so a lot of the codebase has changed. You should consider upgrading your application, if possible.
If you would like to resize images based on whatever, (here the params[:adtype].keys of the AdsController having a certain value) you can do this:
1. Add a custom version of this to your model
{% highlight ruby %} #... include ObjectSpace # change :ad to whatever model your image belongs to. belongs_to :ad def self.find_size # Change AdsContoller to the controller of your model and params[adtype].keys to# whatever value you want to test for.
if adtype = ObjectSpace.each_object(AdsController) {|a| p a.params[:adtype].keys unless a.params[:adtype] == nil} @size = case adtype # customize your sizes and conditions when 1 '150x100' when 2 '200x150' when 3 '250x200' else '250x250' end end return @size || false end #modify this as you normally would, but without the :resize_to parameter has_attachment :content_type => :image, :storage => :file_system, :max_size => 500.kilobytes, :path_prefix => "public/images/" validates_as_attachment {% endhighlight %}
2. Add this to attachment-fu.rb (after the definition for "has_attachment"
{% highlight ruby %} # ... # this allows for dynamic resizing written in the model itself - PS options[:resize_to] = self.find_size if self.respond_to?(find_size) and find_size != false # ... {% endhighlight %}This wasn't working for me. Calling public_filename(:thumb) was always returning the full version since full_filename() is no longer honoring the thumbnail parameter.
This does seem to work for me, however.
{% highlight ruby %} def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix] thumbnail ||= self.thumbnail subdir = thumbnail ? thumbnail.to_s : 'full' File.join(RAILS_ROOT, file_system_path, subdir, thumbnail_name_for) end def thumbnail_name_for(thumbnail = nil) extension = filename.scan(/\.\w+$/) "#{attachment_path_id.to_s}#{extension}" end {% endhighlight %}Hi!
I think that customizing filenames should be in attacment_fu by default, but it seems that the people at TechnoWeenie no longer develop this plugin (at least for the seven past months). I had made my approach to this topic in a different way than you.
Basically, I hook the filename itself before the whole attachment being saved. This is made with a simple method and before callback. Works well when we are keepping the filename bound to its owner model's name or whatever.
For creations:
{% highlight ruby %} def before_create self.filename = "some-name-taken-out-from-othe-model-or-filename-normalization" # The file isn't yet save so we change the filename itself without touching any other thing. The thumbnails will be saved using this filename as well. end {% endhighlight %}The example above could be a before_add statement in a model association, which is the case I are actually using in muy application. Remember that in Rails versions prior to 2.0 the before_add statement only works if you do model.association << object
For updates:
{% highlight ruby %} def filename!(new_filename) self.temp_path = full_filename # This tells attachment_fu where the old file is and that in the save method it has to rename it. serf.filename = new_filename # This simply override the actual filename to, when attachment_fu saves the record, rewrite all files, including thumbnails, using this filename. end {% endhighlight %}And that's all. Enjoy it!
Great article!
I am struggling a bit with custom directories though. My assets are called "mugshots" and they belong_to :artist. I am trying to hook this up so that the shots get saved in a folder with the artists name.
http://pastie.caboo.se/123257
I can save the main image successfully in the artist folder, but oddly enough the thumbnails are still defaulting to numbered folders.
Any ideas?
Hey,
Thanks for the post about the extra credit: like if your attachments belong to another model (like a user) and you need to store the photo_id but I had a question about this that I can not seem to find the answer to.
Basically I am building a social network where you have to upload images and want to store the photo_id of the image into different models. For example, a Users Profile would be one, a Group would be another, and possible even a blog could have its own image. I was looking for a DRY method that would allow me to easily store these photo_id once they upload the photo and update the model with these photo_id.
So you said you can use:
{% highlight ruby %} before_thumbnail_saved do |record, thumbnail| thumbnail.user_id = record.user_id end {% endhighlight %}But how does this work if you have 3 models you need to store the id of the photo you just uploaded? Or if anyone else has any ideas, they would be greatly appreciated.
John,
Polymorphic Associations is what you'd need. Look into that.
Hey, Thanks for the post about the extra credit: like if your attachments belong to another model (like a user) and you need to store the photo_id but I had a question about this that I can not seem to find the answer to. Basically I am building a social network where you have to upload images and want to store the photo_id of the image into different models. For example, a Users Profile would be one, a Group would be another, and possible even a blog could have its own image. I was looking for a DRY method that would allow me to easily store these photo_id once they upload the photo and update the model with these photo_id. So you said you can use before_thumbnail_saved do |record, thumbnail| thumbnail.user_id = record.user_id end But how does this work if you have 3 models you need to store the id of the photo you just uploaded? Or if anyone else has any ideas, they would be greatly appreciated.
I wanted to save all files in a single directory yet change the names.. but gave me so many errors since thumbnail does not exist on creation time.. and we have to use self.thumbnail. did some mods to the code and hope this would be the best place to post the changes (i also got the inspiration to modify the code from here).. thanks for great tips.
modified code will hopefully work for any thumbnail. doesnt have to specify "thumb1", "thumb2" trying to minimize the code size and keeping it generic as possible.
{% highlight ruby %} class Photo < ActiveRecord::Base has_attachment :content_type => :image, :storage => :file_system, :size => 1.kilobyte .. 10.megabytes, :resize_to => '500>', :thumbnails => { :thumb => '125' }, :path_prefix => 'public/images/product_pics', :processor => 'MiniMagick' validates_as_attachment belongs_to :product def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix] @thumb = self.thumbnail unless (@thumb = thumbnail) #checks if thumbnails info already exisit File.join(RAILS_ROOT, file_system_path, self.product.code, thumbnail_name_view(@thumb, self.product.code)) end def thumbnail_name_view(thumbnail = nil, asset = nil) suffix = "_#{thumbnail}" unless thumbnail.blank? #sets suffix by the name of the thumbnail set in has_attachment options extension = filename.scan(/\.\w+$/) # extracts extension return "#{asset}#{suffix}#{extension}" end before_thumbnail_saved do |record, thumbnail| thumbnail.product_id = record.product_id end end {% endhighlight %}I'd been playing with this for a while. I needed unique filenames so that i could use paranoid delete and based on your example i chose this naming scheme: public/images/photos/small/parent_id.extension. One of my goals was to make image tags easy within the app. I wanted to be able to write something like image_tag("photos/large#{photo.id}#{photo.extension}").This works for me:
{% highlight ruby %} def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix] #stole this out of the attachment fu controller.. greek to me ext = nil basename = self.filename.gsub /\.\w+$/ do |s| ext = s; '' end case self.thumbnail when "medium" File.join(RAILS_ROOT, file_system_path, 'medium', "#{self.parent_id.to_s}#{ext}") when "small" File.join(RAILS_ROOT, file_system_path, 'small', "#{self.parent_id.to_s}#{ext}") else File.join(RAILS_ROOT, file_system_path, 'large', "#{self.parent_id.to_s}#{ext}") end end {% endhighlight %}I hope this is helpful to anyone searching for a way to make unique filenames with attachment_fu
overwrite thumbnail_name_for(thumbnail = nil) in attachment_fu.rb to change the name stored in the database. plugins are loaded when the server starts, so don't make the mistake i did and spend an hour wondering why your changes aren't working: restart the server.
nicolas b if this works for you it should work for me right I've got a few errors here m8!
Anyway if you could pasting the model file I would like so greatfull!
Despite correctly renaming the file itself and saving it in the correct location, the name of the file in the 'filename' field in the database doesn't get updated...it stays the same as the name of the file itself instead of getting renamed.
Any idea how to get the filename field to update?
For reference, here's my model:
{% highlight ruby %} class DevicePicture < ActiveRecord::Base belongs_to :manufacturer_model has_attachment :content_type => :image, :storage => :file_system, :max_size => 500.kilobytes, :resize_to => '384x256>', :thumbnails => { :thumb => '98>' }, :path_prefix => 'public/device_pictures/' validates_as_attachment def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix] case self.thumbnail when "thumb" File.join(RAILS_ROOT, file_system_path, 'thumb', thumbnail_name_for(thumbnail, self.parent_id)) else File.join(RAILS_ROOT, file_system_path, 'fullsize', thumbnail_name_for(thumbnail, self.id)) end end def thumbnail_name_for(thumbnail = nil, asset = nil) extension = filename.scan(/\.\w+$/) return "#{asset}#{extension}" end end {% endhighlight %}


Contents
2. The main loop
|
How to program a text adventure in C
by Ruud Helderman
<EMAIL_ADDRESS>Licensed under
MIT License
2. The main loop
Let’s bring some basic interaction into our program!
The basic principle of a text adventure is simple:
- The player enters a command.
- The program
parses
and executes the command.
- Repeat steps 1 and 2 until the player decides to quit.
The code sample below consists of three functions,
one for each step:
- Function getInput.
- Function parseAndExecute.
- Function main,
which takes care of calling the other two functions repeatedly.
| main.c |
- #include <stdbool.h>
- #include <stdio.h>
- #include "parsexec.h"
- static char input[100] = "look around";
- static bool getInput(void)
- {
- printf("\n--> ");
- return fgets(input, sizeof input, stdin) != NULL;
- }
- int main()
- {
- printf("Welcome to Little Cave Adventure.\n");
- while (parseAndExecute(input) && getInput());
- printf("\nBye!\n");
- return 0;
- }
|
Explanation:
- Line 1:
in case some legacy compiler does not support stdbool.h
(it was introduced with
C99),
please be informed that you can easily do without;
just replace all instances of bool, true and false
with int, 1 and 0, respectively.
- Line 5:
string buffer to collect keyboard input.
It is pre-filled with a command that will be executed at startup:
“look around.”
- Line 9:
the
prompt;
feel free to change it into anything you like,
for example a polite “Please enter command.”
- Line 10:
standard function
fgets
gets input from the keyboard.
If the player presses
end-of-file,
function getInput will return false
and the program will end (see line 16).
- Line 16:
this is the main loop;
it alternately calls getInput and parseAndExecute
until either one of them returns false.
I deliberately swapped the function calls
(calling parseAndExecute before getInput)
because I want to execute a pre-determined command first
(as specified on line 5).
I implemented function parseAndExecute in a separate module,
as it is the function that is subject to change in chapters to come.
| parsexec.h |
- extern bool parseAndExecute(char *input);
|
| parsexec.c |
- #include <stdbool.h>
- #include <stdio.h>
- #include <string.h>
- bool parseAndExecute(char *input)
- {
- char *verb = strtok(input, " \n");
- char *noun = strtok(NULL, " \n");
- if (verb != NULL)
- {
- if (strcmp(verb, "quit") == 0)
- {
- return false;
- }
- else if (strcmp(verb, "look") == 0)
- {
- printf("It is very dark in here.\n");
- }
- else if (strcmp(verb, "go") == 0)
- {
- printf("It's too dark to go anywhere.\n");
- }
- else
- {
- printf("I don't know how to '%s'.\n", verb);
- }
- }
- return true;
- }
|
Explanation:
- Line 3:
from string.h
we will be using some standard string manipulation functions
(strcmp, strtok).
- Line 8, 9:
standard function strtok is used to build a very basic verb-noun
parser
in just two lines of code.
- Line 9:
your C compiler may give a warning
regarding the unused variable ‘noun’ on this line.
Please ignore this message; the issue will be resolved in the next chapter.
- Line 10:
nothing to do if the player enters nothing (or just spaces).
- Line 12, 16, 20:
using standard function
strcmp
to match keyboard input with known verbs
(quit, look and go).
For now, this is a
case-sensitive match,
so the player should be careful not to hold
shift or press
caps lock.
- Line 14:
returning false will cause the main loop to end
(see main.c line 16).
- Line 26:
a typical
printf-style format string,
with %s as placeholder for the additional parameter verb.
| Sample output |
Welcome to Little Cave Adventure.
It is very dark in here.
--> go north
It's too dark to go anywhere.
--> look around
It is very dark in here.
--> eat sandwich
I don't know how to 'eat'.
--> quit
Bye!
|
This program, however small it may be, already responds
to commands like go north, look around and quit;
see the sample output on the right.
But of course, it is not much of a game.
It has no locations, no items; all there is, is darkness.
So in the next chapter, we will begin adding locations.
Quit
Notice our program stops at nothing but a command from the player
(either quit or
end-of-file).
So what about
‘game over’
situations, i.e. death and victory?
Well, those are conditions that might end the game,
but not necessarily end the program.
- Death:
the player gets killed by an
orc;
he falls from a cliff; his spaceship blows up.
A good adventure should encourage the player to try again;
not cast him off upon his first failure.
- Victory:
the player found each and every treasure,
he rescued the princess,
he escaped the maze.
It took the player days, maybe months to reach this ultimate goal.
Imagine his disappointment to get thrown out of the program
right after the obligatory ‘congratulations’ message,
just because there is nothing left to achieve.
Why not give the player the time to move around freely
and cherish his moment of victory?
There is also a practical problem
with terminating the program without the player’s consent: in a
windowing system,
it is not unlikely for a
text-based
program to be running in a terminal window
that immediately closes when the program ends,
even before the player has had a chance to read the final message.
We could delay this effect with “press
any key
to exit” or “wanna quit (y/n)”,
but why bother when there is already a perfectly sane alternative?
Just let the player type in ‘quit’
whenever he is ready to leave the imaginary world behind.
Naturally, all this is just an advice; you can take it or leave it.
That’s the great thing about writing a game in a
general-purpose language:
you can mould it any way you like.
Next chapter: 3. Locations


This appears to be a data issue. The harvester checks first for association with the Cicognara digital collection, then for the presence of a DCL identifier, and then checks for the presence of a IIIF manifest URL. Only if all three of these checks pass, is the record then ingested.
I determined the following by debugging the harvester, for items with the value "Getty Research Institute" in the Contributing Library field: * Passed Cicognara check: 1298 records * Passed DCL number check: 1272 records * Passed manifest URL check: 5
So it looks like there is a problem getting manifests into the correct place in the records. It looks like it's completely missing from the Getty records rather than just in the wrong place.
We need to get the missing manifest URL values into the records from the Getty, or adjust the check. The items without manifest URLs are using the Archive.org BookReader.
closed
by Kate Lynch at 2021-12-20 17:32:05.Actually any search fails DPUL has changed it's underly json structure.
labeled
by Kevin Reiss at 2021-12-20 19:00:21.renamed
by Kevin Reiss at 2021-12-20 20:21:01.review_requested
by Kevin Reiss at 2021-12-20 20:21:12.referenced
by None at 2021-12-20 20:23:17.merged
by None at 2021-12-20 20:23:17.closed
by None at 2021-12-20 20:23:17.head_ref_deleted
by None at 2021-12-20 20:23:18.referenced
by Kevin Reiss at 2021-12-20 14:50:11.merged
by Kevin Reiss at 2021-12-20 14:50:11.closed
by Kevin Reiss at 2021-12-20 14:50:11.head_ref_deleted
by Kevin Reiss at 2021-12-20 14:50:12.referenced
by None at 2021-12-20 13:28:40.merged
by None at 2021-12-20 13:28:40.closed
by None at 2021-12-20 13:28:40.head_ref_deleted
by None at 2021-12-20 13:28:41.Related to https://github.com/pulibrary/pul-the-hard-way/pull/92. Leaving this open on the assumption that we do want to automate Confluence installation at some point.
Looks good, thanks @kayiwa.
mentioned
by Francis Kayiwa at 2021-12-20 16:55:25.subscribed
by Francis Kayiwa at 2021-12-20 16:55:26.referenced
by Alicia Cozine at 2021-12-20 16:55:39.merged
by Alicia Cozine at 2021-12-20 16:55:39.closed
by Alicia Cozine at 2021-12-20 16:55:39.head_ref_deleted
by Alicia Cozine at 2021-12-20 16:55:40.referenced
by Alicia Cozine at 2021-12-20 16:56:27.merged
by Alicia Cozine at 2021-12-20 16:56:27.closed
by Alicia Cozine at 2021-12-20 16:56:27.head_ref_deleted
by Alicia Cozine at 2021-12-20 16:56:27.referenced
by Alicia Cozine at 2021-12-20 16:57:48.merged
by Alicia Cozine at 2021-12-20 16:57:48.closed
by Alicia Cozine at 2021-12-20 16:57:48.head_ref_deleted
by Alicia Cozine at 2021-12-20 16:57:49.referenced
by Alicia Cozine at 2021-12-20 23:02:46.merged
by Alicia Cozine at 2021-12-20 23:02:47.closed
by Alicia Cozine at 2021-12-20 23:02:47.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:02:47.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:02.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:11.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:13.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:14.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:15.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:17.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:18.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:19.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:20.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:28.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:29.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:30.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:31.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:32.head_ref_deleted
by Alicia Cozine at 2021-12-20 23:19:44.referenced
by None at 2021-12-20 13:12:58.merged
by None at 2021-12-20 13:12:58.closed
by None at 2021-12-20 13:12:58.head_ref_deleted
by None at 2021-12-20 13:12:59.closed
by None at 2021-12-20 15:32:46.closed
by None at 2021-12-20 15:33:26.assigned
by Kevin Reiss at 2021-12-20 15:33:49.labeled
by Christa Cleeton at 2021-12-20 13:45:37.


Pageviews Analysis fetches data via the
Wikimedia REST API.
This API is still listed to be in an
experimental phase.
This means functionality
could break at any time without warning. The
maintainer of Pageviews Anaylsis will work to stay
up-to-date with API changes, but no promises can be made that the tool will indefinitely be stable until the API
is released as stable.


Prince Charles flips Indian flatbread at community kitchen in New Delhi
by Nathalie TexierPrince Charles took a tour of the Sikh temple, interacting with the faithful as part of a three-day visit to India.
He also prepared Indian bread, roti, at a community kitchen..
The trip marks Charles' 10th official visit to India. His last trip was part of a joint tour with his wife Camilla, Duchess of Cornwall, to Brunei, India, Singapore, and Malaysia in November 2017.