I am creating a small program that will make an SVG file based on options I select. The problem I am having is that JSVGCanvas (from Batik) will not seem to render gradients that are made (using the DOM api). The weird part is, if I export the file and open it in Inkscape or with the test class here, it (the gradient) renders! Here is all the info I think may be important:
Batik Version: 1.7
Gradient Build Code:
public Element parse(Document doc, String id) { //This is inside a gradient object.
Element defs = doc.createElementNS(Main.svgNS, "defs"); //Since multiple defs work in an SVG (or at least Inkscape), I add a defs tag per set of gradients.
defs.setAttribute("id", id); //Sets the id of the defs tag.
for (JsonElement je : json.getAsJsonArray("Linears")) { //I use GSON to store data, I am iterating through all objects in the Linears array.
if (je.isJsonObject()) { //Just a type check.
JsonObject jo = je.getAsJsonObject();
Element lin = doc.createElementNS(Main.svgNS, "linearGradient"); //Creates the linearGradient element.
for (Entry<String, JsonElement> entry : jo.entrySet()) { //For all elements other than Stops (it is an array) we want to set the attribute.
if (!entry.getKey().equals("Stops")) {
lin.setAttribute(entry.getKey(), entry.getValue().getAsJsonPrimitive().getAsString());
}
}
for (JsonElement stop : jo.getAsJsonArray("Stops")) { //For all objects in stops we want to add a stop.
Element el = doc.createElementNS(Main.svgNS, "stop"); //Creates a stop element.
for (Entry<String, JsonElement> entry : stop.getAsJsonObject().entrySet()) {
if (entry.getValue().isJsonObject()) {
el.setAttribute(entry.getKey(), parseObject(entry.getValue().getAsJsonObject())); //The parseObject method makes a string based on the JsonObject (this is used to get "variables" such as colors).
} else {
el.setAttribute(entry.getKey(), entry.getValue().getAsString());
}
}
lin.appendChild(el); //Add stop to the linear gradient.
}
defs.appendChild(lin); //Add the linear gradient to defs.
}
}
for (JsonElement je : json.getAsJsonArray("Radials")) { //Loops through all the radial gradients.
if (je.isJsonObject()) { //Type check.
JsonObject jo = je.getAsJsonObject();
Element rad = doc.createElementNS(Main.svgNS, "radialGradient"); //Creates the radialGradient element.
for (Entry<String, JsonElement> entry : jo.entrySet()) {
if (entry.getValue().isJsonPrimitive()) {
rad.setAttribute(entry.getKey(), entry.getValue().getAsJsonPrimitive().getAsString());
} else {
rad.setAttribute(entry.getKey(), parseObject(entry.getValue().getAsJsonObject()));
}
}
defs.appendChild(rad); //Adds the radial gradient to defs.
}
}
cache = defs;
return cache; //We return the defs to be added in the main build code.
}
Document Creation:
Constructor:
impl = SVGDOMImplementation.getDOMImplementation();
Build Method:
doc = (SVGDocument) impl.createDocument(svgNS, "svg", null);
JSVGCanvas Creation:
preview = new JSVGCanvas();
preview.setEnableImageZoomInteractor(true);
preview.setEnablePanInteractor(true);
Here is what the gradients part of my template files looks like (JSON):
"Gradients": {
"Radials": [
{
"gradientTransform": "matrix(0.96774313,-1.5022501e-7,3.5488612e-7,1.483871,-201.69415,-217.84922)",
"inkscape:collect": "always",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
"id": "radialGradient5607",
"gradientUnits": "userSpaceOnUse",
"xlink:show": "other",
"xlink:type": "simple",
"r": "15.5",
"cx":{
pre:"",
var:"X",
add:327,
post:""
},
"fx":{
pre:"",
var:"Y",
add:327,
post:""
},
"cy": {
pre:"",
var:"X",
add:209,
post:""
},
"fy": {
pre:"",
var:"Y",
add:209,
post:""
},
"xlink:href": "#linearGradient4114",
"xlink:actuate": "onLoad"
}
],
"Linears": [
{
"xlink:actuate": "onLoad",
"xlink:type": "simple",
"id": "linearGradient4114",
"xlink:show": "other",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
"Stops": [
{
"offset": "0",
"style": {
"pre": "stop-color:",
"var": "Eye",
"post": ";stop-opacity:1;"
},
"id": "stop4116"
},
{
"style": {
"pre": "stop-color:",
"var": "Eye2",
"post": ";stop-opacity:1;"
},
"offset": "0.5",
"id": "stop4122"
},
{
"offset": "1",
"style": {
"pre": "stop-color:",
"var": "Eye3",
"post": ";stop-opacity:1;"
},
"id": "stop4118"
}
]
}
]
}
That part of the config file then gets turned into:
<defs id="defs0">
<linearGradient
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="simple"
xlink:actuate="onLoad"
id="linearGradient4114"
xlink:show="other">
<stop
style="stop-color:#d80a00;stop-opacity:1;"
offset="0"
id="stop4116"/>
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0.5"
id="stop4122"/>
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop4118"/>
</linearGradient>
<radialGradient
gradientTransform="matrix(0.96774313,-1.5022501e-7,3.5488612e-7,1.483871,-201.69415,-217.84922)"
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="simple"
xlink:href="#linearGradient4114"
id="radialGradient5607"
xlink:show="other"
gradientUnits="userSpaceOnUse"
r="15.5"
xlink:actuate="onLoad"
cx="392.0"
fx="427.0"
cy="274.0"
fy="309.0"
inkscape:collect="always"/>
I assume this is a namespace thing (I don't quite get XML/SVG). I assume this because I have had an error like this before, where it wouldn't work in the preview but it would work after export (and loading the svg file). Note that this is a quick home application, so the code isn't the finest (nor the design), but it works for what I am doing. Also note that I do not type these config files by hand (that would be insane), I have a converter that converts an svg file to these configs (yes it sounds like going back and forth but I am combining multiple of these configs to make an image).
I figured it out after messing around yesterday and today. The xlink:href attribute requires that you set its namespace to be xlink's. For my project that meant just doing this:
if (entry.getKey().equals("xlink:href")) {
rad.setAttributeNS(XMLConstants.XLINK_NAMESPACE_URI, "xlink:href", entry.getValue().getAsJsonPrimitive().getAsString());
}
After adding that in, my gradients worked successfully.
Related
This is a follow up to an earlier question. Thanks #harshank for the quick help. I am posting this question separately since there was an additional check required which I had not asked in the earlier question. So, the solution posted earlier works fine for the stated requirement.
I am adding the additional requirement here:
The decision if gp and parent match depends on an additional attribute eye_colour (please see sample payload below).
Also in the response additional fields need to be populated (which was not mentioned in the earlier question). Things like name and eye_colour.
So, as an example this is the payload:
[
{
"gp": "T1",
"gp_eye_colour": "blue",
"gp_name" : "John",
"parent": "T1",
"parent_eye_colour" : "black",
"parent_name" : "Sam",
"child": "C1",
"child_eye_colour" : "brown",
"child_name" : "C1"
},
{
"gp": "T1",
"gp_eye_colour": "blue",
"gp_name" : "John",
"parent": "T1",
"parent_eye_colour" : "black",
"parent_name" : "Sam",
"child": "C1",
"child_eye_colour" : "brown",
"child_name" : "C1"
}
]
And now using the earlier solution, I have tried to build on top of it as:
%dw 2.0
output application/xml
// hierarchy's structure. If, lets say you add a "grand child" it should work. (haven't tested this though)
var hierarchy = {
gp: {
parent: {
child: null
}
}
}
fun getDirectGeanologies(records, hierarchyStructure,isGp,isParent,isChild) = do {
var topLevel = keysOf(hierarchyStructure)[0] as String
var secondLevel = keysOf(hierarchyStructure[topLevel])[0]
---
(records groupBy $[topLevel])
mapObject ((level1Childs, code) ->
genealogy: {
code: code,
eye_colour: if(isGp) (level1Childs.gp_eye_colour) else if(isParent) (level1Childs.parent_eye_colour) else (level1Childs.child_eye_colour),
name: if(isGp) (level1Childs.gp_name) else if(isParent) (level1Childs.parent_name) else (level1Childs.child_name),
hierarchy:
if(secondLevel != null) // If there are no more childs, do nothing.
(level1Childs groupBy ((item) -> item[secondLevel] ~= code) // This is to handle when parent = gp
mapObject ((value, hasSameParent) ->
// If parent = gp, we want to skip parent and link directly to child
if(hasSameParent as Boolean) getDirectGeanologies(value, hierarchyStructure[topLevel][secondLevel],false,false,true)
// recurrsively call the function with child records and going down the hierarchyStructure by one
else getDirectGeanologies(value, hierarchyStructure[topLevel],false,true,false)
))
else {}
}
)
}
---
list: getDirectGeanologies(payload,hierarchy,true,false,false)
However, it is generating duplicate elements for eye_colour and name. Ideally, it should print the values only once with the value of either gp / parent / child.
This is the expected response:
<?xml version='1.0' encoding='UTF-8'?>
<list>
<genealogy>
<code>T1</code>
<eye_colour>blue</eye_colour>
<name>John</name>
<hierarchy>
<genealogy>
<code>C1</code>
<eye_colour>brown</eye_colour>
<name>C1</name>
<hierarchy/>
</genealogy>
</hierarchy>
</genealogy>
</list>
I was also not able to figure out how to check if gp and parent have same values based on two fields (gp & gp_eye_colour vs parent & parent_eye_colour)
Thank you #sudhish_s for the response, adding an example to elaborate regarding decision of when to skip parent (when gp & gp_eye_colour of gp are same as parent & parent_eye_colour of parent).
Sample input: (Here parent Sam should not be skipped since though gp and parent are same their eye colours are different. Similarly, in case of Don, this parent should be skipped since gp and parent and their eye colours are the same).
[
{
"gp": "T1",
"gp_eye_colour": "blue",
"gp_name" : "John",
"parent": "T1",
"parent_eye_colour" : "black",
"parent_name" : "Sam",
"child": "C1",
"child_eye_colour" : "brown",
"child_name" : "C1"
},
{
"gp": "T1",
"gp_eye_colour": "blue",
"gp_name" : "John",
"parent": "T1",
"parent_eye_colour" : "blue",
"parent_name" : "Don",
"child": "C1",
"child_eye_colour" : "brown",
"child_name" : "C1"
}
]
Here is the expected response:
<?xml version='1.0' encoding='UTF-8'?>
<list>
<genealogy>
<code>T1</code>
<eye_colour>blue</eye_colour>
<name>John</name>
<hierarchy>
<genealogy>
<code>T1</code>
<eye_colour>black</eye_colour>
<name>Sam</name>
<hierarchy>
<genealogy>
<code>C1</code>
<eye_colour>brown</eye_colour>
<name>C1</name>
<hierarchy/>
</genealogy>
</hierarchy>
</genealogy>
<genealogy>
<code>C1</code>
<eye_colour>brown</eye_colour>
<name>C1</name>
<hierarchy/>
</genealogy>
</hierarchy>
</genealogy>
</list>
Slightly different version. Used arrays for reference.
%dw 2.0
output application/xml
var hierarchy = ["gp", "parent", "child"]
fun getDirectGeanologies(records, level) = do {
var hLevel = hierarchy[level]
---
records groupBy $[hLevel] mapObject ((children, code) ->
genealogy: {
code: code,
eye_colour: children[0][hLevel ++ "_eye_colour"],
name: children[0][hLevel ++ "_name"],
hierarchy:
if (level == sizeOf(hierarchy) - 1) {}
else do {
var nextLevel = level + 1
var nextGen = children groupBy ($[hierarchy[nextLevel]])
---
nextGen mapObject ((nextGenChildren, nextGenCode) ->
if (nextGenCode == code)
getDirectGeanologies (nextGenChildren, nextLevel + 1 )
else
getDirectGeanologies (nextGenChildren, nextLevel)
)
}
}
)
}
---
list: getDirectGeanologies(payload,0)
The issue with elements appear multiple times is due to the level1Childs is Array. Change the output to json, it will be more clear. If you want to use your code, change the eye_colour and name mapping to below
eye_colour: if(isGp) (level1Childs[0].gp_eye_colour) else if(isParent) (level1Childs[0].parent_eye_colour) else (level1Childs[0].child_eye_colour),
name: if(isGp) (level1Childs[0].gp_name) else if(isParent) (level1Childs[0].parent_name) else (level1Childs[0].child_name),
I'm making a game and I want to change the texture on a model depending on associated data but no matter what I as soon as the texture should change the model becomes invisible the model is in g3dj format here is the materials portion
"materials":[
{
"id":"default",
"diffuse": [0.640000, 0.640000, 0.640000],
},
{
"id":"plant fibre",
"textures":[
{
"id": "Texture.001",
"filename": "graphics/plant fibre.png",
"type": "DIFFUSE"
}
]
},
{
"id":"logs",
"textures":[
{
"id": "Texture.001",
"filename": "graphics/logs.png",
"type": "DIFFUSE"
}
]
}
],
And here is my latest attempt
self.getMaterial("default").clear();
self.getMaterial("default").set(self.getMaterial(extra.getString("item")).get(TextureAttribute.Diffuse));
I have already tried everything I found by searching online and it all had the same problem
Turns out unused materials aren't passed to the modelInstance I just had to modify the model then create and render the modelInstance
Ps: this always happens I get so fed up with something that I post a question here and then solve the problem within 24 hours
// trees
val loader: ObjLoader = ObjLoader()
tree = loader.loadModel(Gdx.files.internal("data/tree2.obj"))
treeInstance = ModelInstance(tree)
for (m in treeInstance!!.materials) {
println("material id: ${m.id}")
m.remove(ColorAttribute.Emissive)
if (m.id == "Leafs" || m.id == "Leafs2") {
m.set(ColorAttribute.createDiffuse(Color.FOREST))
} else {
m.set(ColorAttribute.createDiffuse(Color.BROWN))
}
}
I have the following view
{
"views" : {
"categories" : {
"map" : "function (doc) { emit(doc._id,{"title":doc.title,"parentnode":doc.parentnode});}"
}
}
}
i.e., for each document, return a JSON object with two keys : title and parentnode with their respective values. The view runs just fine in the cloudant UI
{
"id": "3bacce314363eb954f1922ff3cd2240c",
"key": "3bacce314363eb954f1922ff3cd2240c",
"value": {
"title": "poi",
"parentnode": "asd"
},
"_id": "3bacce314363eb954f1922ff3cd2240c"
}
which is perfect. Now i try to read this in my Java program as
List<JSONObject> vals = cloudant.getViewRequestBuilder("categoryDesign", "categories")
.newRequest(com.cloudant.client.api.views.Key.Type.STRING, JSONObject.class)
.includeDocs(true)
.build()
.getResponse()
.getValues();
Note that JSONObject in this case is org.json.JSONObject;. But for this i get
[{}]
so then i changed the view a little bit
{
"views" : {
"categories" : {
"map" : "function (doc) { emit(doc._id,doc.title+":"+doc.parentnode);}"
}
}
}
and in the cloudant UI i see
{
"id": "9db1f03e8f4d239a6e18d4612b1a4275",
"key": "9db1f03e8f4d239a6e18d4612b1a4275",
"value": "poi:asd",
"_id": "9db1f03e8f4d239a6e18d4612b1a4275"
}
and now i do
List<String> vals = cloudant.getViewRequestBuilder("categoryDesign", "categories")
.newRequest(com.cloudant.client.api.views.Key.Type.STRING, String.class)
.includeDocs(true)
.build()
.getResponse()
.getValues();
Now, the output is
["poi:asd"]
What can i do to read the values as JSONObjects ?
Follow Up: How can i remove the duplicates from the output of the view?
Cloudant client does not seem to work with org.json.JSONObject. I got your first example to work with com.google.gson.JsonObject and org.apache.wink.json4j.JSONObject. Here are the maven dependencies:
com.google.gson.JsonObject:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.7</version>
</dependency>
org.apache.wink.json4j.JSONObject:
<dependency>
<groupId>org.apache.wink</groupId>
<artifactId>wink-json4j</artifactId>
<version>1.4</version>
</dependency>
Output from System.out.println(vals.toString()):
[{"parentnode":"asd","title":"poi"}]
To answer the followup question:
You can eliminate duplicates by using a reduce function (sum or count) in your view, but this also would require that you change your key to be the unique key for the data. So, if the combination of title and parentnode is what you want to be your unique key, your view would look like this:
"categoriesNoDups": {
"reduce": "_sum",
"map": "function (doc) { emit([doc.title, doc.parentnode], 1); } }"
}
Now, when you call your view (note the new view is named categoriesNoDups) you want to add ?group=true like so:
https://youraccount.cloudant.com/yourdb
/_design/categoryDesign/
_view/categoriesNoDups?group=true
The data will look similar to the following:
{
"rows": [
{
"key": [
"poi",
"asd"
],
"value": 2
}
]
}
Now, instead of getting the values you want to get the keys. To retrieve the keys in Java you would do something like this:
List<Key.ComplexKey> keys = cloudant.getViewRequestBuilder("categoryDesign", "categoriesNoDups")
.newRequest(Key.Type.COMPLEX, Number.class)
.group(true)
.build()
.getResponse()
.getKeys();
for (Key.ComplexKey key : keys) {
JSONArray keyValues = new JSONArray(key.toJson());
System.out.println("title = " + keyValues.getString(0));
System.out.println("parentnode = " + keyValues.getString(1));
}
Now you're back to dealing with arrays instead of JSON objects. Also note: I am using the Apache Wink JSON library to convert the keys to JSON objects (which are just arrays) and then accessing the values from those objects. The output looks similar to the following:
title = poi
parentnode = asd
I have this below json content in a file.
[
{
"prjId": 1,
"name" : "ABC",
"issueCounter": 7,
"issue": [
{
"id": 1,
"status" : "Closed",
"comment" : "blah blah blah",
"loggedBy": "shdkjdlsj"
},
{
"id": 2,
"status" : "Open",
"comment": "nothing",
"loggedBy": "xyz"
}
]
},
{
"prjId": 2,
"name" : "XYZ",
"issueCounter": 7,
"issue": [
{
"id": 3,
"status" : "Closed",
"comment": "jjjjjjjj",
"loggedBy": "klwm"
},
{
"id": 4,
"status" : "Closed",
"comment": "test test test",
"loggedBy": "NACK"
}
]
}]
I am able to parse this file and then traverse this json to get to the node where i wish to update. Upon reaching that point, i am confused on how to update it so that it would reflect on file on filesystem.
JSONParser parser = new JSONParser();
JSONArray rootArray = (JSONArray) parser.parse(new FileReader(pathToFile+fileName));
JSONObject project=null;
for (Object rootEntry : rootArray)
{
project = (JSONObject) rootEntry;
if( 1 == (Long) project.get("prjId"))
{
JSONArray issueArray = (JSONArray) project.get("issue");
for (Object issueEntry : issueArray)
{
JSONObject issue = (JSONObject) issueEntry;
System.out.println((Long) issue.get("id"));
if(2==(Long) issue.get("id"))
{
//now update the "comment" attribute here.
System.out.println("updated the comment: "+trckParam.getComment());
}
}
}
I have just the json reader till this point so do I construct a String as-and-when I traverse the nodes and when i reach the point I need to update, I update it (and construct the remainder of the nodes in this json object, (not to break from the loop)) and then write the entire reconstructed string to same file? Or is there a better approach where I can traverse the JSONObject and once i update it, push the entire object onto a file without constructing a String of entire file.
I still need to figure out a way to take either of the approach. So any pointers or direction will be greatly helpful.
I am trying to insert a value into a json object in the following format and save it to the database.
{
"mainObject": {
"childObject1": {
"validator": {
"dataType": "TEXT",
"regEx": "^[a-zA-Z0-9_]*{3,10}$"
},
"input": "Enter data value here"
},
"childObject2": {
"validator": {
"dataType": "TEXT",
"regEx": "^[a-zA-Z0-9_]*{3,10}$"
},
"input": "Enter data value here"
}
}
}
I need to enter values in the tag where it says input. Something like this:
{
"mainObject": {
"childObject1": {
"validator": {
"dataType": "TEXT",
"regEx": "^[a-zA-Z0-9_]*{3,10}$"
},
"input": "Input 1"
},
"childObject2": {
"validator": {
"dataType": "TEXT",
"regEx": "^[a-zA-Z0-9_]*{3,10}$"
},
"input": "Input 2"
}
}
}
I have tried the following code snippet in java. I am able to insert data into the right node.
String output = "{
\"mainObject\": {
\"childObject1\": {
\"validator\": {
\"dataType\": \"TEXT\",
\"regEx\": \"^[a-zA-Z0-9_]*{3,10}$\"
},
\"input\": \"Enter data value here\"
},
\"childObject2\": {
\"validator\": {
\"dataType\": \"TEXT\",
\"regEx\": \"^[a-zA-Z0-9_]*{3,10}$\"
},
\"input\": \"Enter data value here\"
}
}
}";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(output);
ObjectNode newMetadata = (ObjectNode) rootNode.path("mainObject").path("childObject1");
newMetadata.put("inputValue", "Input 1");
My problem is how to update and write this back to the mainObject node. At the moment the child nodes input tag is updated with the value, i want to update the mainobject with this and save to database.
I have already pasted the code that had the answer. I was looking at the wrong node. I mentioned in my previous comment, I didn't really see an option to close the question. To make it clear, the answer was that as soon as I write to the child nodes, the parent node will have the property as well. That is working code which does not really need a fix, as I said I was looking at the wrong node.